From fd49a9160c09b88338a005a39d8ec6aef9a73edc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 12:50:44 +0200 Subject: [PATCH 01/87] Release cloudnative-pg-v0.19.0 (#154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Niccolò Fei Co-authored-by: Niccolò Fei --- charts/cloudnative-pg/Chart.yaml | 4 +- charts/cloudnative-pg/README.md | 6 +- .../cloudnative-pg/templates/crds/crds.yaml | 337 +++++++++++++----- .../mutatingwebhookconfiguration.yaml | 6 +- charts/cloudnative-pg/templates/rbac.yaml | 74 ++-- .../validatingwebhookconfiguration.yaml | 8 +- charts/cloudnative-pg/values.schema.json | 12 +- charts/cloudnative-pg/values.yaml | 17 +- 8 files changed, 326 insertions(+), 138 deletions(-) diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index 52f6ba711..b4e3ae1ec 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,12 +18,12 @@ name: cloudnative-pg description: CloudNativePG Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.18.2" +version: "0.19.0" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.20.2" +appVersion: "1.21.0" sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index d47531715..0c1201330 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.18.2](https://img.shields.io/badge/Version-0.18.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.20.2](https://img.shields.io/badge/AppVersion-1.20.2-informational?style=flat-square) +![Version: 0.19.0](https://img.shields.io/badge/Version-0.19.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.21.0](https://img.shields.io/badge/AppVersion-1.21.0-informational?style=flat-square) CloudNativePG Helm Chart @@ -28,7 +28,7 @@ CloudNativePG Helm Chart | config.data | object | `{}` | The content of the configmap/secret, see https://cloudnative-pg.io/documentation/current/operator_conf/#available-options for all the available options. | | config.name | string | `"cnpg-controller-manager-config"` | The name of the configmap/secret to use. | | config.secret | bool | `false` | Specifies whether it should be stored in a secret, instead of a configmap. | -| containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsGroup":10001,"runAsUser":10001}` | Container Security Context. | +| containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | Container Security Context. | | crds.create | bool | `true` | Specifies whether the CRDs should be created when installing the chart. | | fullnameOverride | string | `""` | | | image.pullPolicy | string | `"IfNotPresent"` | | @@ -37,7 +37,7 @@ CloudNativePG Helm Chart | imagePullSecrets | list | `[]` | | | monitoring.podMonitorEnabled | bool | `false` | Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. | | monitoringQueriesConfigMap.name | string | `"cnpg-default-monitoring"` | The name of the default monitoring configmap. | -| monitoringQueriesConfigMap.queries | string | `"backends:\n query: |\n SELECT sa.datname\n , sa.usename\n , sa.application_name\n , states.state\n , COALESCE(sa.count, 0) AS total\n , COALESCE(sa.max_tx_secs, 0) AS max_tx_duration_seconds\n FROM ( VALUES ('active')\n , ('idle')\n , ('idle in transaction')\n , ('idle in transaction (aborted)')\n , ('fastpath function call')\n , ('disabled')\n ) AS states(state)\n LEFT JOIN (\n SELECT datname\n , state\n , usename\n , COALESCE(application_name, '') AS application_name\n , COUNT(*)\n , COALESCE(EXTRACT (EPOCH FROM (max(now() - xact_start))), 0) AS max_tx_secs\n FROM pg_catalog.pg_stat_activity\n GROUP BY datname, state, usename, application_name\n ) sa ON states.state = sa.state\n WHERE sa.usename IS NOT NULL\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - usename:\n usage: \"LABEL\"\n description: \"Name of the user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - state:\n usage: \"LABEL\"\n description: \"State of the backend\"\n - total:\n usage: \"GAUGE\"\n description: \"Number of backends\"\n - max_tx_duration_seconds:\n usage: \"GAUGE\"\n description: \"Maximum duration of a transaction in seconds\"\n\nbackends_waiting:\n query: |\n SELECT count(*) AS total\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks\n ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n WHERE NOT blocked_locks.granted\n metrics:\n - total:\n usage: \"GAUGE\"\n description: \"Total number of backends that are currently waiting on other queries\"\n\npg_database:\n query: |\n SELECT datname\n , pg_catalog.pg_database_size(datname) AS size_bytes\n , pg_catalog.age(datfrozenxid) AS xid_age\n , pg_catalog.mxid_age(datminmxid) AS mxid_age\n FROM pg_catalog.pg_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - size_bytes:\n usage: \"GAUGE\"\n description: \"Disk space used by the database\"\n - xid_age:\n usage: \"GAUGE\"\n description: \"Number of transactions from the frozen XID to the current one\"\n - mxid_age:\n usage: \"GAUGE\"\n description: \"Number of multiple transactions (Multixact) from the frozen XID to the current one\"\n\npg_postmaster:\n query: |\n SELECT EXTRACT(EPOCH FROM pg_postmaster_start_time) AS start_time\n FROM pg_catalog.pg_postmaster_start_time()\n metrics:\n - start_time:\n usage: \"GAUGE\"\n description: \"Time at which postgres started (based on epoch)\"\n\npg_replication:\n query: \"SELECT CASE WHEN NOT pg_catalog.pg_is_in_recovery()\n THEN 0\n ELSE GREATEST (0,\n EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp())))\n END AS lag,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up,\n (SELECT count(*) FROM pg_stat_replication) AS streaming_replicas\"\n metrics:\n - lag:\n usage: \"GAUGE\"\n description: \"Replication lag behind primary in seconds\"\n - in_recovery:\n usage: \"GAUGE\"\n description: \"Whether the instance is in recovery\"\n - is_wal_receiver_up:\n usage: \"GAUGE\"\n description: \"Whether the instance wal_receiver is up\"\n - streaming_replicas:\n usage: \"GAUGE\"\n description: \"Number of streaming replicas connected to the instance\"\n\npg_replication_slots:\n query: |\n SELECT slot_name,\n slot_type,\n database,\n active,\n pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn)\n FROM pg_catalog.pg_replication_slots\n WHERE NOT temporary\n metrics:\n - slot_name:\n usage: \"LABEL\"\n description: \"Name of the replication slot\"\n - slot_type:\n usage: \"LABEL\"\n description: \"Type of the replication slot\"\n - database:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - active:\n usage: \"GAUGE\"\n description: \"Flag indicating whether the slot is active\"\n - pg_wal_lsn_diff:\n usage: \"GAUGE\"\n description: \"Replication lag in bytes\"\n\npg_stat_archiver:\n query: |\n SELECT archived_count\n , failed_count\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_archived_time)), -1) AS seconds_since_last_archival\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_failed_time)), -1) AS seconds_since_last_failure\n , COALESCE(EXTRACT(EPOCH FROM last_archived_time), -1) AS last_archived_time\n , COALESCE(EXTRACT(EPOCH FROM last_failed_time), -1) AS last_failed_time\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_archived_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_archived_wal_start_lsn\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_failed_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_failed_wal_start_lsn\n , EXTRACT(EPOCH FROM stats_reset) AS stats_reset_time\n FROM pg_catalog.pg_stat_archiver\n metrics:\n - archived_count:\n usage: \"COUNTER\"\n description: \"Number of WAL files that have been successfully archived\"\n - failed_count:\n usage: \"COUNTER\"\n description: \"Number of failed attempts for archiving WAL files\"\n - seconds_since_last_archival:\n usage: \"GAUGE\"\n description: \"Seconds since the last successful archival operation\"\n - seconds_since_last_failure:\n usage: \"GAUGE\"\n description: \"Seconds since the last failed archival operation\"\n - last_archived_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving succeeded\"\n - last_failed_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving failed\"\n - last_archived_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Archived WAL start LSN\"\n - last_failed_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Last failed WAL LSN\"\n - stats_reset_time:\n usage: \"GAUGE\"\n description: \"Time at which these statistics were last reset\"\n\npg_stat_bgwriter:\n query: |\n SELECT checkpoints_timed\n , checkpoints_req\n , checkpoint_write_time\n , checkpoint_sync_time\n , buffers_checkpoint\n , buffers_clean\n , maxwritten_clean\n , buffers_backend\n , buffers_backend_fsync\n , buffers_alloc\n FROM pg_catalog.pg_stat_bgwriter\n metrics:\n - checkpoints_timed:\n usage: \"COUNTER\"\n description: \"Number of scheduled checkpoints that have been performed\"\n - checkpoints_req:\n usage: \"COUNTER\"\n description: \"Number of requested checkpoints that have been performed\"\n - checkpoint_write_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds\"\n - checkpoint_sync_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds\"\n - buffers_checkpoint:\n usage: \"COUNTER\"\n description: \"Number of buffers written during checkpoints\"\n - buffers_clean:\n usage: \"COUNTER\"\n description: \"Number of buffers written by the background writer\"\n - maxwritten_clean:\n usage: \"COUNTER\"\n description: \"Number of times the background writer stopped a cleaning scan because it had written too many buffers\"\n - buffers_backend:\n usage: \"COUNTER\"\n description: \"Number of buffers written directly by a backend\"\n - buffers_backend_fsync:\n usage: \"COUNTER\"\n description: \"Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)\"\n - buffers_alloc:\n usage: \"COUNTER\"\n description: \"Number of buffers allocated\"\n\npg_stat_database:\n query: |\n SELECT datname\n , xact_commit\n , xact_rollback\n , blks_read\n , blks_hit\n , tup_returned\n , tup_fetched\n , tup_inserted\n , tup_updated\n , tup_deleted\n , conflicts\n , temp_files\n , temp_bytes\n , deadlocks\n , blk_read_time\n , blk_write_time\n FROM pg_catalog.pg_stat_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of this database\"\n - xact_commit:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been committed\"\n - xact_rollback:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been rolled back\"\n - blks_read:\n usage: \"COUNTER\"\n description: \"Number of disk blocks read in this database\"\n - blks_hit:\n usage: \"COUNTER\"\n description: \"Number of times disk blocks were found already in the buffer cache, so that a read was not necessary (this only includes hits in the PostgreSQL buffer cache, not the operating system's file system cache)\"\n - tup_returned:\n usage: \"COUNTER\"\n description: \"Number of rows returned by queries in this database\"\n - tup_fetched:\n usage: \"COUNTER\"\n description: \"Number of rows fetched by queries in this database\"\n - tup_inserted:\n usage: \"COUNTER\"\n description: \"Number of rows inserted by queries in this database\"\n - tup_updated:\n usage: \"COUNTER\"\n description: \"Number of rows updated by queries in this database\"\n - tup_deleted:\n usage: \"COUNTER\"\n description: \"Number of rows deleted by queries in this database\"\n - conflicts:\n usage: \"COUNTER\"\n description: \"Number of queries canceled due to conflicts with recovery in this database\"\n - temp_files:\n usage: \"COUNTER\"\n description: \"Number of temporary files created by queries in this database\"\n - temp_bytes:\n usage: \"COUNTER\"\n description: \"Total amount of data written to temporary files by queries in this database\"\n - deadlocks:\n usage: \"COUNTER\"\n description: \"Number of deadlocks detected in this database\"\n - blk_read_time:\n usage: \"COUNTER\"\n description: \"Time spent reading data file blocks by backends in this database, in milliseconds\"\n - blk_write_time:\n usage: \"COUNTER\"\n description: \"Time spent writing data file blocks by backends in this database, in milliseconds\"\n\npg_stat_replication:\n primary: true\n query: |\n SELECT usename\n , COALESCE(application_name, '') AS application_name\n , COALESCE(client_addr::text, '') AS client_addr\n , EXTRACT(EPOCH FROM backend_start) AS backend_start\n , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), write_lsn) AS write_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), flush_lsn) AS flush_diff_bytes\n , COALESCE(pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), replay_lsn),0) AS replay_diff_bytes\n , COALESCE((EXTRACT(EPOCH FROM write_lag)),0)::float AS write_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM flush_lag)),0)::float AS flush_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM replay_lag)),0)::float AS replay_lag_seconds\n FROM pg_catalog.pg_stat_replication\n metrics:\n - usename:\n usage: \"LABEL\"\n description: \"Name of the replication user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - client_addr:\n usage: \"LABEL\"\n description: \"Client IP address\"\n - backend_start:\n usage: \"COUNTER\"\n description: \"Time when this process was started\"\n - backend_xmin_age:\n usage: \"COUNTER\"\n description: \"The age of this standby's xmin horizon\"\n - sent_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location sent on this connection\"\n - write_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location written to disk by this standby server\"\n - flush_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location flushed to disk by this standby server\"\n - replay_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location replayed into the database on this standby server\"\n - write_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written it\"\n - flush_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it\"\n - replay_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written, flushed and applied it\"\n\npg_settings:\n query: |\n SELECT name,\n CASE setting WHEN 'on' THEN '1' WHEN 'off' THEN '0' ELSE setting END AS setting\n FROM pg_catalog.pg_settings\n WHERE vartype IN ('integer', 'real', 'bool')\n ORDER BY 1\n metrics:\n - name:\n usage: \"LABEL\"\n description: \"Name of the setting\"\n - setting:\n usage: \"GAUGE\"\n description: \"Setting value\"\n"` | A string representation of a YAML defining monitoring queries. | +| monitoringQueriesConfigMap.queries | string | `"backends:\n query: |\n SELECT sa.datname\n , sa.usename\n , sa.application_name\n , states.state\n , COALESCE(sa.count, 0) AS total\n , COALESCE(sa.max_tx_secs, 0) AS max_tx_duration_seconds\n FROM ( VALUES ('active')\n , ('idle')\n , ('idle in transaction')\n , ('idle in transaction (aborted)')\n , ('fastpath function call')\n , ('disabled')\n ) AS states(state)\n LEFT JOIN (\n SELECT datname\n , state\n , usename\n , COALESCE(application_name, '') AS application_name\n , COUNT(*)\n , COALESCE(EXTRACT (EPOCH FROM (max(now() - xact_start))), 0) AS max_tx_secs\n FROM pg_catalog.pg_stat_activity\n GROUP BY datname, state, usename, application_name\n ) sa ON states.state = sa.state\n WHERE sa.usename IS NOT NULL\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - usename:\n usage: \"LABEL\"\n description: \"Name of the user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - state:\n usage: \"LABEL\"\n description: \"State of the backend\"\n - total:\n usage: \"GAUGE\"\n description: \"Number of backends\"\n - max_tx_duration_seconds:\n usage: \"GAUGE\"\n description: \"Maximum duration of a transaction in seconds\"\n\nbackends_waiting:\n query: |\n SELECT count(*) AS total\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks\n ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n WHERE NOT blocked_locks.granted\n metrics:\n - total:\n usage: \"GAUGE\"\n description: \"Total number of backends that are currently waiting on other queries\"\n\npg_database:\n query: |\n SELECT datname\n , pg_catalog.pg_database_size(datname) AS size_bytes\n , pg_catalog.age(datfrozenxid) AS xid_age\n , pg_catalog.mxid_age(datminmxid) AS mxid_age\n FROM pg_catalog.pg_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - size_bytes:\n usage: \"GAUGE\"\n description: \"Disk space used by the database\"\n - xid_age:\n usage: \"GAUGE\"\n description: \"Number of transactions from the frozen XID to the current one\"\n - mxid_age:\n usage: \"GAUGE\"\n description: \"Number of multiple transactions (Multixact) from the frozen XID to the current one\"\n\npg_postmaster:\n query: |\n SELECT EXTRACT(EPOCH FROM pg_postmaster_start_time) AS start_time\n FROM pg_catalog.pg_postmaster_start_time()\n metrics:\n - start_time:\n usage: \"GAUGE\"\n description: \"Time at which postgres started (based on epoch)\"\n\npg_replication:\n query: \"SELECT CASE WHEN (\n NOT pg_catalog.pg_is_in_recovery()\n OR pg_catalog.pg_last_wal_receive_lsn() = pg_catalog.pg_last_wal_replay_lsn())\n THEN 0\n ELSE GREATEST (0,\n EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp())))\n END AS lag,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up,\n (SELECT count(*) FROM pg_catalog.pg_stat_replication) AS streaming_replicas\"\n metrics:\n - lag:\n usage: \"GAUGE\"\n description: \"Replication lag behind primary in seconds\"\n - in_recovery:\n usage: \"GAUGE\"\n description: \"Whether the instance is in recovery\"\n - is_wal_receiver_up:\n usage: \"GAUGE\"\n description: \"Whether the instance wal_receiver is up\"\n - streaming_replicas:\n usage: \"GAUGE\"\n description: \"Number of streaming replicas connected to the instance\"\n\npg_replication_slots:\n query: |\n SELECT slot_name,\n slot_type,\n database,\n active,\n (CASE pg_catalog.pg_is_in_recovery()\n WHEN TRUE THEN pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_last_wal_receive_lsn(), restart_lsn)\n ELSE pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn)\n END) as pg_wal_lsn_diff\n FROM pg_catalog.pg_replication_slots\n WHERE NOT temporary\n metrics:\n - slot_name:\n usage: \"LABEL\"\n description: \"Name of the replication slot\"\n - slot_type:\n usage: \"LABEL\"\n description: \"Type of the replication slot\"\n - database:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - active:\n usage: \"GAUGE\"\n description: \"Flag indicating whether the slot is active\"\n - pg_wal_lsn_diff:\n usage: \"GAUGE\"\n description: \"Replication lag in bytes\"\n\npg_stat_archiver:\n query: |\n SELECT archived_count\n , failed_count\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_archived_time)), -1) AS seconds_since_last_archival\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_failed_time)), -1) AS seconds_since_last_failure\n , COALESCE(EXTRACT(EPOCH FROM last_archived_time), -1) AS last_archived_time\n , COALESCE(EXTRACT(EPOCH FROM last_failed_time), -1) AS last_failed_time\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_archived_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_archived_wal_start_lsn\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_failed_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_failed_wal_start_lsn\n , EXTRACT(EPOCH FROM stats_reset) AS stats_reset_time\n FROM pg_catalog.pg_stat_archiver\n metrics:\n - archived_count:\n usage: \"COUNTER\"\n description: \"Number of WAL files that have been successfully archived\"\n - failed_count:\n usage: \"COUNTER\"\n description: \"Number of failed attempts for archiving WAL files\"\n - seconds_since_last_archival:\n usage: \"GAUGE\"\n description: \"Seconds since the last successful archival operation\"\n - seconds_since_last_failure:\n usage: \"GAUGE\"\n description: \"Seconds since the last failed archival operation\"\n - last_archived_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving succeeded\"\n - last_failed_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving failed\"\n - last_archived_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Archived WAL start LSN\"\n - last_failed_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Last failed WAL LSN\"\n - stats_reset_time:\n usage: \"GAUGE\"\n description: \"Time at which these statistics were last reset\"\n\npg_stat_bgwriter:\n query: |\n SELECT checkpoints_timed\n , checkpoints_req\n , checkpoint_write_time\n , checkpoint_sync_time\n , buffers_checkpoint\n , buffers_clean\n , maxwritten_clean\n , buffers_backend\n , buffers_backend_fsync\n , buffers_alloc\n FROM pg_catalog.pg_stat_bgwriter\n metrics:\n - checkpoints_timed:\n usage: \"COUNTER\"\n description: \"Number of scheduled checkpoints that have been performed\"\n - checkpoints_req:\n usage: \"COUNTER\"\n description: \"Number of requested checkpoints that have been performed\"\n - checkpoint_write_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds\"\n - checkpoint_sync_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds\"\n - buffers_checkpoint:\n usage: \"COUNTER\"\n description: \"Number of buffers written during checkpoints\"\n - buffers_clean:\n usage: \"COUNTER\"\n description: \"Number of buffers written by the background writer\"\n - maxwritten_clean:\n usage: \"COUNTER\"\n description: \"Number of times the background writer stopped a cleaning scan because it had written too many buffers\"\n - buffers_backend:\n usage: \"COUNTER\"\n description: \"Number of buffers written directly by a backend\"\n - buffers_backend_fsync:\n usage: \"COUNTER\"\n description: \"Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)\"\n - buffers_alloc:\n usage: \"COUNTER\"\n description: \"Number of buffers allocated\"\n\npg_stat_database:\n query: |\n SELECT datname\n , xact_commit\n , xact_rollback\n , blks_read\n , blks_hit\n , tup_returned\n , tup_fetched\n , tup_inserted\n , tup_updated\n , tup_deleted\n , conflicts\n , temp_files\n , temp_bytes\n , deadlocks\n , blk_read_time\n , blk_write_time\n FROM pg_catalog.pg_stat_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of this database\"\n - xact_commit:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been committed\"\n - xact_rollback:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been rolled back\"\n - blks_read:\n usage: \"COUNTER\"\n description: \"Number of disk blocks read in this database\"\n - blks_hit:\n usage: \"COUNTER\"\n description: \"Number of times disk blocks were found already in the buffer cache, so that a read was not necessary (this only includes hits in the PostgreSQL buffer cache, not the operating system's file system cache)\"\n - tup_returned:\n usage: \"COUNTER\"\n description: \"Number of rows returned by queries in this database\"\n - tup_fetched:\n usage: \"COUNTER\"\n description: \"Number of rows fetched by queries in this database\"\n - tup_inserted:\n usage: \"COUNTER\"\n description: \"Number of rows inserted by queries in this database\"\n - tup_updated:\n usage: \"COUNTER\"\n description: \"Number of rows updated by queries in this database\"\n - tup_deleted:\n usage: \"COUNTER\"\n description: \"Number of rows deleted by queries in this database\"\n - conflicts:\n usage: \"COUNTER\"\n description: \"Number of queries canceled due to conflicts with recovery in this database\"\n - temp_files:\n usage: \"COUNTER\"\n description: \"Number of temporary files created by queries in this database\"\n - temp_bytes:\n usage: \"COUNTER\"\n description: \"Total amount of data written to temporary files by queries in this database\"\n - deadlocks:\n usage: \"COUNTER\"\n description: \"Number of deadlocks detected in this database\"\n - blk_read_time:\n usage: \"COUNTER\"\n description: \"Time spent reading data file blocks by backends in this database, in milliseconds\"\n - blk_write_time:\n usage: \"COUNTER\"\n description: \"Time spent writing data file blocks by backends in this database, in milliseconds\"\n\npg_stat_replication:\n primary: true\n query: |\n SELECT usename\n , COALESCE(application_name, '') AS application_name\n , COALESCE(client_addr::text, '') AS client_addr\n , COALESCE(client_port::text, '') AS client_port\n , EXTRACT(EPOCH FROM backend_start) AS backend_start\n , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), write_lsn) AS write_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), flush_lsn) AS flush_diff_bytes\n , COALESCE(pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), replay_lsn),0) AS replay_diff_bytes\n , COALESCE((EXTRACT(EPOCH FROM write_lag)),0)::float AS write_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM flush_lag)),0)::float AS flush_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM replay_lag)),0)::float AS replay_lag_seconds\n FROM pg_catalog.pg_stat_replication\n metrics:\n - usename:\n usage: \"LABEL\"\n description: \"Name of the replication user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - client_addr:\n usage: \"LABEL\"\n description: \"Client IP address\"\n - client_port:\n usage: \"LABEL\"\n description: \"Client TCP port\"\n - backend_start:\n usage: \"COUNTER\"\n description: \"Time when this process was started\"\n - backend_xmin_age:\n usage: \"COUNTER\"\n description: \"The age of this standby's xmin horizon\"\n - sent_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location sent on this connection\"\n - write_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location written to disk by this standby server\"\n - flush_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location flushed to disk by this standby server\"\n - replay_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location replayed into the database on this standby server\"\n - write_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written it\"\n - flush_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it\"\n - replay_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written, flushed and applied it\"\n\npg_settings:\n query: |\n SELECT name,\n CASE setting WHEN 'on' THEN '1' WHEN 'off' THEN '0' ELSE setting END AS setting\n FROM pg_catalog.pg_settings\n WHERE vartype IN ('integer', 'real', 'bool')\n ORDER BY 1\n metrics:\n - name:\n usage: \"LABEL\"\n description: \"Name of the setting\"\n - setting:\n usage: \"GAUGE\"\n description: \"Setting value\"\n"` | A string representation of a YAML defining monitoring queries. | | nameOverride | string | `""` | | | nodeSelector | object | `{}` | Nodeselector for the operator to be installed. | | podAnnotations | object | `{}` | Annotations to be added to the pod. | diff --git a/charts/cloudnative-pg/templates/crds/crds.yaml b/charts/cloudnative-pg/templates/crds/crds.yaml index 865600244..d15a9e50d 100644 --- a/charts/cloudnative-pg/templates/crds/crds.yaml +++ b/charts/cloudnative-pg/templates/crds/crds.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.1 + controller-gen.kubebuilder.io/version: v0.13.0 helm.sh/resource-policy: keep name: backups.postgresql.cnpg.io spec: @@ -22,6 +22,9 @@ spec: - jsonPath: .spec.cluster.name name: Cluster type: string + - jsonPath: .spec.method + name: Method + type: string - jsonPath: .status.phase name: Phase type: string @@ -58,6 +61,14 @@ spec: required: - name type: object + method: + default: barmanObjectStore + description: 'The backup method to be used, possible options are `barmanObjectStore` + and `volumeSnapshot`. Defaults to: `barmanObjectStore`.' + enum: + - barmanObjectStore + - volumeSnapshot + type: string target: description: The policy to decide which instance should perform this backup. If empty, it defaults to `cluster.spec.backup.target`. Available @@ -68,6 +79,8 @@ spec: - primary - prefer-standby type: string + required: + - cluster type: object status: description: 'Most recently observed status of the backup. This data may @@ -223,6 +236,9 @@ spec: description: The pod name type: string type: object + method: + description: The backup method being used + type: string phase: description: The last backup status type: string @@ -291,6 +307,29 @@ spec: description: The server name on S3, the cluster name is used if this parameter is omitted type: string + snapshotBackupStatus: + description: Status of the volumeSnapshot backup + properties: + elements: + description: The elements list, populated with the gathered volume + snapshots + items: + description: BackupSnapshotElementStatus is a volume snapshot + that is part of a volume snapshot method backup + properties: + name: + description: Name is the snapshot resource name + type: string + type: + description: Type is tho role of the snapshot in the cluster, + such as PG_DATA and PG_WAL + type: string + required: + - name + - type + type: object + type: array + type: object startedAt: description: When the backup was started format: date-time @@ -300,6 +339,9 @@ spec: format: date-time type: string type: object + required: + - metadata + - spec type: object served: true storage: true @@ -310,7 +352,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.1 + controller-gen.kubebuilder.io/version: v0.13.0 helm.sh/resource-policy: keep name: clusters.postgresql.cnpg.io spec: @@ -1535,7 +1577,8 @@ spec: description: RetentionPolicy is the retention policy to be used for backups and WALs (i.e. '60d'). The retention policy is expressed in the form of `XXu` where `XX` is a positive integer and `u` - is in `[dwm]` - days, weeks, months. + is in `[dwm]` - days, weeks, months. It's currently only applicable + when using the BarmanObjectStore method. pattern: ^[1-9][0-9]*[dwm]$ type: string target: @@ -1549,6 +1592,41 @@ spec: - primary - prefer-standby type: string + volumeSnapshot: + description: VolumeSnapshot provides the configuration for the + execution of volume snapshot backups. + properties: + annotations: + additionalProperties: + type: string + description: Annotations key-value pairs that will be added + to .metadata.annotations snapshot resources. + type: object + className: + description: ClassName specifies the Snapshot Class to be + used for PG_DATA PersistentVolumeClaim. It is the default + class for the other types if no specific class is present + type: string + labels: + additionalProperties: + type: string + description: Labels are key-value pairs that will be added + to .metadata.labels snapshot resources. + type: object + snapshotOwnerReference: + default: none + description: SnapshotOwnerReference indicates the type of + owner reference the snapshot should have + enum: + - none + - cluster + - backup + type: string + walClassName: + description: WalClassName specifies the Snapshot Class to + be used for the PG_WAL PersistentVolumeClaim. + type: string + type: object type: object bootstrap: description: Instructions to bootstrap this cluster @@ -1855,7 +1933,7 @@ spec: description: The external cluster whose backup we will restore. This is also used as the name of the folder under which the backup is stored, so it must be set to the name of the - source cluster Mutually exclusive with `backup` and `volumeSnapshots`. + source cluster Mutually exclusive with `backup`. type: string volumeSnapshots: description: The static PVC data source(s) from which to initiate @@ -1864,7 +1942,7 @@ spec: PVC group, compatible with CloudNativePG, and taken with a cold backup copy on a fenced Postgres instance (limitation which will be removed in the future when online backup will - be implemented). Mutually exclusive with `backup` and `source`. + be implemented). Mutually exclusive with `backup`. properties: storage: description: Configuration of the storage of the instances @@ -1959,13 +2037,13 @@ spec: description: Description of this PostgreSQL cluster type: string enableSuperuserAccess: - default: true + default: false description: When this option is enabled, the operator will use the `SuperuserSecret` to update the `postgres` user password (if the secret is not present, the operator will automatically create one). When this option is disabled, the operator will ignore the `SuperuserSecret` content, delete it when automatically created, and then blank the - password of the `postgres` user by setting it to `NULL`. Enabled + password of the `postgres` user by setting it to `NULL`. Disabled by default. type: boolean env: @@ -2114,6 +2192,26 @@ spec: x-kubernetes-map-type: atomic type: object type: array + ephemeralVolumesSizeLimit: + description: EphemeralVolumesSizeLimit allows the user to set the + limits for the ephemeral volumes + properties: + shm: + anyOf: + - type: integer + - type: string + description: Shm is the size limit of the shared memory volume + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + temporaryData: + anyOf: + - type: integer + - type: string + description: TemporaryData is the size limit of the temporary + data volume + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object externalClusters: description: The list of external clusters which are used in the configuration items: @@ -2729,8 +2827,6 @@ spec: description: Reuse the existing PVC (wait for the node to come up again) or not (recreate it elsewhere - when `instances` >1) type: boolean - required: - - inProgress type: object postgresGID: default: 26 @@ -3133,8 +3229,8 @@ spec: description: If replica mode is enabled, this cluster will be a replica of an existing cluster. Replica cluster can be created from a recovery object store or via streaming through pg_basebackup. - Refer to the Replication page of the documentation for more - information. + Refer to the Replica clusters page of the documentation for + more information. type: boolean source: description: The name of the external cluster which is the replication @@ -3142,25 +3238,30 @@ spec: minLength: 1 type: string required: + - enabled - source type: object replicationSlots: + default: + highAvailability: + enabled: true description: Replication slots management configuration properties: highAvailability: + default: + enabled: true description: Replication slots for high availability configuration properties: enabled: - default: false - description: If enabled, the operator will automatically manage - replication slots on the primary instance and use them in - streaming replication connections with all the standby instances - that are part of the HA cluster. If disabled (default), + default: true + description: If enabled (default), the operator will automatically + manage replication slots on the primary instance and use + them in streaming replication connections with all the standby + instances that are part of the HA cluster. If disabled, the operator will not take advantage of replication slots in streaming connections with the replicas. This feature also controls replication slots in replica cluster, from - the designated primary to its cascading replicas. This can - only be set at creation time. + the designated primary to its cascading replicas. type: boolean slotPrefix: default: _cnpg_ @@ -3241,8 +3342,8 @@ spec: description: localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to - the kubelet's configured seccomp profile location. Must only - be set if type is "Localhost". + the kubelet's configured seccomp profile location. Must be set + if type is "Localhost". Must NOT be set for any other type. type: string type: description: "type indicates which kind of seccomp profile will @@ -3282,16 +3383,26 @@ spec: required: - metadata type: object + smartShutdownTimeout: + default: 180 + description: 'The time in seconds that controls the window of time + reserved for the smart shutdown of Postgres to complete. Make sure + you reserve enough time for the operator to request a fast shutdown + of Postgres (that is: `stopDelay` - `smartShutdownTimeout`).' + format: int32 + type: integer startDelay: - default: 30 - description: The time in seconds that is allowed for a PostgreSQL - instance to successfully start up (default 30) + default: 3600 + description: 'The time in seconds that is allowed for a PostgreSQL + instance to successfully start up (default 3600). The startup probe + failure threshold is derived from this value using the formula: + ceiling(startDelay / 10).' format: int32 type: integer stopDelay: - default: 30 + default: 1800 description: The time in seconds that is allowed for a PostgreSQL - instance to gracefully shutdown (default 30) + instance to gracefully shutdown (default 1800) format: int32 type: integer storage: @@ -3533,11 +3644,10 @@ spec: - name type: object switchoverDelay: - default: 40000000 + default: 3600 description: The time in seconds that is allowed for a primary PostgreSQL instance to gracefully shutdown during a switchover. Default value - is 40000000, greater than one year in seconds, big enough to simulate - an infinite delay + is 3600 seconds (1 hour). format: int32 type: integer topologySpreadConstraints: @@ -4343,6 +4453,9 @@ spec: description: Current write pod type: string type: object + required: + - metadata + - spec type: object served: true storage: true @@ -4356,7 +4469,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.1 + controller-gen.kubebuilder.io/version: v0.13.0 helm.sh/resource-policy: keep name: poolers.postgresql.cnpg.io spec: @@ -4396,7 +4509,8 @@ spec: metadata: type: object spec: - description: PoolerSpec defines the desired state of Pooler + description: 'Specification of the desired behavior of the Pooler. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' properties: cluster: description: This is the cluster reference on which the Pooler will @@ -4459,7 +4573,7 @@ spec: type: object instances: default: 1 - description: The number of replicas we want + description: 'The number of replicas we want. Default: 1.' format: int32 type: integer monitoring: @@ -4517,13 +4631,11 @@ spec: type: array poolMode: default: session - description: The pool mode + description: 'The pool mode. Default: `session`.' enum: - session - transaction type: string - required: - - poolMode type: object template: description: The template of the Pod to be created @@ -6349,6 +6461,28 @@ spec: exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + restartPolicy: + description: 'RestartPolicy defines the restart behavior + of individual containers in a pod. This field may + only be set for init containers, and the only allowed + value is "Always". For non-init containers or when + this field is not specified, the restart behavior + is defined by the Pod''s restart policy and the container + type. Setting the RestartPolicy as "Always" for the + init container will have the following effect: this + init container will be continually restarted on exit + until all regular containers have terminated. Once + all regular containers have completed, all init containers + with restartPolicy "Always" will be shut down. This + lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although + this init container still starts in the init container + sequence, it does not wait for the container to complete + before proceeding to the next init container. Instead, + the next init container starts immediately after this + init container is started, or after any startupProbe + has successfully completed.' + type: string securityContext: description: 'SecurityContext defines the security options the container should be run with. If set, the fields @@ -6478,7 +6612,8 @@ spec: The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". + location. Must be set if type is "Localhost". + Must NOT be set for any other type. type: string type: description: "type indicates which kind of seccomp @@ -6514,14 +6649,10 @@ spec: hostProcess: description: HostProcess determines if a container should be run as a 'Host Process' container. - This field is alpha-level and will only be - honored by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the - feature flag will result in errors when validating - the Pod. All of a Pod's containers must have - the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, + All of a Pod's containers must have the same + effective HostProcess value (it is not allowed + to have a mix of HostProcess containers and + non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean @@ -7755,6 +7886,12 @@ spec: exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + restartPolicy: + description: Restart policy for the container to manage + the restart behavior of each container within a pod. + This may only be set for init containers. You cannot + set this field on ephemeral containers. + type: string securityContext: description: 'Optional: SecurityContext defines the security options the ephemeral container should be @@ -7884,7 +8021,8 @@ spec: The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". + location. Must be set if type is "Localhost". + Must NOT be set for any other type. type: string type: description: "type indicates which kind of seccomp @@ -7920,14 +8058,10 @@ spec: hostProcess: description: HostProcess determines if a container should be run as a 'Host Process' container. - This field is alpha-level and will only be - honored by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the - feature flag will result in errors when validating - the Pod. All of a Pod's containers must have - the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, + All of a Pod's containers must have the same + effective HostProcess value (it is not allowed + to have a mix of HostProcess containers and + non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean @@ -9194,6 +9328,28 @@ spec: exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + restartPolicy: + description: 'RestartPolicy defines the restart behavior + of individual containers in a pod. This field may + only be set for init containers, and the only allowed + value is "Always". For non-init containers or when + this field is not specified, the restart behavior + is defined by the Pod''s restart policy and the container + type. Setting the RestartPolicy as "Always" for the + init container will have the following effect: this + init container will be continually restarted on exit + until all regular containers have terminated. Once + all regular containers have completed, all init containers + with restartPolicy "Always" will be shut down. This + lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although + this init container still starts in the init container + sequence, it does not wait for the container to complete + before proceeding to the next init container. Instead, + the next init container starts immediately after this + init container is started, or after any startupProbe + has successfully completed.' + type: string securityContext: description: 'SecurityContext defines the security options the container should be run with. If set, the fields @@ -9323,7 +9479,8 @@ spec: The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". + location. Must be set if type is "Localhost". + Must NOT be set for any other type. type: string type: description: "type indicates which kind of seccomp @@ -9359,14 +9516,10 @@ spec: hostProcess: description: HostProcess determines if a container should be run as a 'Host Process' container. - This field is alpha-level and will only be - honored by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the - feature flag will result in errors when validating - the Pod. All of a Pod's containers must have - the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, + All of a Pod's containers must have the same + effective HostProcess value (it is not allowed + to have a mix of HostProcess containers and + non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean @@ -9802,19 +9955,14 @@ spec: namespace as this pod. \n The template will be used to create a new ResourceClaim, which will be bound to this pod. When this pod is deleted, - the ResourceClaim will also be deleted. The name - of the ResourceClaim will be -, where is the PodResourceClaim.Name. - Pod validation will reject the pod if the concatenated - name is not valid for a ResourceClaim (e.g. too - long). \n An existing ResourceClaim with that - name that is not owned by the pod will not be - used for the pod to avoid using an unrelated resource - by mistake. Scheduling and pod startup are then - blocked until the unrelated ResourceClaim is removed. - \n This field is immutable and no changes will - be made to the corresponding ResourceClaim by - the control plane after creating the ResourceClaim." + the ResourceClaim will also be deleted. The pod + name and resource name, along with a generated + component, will be used to form a unique name + for the ResourceClaim, which will be recorded + in pod.status.resourceClaimStatuses. \n This field + is immutable and no changes will be made to the + corresponding ResourceClaim by the control plane + after creating the ResourceClaim." type: string type: object required: @@ -9961,8 +10109,9 @@ spec: defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's - configured seccomp profile location. Must only be - set if type is "Localhost". + configured seccomp profile location. Must be set + if type is "Localhost". Must NOT be set for any + other type. type: string type: description: "type indicates which kind of seccomp @@ -10030,15 +10179,11 @@ spec: type: string hostProcess: description: HostProcess determines if a container - should be run as a 'Host Process' container. This - field is alpha-level and will only be honored by - components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature - flag will result in errors when validating the Pod. - All of a Pod's containers must have the same effective + should be run as a 'Host Process' container. All + of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix - of HostProcess containers and non-HostProcess containers). In - addition, if HostProcess is true then HostNetwork + of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean runAsUserName: @@ -12038,19 +12183,18 @@ spec: type: object type: default: rw - description: Which instances we must forward traffic to? + description: 'Type of service to forward traffic to. Default: `rw`.' enum: - rw - ro type: string required: - cluster - - instances - pgbouncer - - type type: object status: - description: PoolerStatus defines the observed state of Pooler + description: 'Most recently observed status of the Pooler. This data may + not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' properties: instances: description: The number of pods trying to be scheduled @@ -12105,6 +12249,9 @@ spec: type: object type: object type: object + required: + - metadata + - spec type: object served: true storage: true @@ -12118,7 +12265,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.12.1 + controller-gen.kubebuilder.io/version: v0.13.0 helm.sh/resource-policy: keep name: scheduledbackups.postgresql.cnpg.io spec: @@ -12186,6 +12333,14 @@ spec: description: If the first backup has to be immediately start after creation or not type: boolean + method: + default: barmanObjectStore + description: 'The backup method to be used, possible options are `barmanObjectStore` + and `volumeSnapshot`. Defaults to: `barmanObjectStore`.' + enum: + - barmanObjectStore + - volumeSnapshot + type: string schedule: description: The schedule does not follow the same format used in Kubernetes CronJobs as it includes an additional seconds specifier, @@ -12205,6 +12360,7 @@ spec: - prefer-standby type: string required: + - cluster - schedule type: object status: @@ -12226,6 +12382,9 @@ spec: format: date-time type: string type: object + required: + - metadata + - spec type: object served: true storage: true diff --git a/charts/cloudnative-pg/templates/mutatingwebhookconfiguration.yaml b/charts/cloudnative-pg/templates/mutatingwebhookconfiguration.yaml index 3407a9f29..200695b14 100644 --- a/charts/cloudnative-pg/templates/mutatingwebhookconfiguration.yaml +++ b/charts/cloudnative-pg/templates/mutatingwebhookconfiguration.yaml @@ -35,7 +35,7 @@ webhooks: path: /mutate-postgresql-cnpg-io-v1-backup port: {{ .Values.service.port }} failurePolicy: {{ .Values.webhook.mutating.failurePolicy }} - name: mbackup.kb.io + name: mbackup.cnpg.io rules: - apiGroups: - postgresql.cnpg.io @@ -56,7 +56,7 @@ webhooks: path: /mutate-postgresql-cnpg-io-v1-cluster port: {{ .Values.service.port }} failurePolicy: {{ .Values.webhook.mutating.failurePolicy }} - name: mcluster.kb.io + name: mcluster.cnpg.io rules: - apiGroups: - postgresql.cnpg.io @@ -77,7 +77,7 @@ webhooks: path: /mutate-postgresql-cnpg-io-v1-scheduledbackup port: {{ .Values.service.port }} failurePolicy: {{ .Values.webhook.mutating.failurePolicy }} - name: mscheduledbackup.kb.io + name: mscheduledbackup.cnpg.io rules: - apiGroups: - postgresql.cnpg.io diff --git a/charts/cloudnative-pg/templates/rbac.yaml b/charts/cloudnative-pg/templates/rbac.yaml index e5126e54b..cf7982939 100644 --- a/charts/cloudnative-pg/templates/rbac.yaml +++ b/charts/cloudnative-pg/templates/rbac.yaml @@ -135,13 +135,13 @@ rules: - update - watch - apiGroups: - - "" + - "" resources: - - secrets/status + - secrets/status verbs: - - get - - patch - - update + - get + - patch + - update - apiGroups: - "" resources: @@ -192,17 +192,17 @@ rules: - list - update - apiGroups: - - apps + - apps resources: - - deployments + - deployments verbs: - - create - - delete - - get - - list - - patch - - update - - watch + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - batch resources: @@ -288,32 +288,32 @@ rules: resources: - clusters/status verbs: - - get - - patch - - update - - watch + - get + - patch + - update + - watch - apiGroups: - - postgresql.cnpg.io + - postgresql.cnpg.io resources: - - poolers + - poolers verbs: - - create - - delete - - get - - list - - patch - - update - - watch + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - - postgresql.cnpg.io + - postgresql.cnpg.io resources: - - poolers/finalizers + - poolers/finalizers verbs: - - update + - update - apiGroups: - - postgresql.cnpg.io + - postgresql.cnpg.io resources: - - poolers/status + - poolers/status verbs: - get - patch @@ -361,6 +361,16 @@ rules: - patch - update - watch +- apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshots + verbs: + - create + - get + - list + - patch + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/charts/cloudnative-pg/templates/validatingwebhookconfiguration.yaml b/charts/cloudnative-pg/templates/validatingwebhookconfiguration.yaml index 4640b06c5..be9fff18e 100644 --- a/charts/cloudnative-pg/templates/validatingwebhookconfiguration.yaml +++ b/charts/cloudnative-pg/templates/validatingwebhookconfiguration.yaml @@ -35,7 +35,7 @@ webhooks: path: /validate-postgresql-cnpg-io-v1-backup port: {{ .Values.service.port }} failurePolicy: {{ .Values.webhook.validating.failurePolicy }} - name: vbackup.kb.io + name: vbackup.cnpg.io rules: - apiGroups: - postgresql.cnpg.io @@ -56,7 +56,7 @@ webhooks: path: /validate-postgresql-cnpg-io-v1-cluster port: {{ .Values.service.port }} failurePolicy: {{ .Values.webhook.validating.failurePolicy }} - name: vcluster.kb.io + name: vcluster.cnpg.io rules: - apiGroups: - postgresql.cnpg.io @@ -77,7 +77,7 @@ webhooks: path: /validate-postgresql-cnpg-io-v1-scheduledbackup port: {{ .Values.service.port }} failurePolicy: {{ .Values.webhook.validating.failurePolicy }} - name: vscheduledbackup.kb.io + name: vscheduledbackup.cnpg.io rules: - apiGroups: - postgresql.cnpg.io @@ -98,7 +98,7 @@ webhooks: path: /validate-postgresql-cnpg-io-v1-pooler port: {{ .Values.service.port }} failurePolicy: {{ .Values.webhook.validating.failurePolicy }} - name: vpooler.kb.io + name: vpooler.cnpg.io rules: - apiGroups: - postgresql.cnpg.io diff --git a/charts/cloudnative-pg/values.schema.json b/charts/cloudnative-pg/values.schema.json index 3bdf8322f..b5a5b4622 100644 --- a/charts/cloudnative-pg/values.schema.json +++ b/charts/cloudnative-pg/values.schema.json @@ -53,6 +53,14 @@ }, "runAsUser": { "type": "integer" + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + } } } }, @@ -137,10 +145,10 @@ "rbac": { "type": "object", "properties": { - "create": { + "aggregateClusterRoles": { "type": "boolean" }, - "aggregateClusterRoles": { + "create": { "type": "boolean" } } diff --git a/charts/cloudnative-pg/values.yaml b/charts/cloudnative-pg/values.yaml index 9faf8ec0c..8849fad60 100644 --- a/charts/cloudnative-pg/values.yaml +++ b/charts/cloudnative-pg/values.yaml @@ -93,6 +93,8 @@ containerSecurityContext: readOnlyRootFilesystem: true runAsUser: 10001 runAsGroup: 10001 + seccompProfile: + type: RuntimeDefault capabilities: drop: - "ALL" @@ -244,14 +246,16 @@ monitoringQueriesConfigMap: description: "Time at which postgres started (based on epoch)" pg_replication: - query: "SELECT CASE WHEN NOT pg_catalog.pg_is_in_recovery() + query: "SELECT CASE WHEN ( + NOT pg_catalog.pg_is_in_recovery() + OR pg_catalog.pg_last_wal_receive_lsn() = pg_catalog.pg_last_wal_replay_lsn()) THEN 0 ELSE GREATEST (0, EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp()))) END AS lag, pg_catalog.pg_is_in_recovery() AS in_recovery, EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up, - (SELECT count(*) FROM pg_stat_replication) AS streaming_replicas" + (SELECT count(*) FROM pg_catalog.pg_stat_replication) AS streaming_replicas" metrics: - lag: usage: "GAUGE" @@ -272,7 +276,10 @@ monitoringQueriesConfigMap: slot_type, database, active, - pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn) + (CASE pg_catalog.pg_is_in_recovery() + WHEN TRUE THEN pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_last_wal_receive_lsn(), restart_lsn) + ELSE pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn) + END) as pg_wal_lsn_diff FROM pg_catalog.pg_replication_slots WHERE NOT temporary metrics: @@ -453,6 +460,7 @@ monitoringQueriesConfigMap: SELECT usename , COALESCE(application_name, '') AS application_name , COALESCE(client_addr::text, '') AS client_addr + , COALESCE(client_port::text, '') AS client_port , EXTRACT(EPOCH FROM backend_start) AS backend_start , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes @@ -473,6 +481,9 @@ monitoringQueriesConfigMap: - client_addr: usage: "LABEL" description: "Client IP address" + - client_port: + usage: "LABEL" + description: "Client TCP port" - backend_start: usage: "COUNTER" description: "Time when this process was started" From 0ecada9d9d5b1f8f4d125585add2c9d7637d194b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 09:29:59 -0300 Subject: [PATCH 02/87] chore(deps): update actions/checkout action to v4.1.1 (#146) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/continuous-delivery.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/release-pr.yml | 2 +- .github/workflows/release-publish.yml | 2 +- .github/workflows/release-tag.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/continuous-delivery.yml b/.github/workflows/continuous-delivery.yml index 3117cb0e1..ef39c8e83 100644 --- a/.github/workflows/continuous-delivery.yml +++ b/.github/workflows/continuous-delivery.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0033ace7f..d876f2f03 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index c01f148df..304eb5120 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Get tag run: | diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index ff898a871..5527f8212 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index a16875cf7..9d4844b68 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Temporarily disable "include administrators" branch protection if: ${{ always() && github.ref == 'refs/heads/main' }} From 8c1dd3c5760b0cb5aa056898f2ef8d491ac52e78 Mon Sep 17 00:00:00 2001 From: Philippe Scorsolini Date: Mon, 30 Oct 2023 11:05:54 +0100 Subject: [PATCH 03/87] fix: avoid setting configmap name with config.secret=true Signed-off-by: Philippe Scorsolini --- charts/cloudnative-pg/templates/deployment.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/charts/cloudnative-pg/templates/deployment.yaml b/charts/cloudnative-pg/templates/deployment.yaml index 6bdcf06e2..45619fa70 100644 --- a/charts/cloudnative-pg/templates/deployment.yaml +++ b/charts/cloudnative-pg/templates/deployment.yaml @@ -49,9 +49,12 @@ spec: - args: - controller - --leader-elect - {{- with .Values.config.name }} - - --config-map-name={{ . }} - - --secret-name={{ . }} + {{- if .Values.config.name }} + {{- if not .Values.config.secret }} + - --config-map-name={{ .Values.config.name }} + {{- else }} + - --secret-name={{ .Values.config.name }} + {{- end }} {{- end }} - --webhook-port={{ .Values.webhook.port }} {{- range .Values.additionalArgs }} From 0a088b1797c3c9299cfd4b076edf1ef9cd117b7a Mon Sep 17 00:00:00 2001 From: Philippe Scorsolini Date: Mon, 30 Oct 2023 11:14:22 +0100 Subject: [PATCH 04/87] fix: actually create config secret if required Signed-off-by: Philippe Scorsolini --- charts/cloudnative-pg/templates/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/cloudnative-pg/templates/config.yaml b/charts/cloudnative-pg/templates/config.yaml index 867d3611c..4dc065fca 100644 --- a/charts/cloudnative-pg/templates/config.yaml +++ b/charts/cloudnative-pg/templates/config.yaml @@ -27,7 +27,6 @@ metadata: {{- end }} data: {{- toYaml .Values.config.data | nindent 2 }} -{{- end }} {{- else }} apiVersion: v1 kind: Secret @@ -43,3 +42,4 @@ metadata: stringData: {{- toYaml .Values.config.data | nindent 2 }} {{- end }} +{{- end }} From f91cefa854e7e8653829bc0210dcf7360c1bf5d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:53:49 +0100 Subject: [PATCH 05/87] Release cloudnative-pg-v0.19.1 (#160) Signed-off-by: Jaime Silvela Co-authored-by: Jaime Silvela --- charts/cloudnative-pg/Chart.yaml | 4 +- charts/cloudnative-pg/README.md | 2 +- .../cloudnative-pg/templates/crds/crds.yaml | 116 +++++++++++++++++- 3 files changed, 118 insertions(+), 4 deletions(-) diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index b4e3ae1ec..ac24b73a0 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,12 +18,12 @@ name: cloudnative-pg description: CloudNativePG Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.19.0" +version: "0.19.1" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.21.0" +appVersion: "1.21.1" sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 0c1201330..21bf54d80 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.19.0](https://img.shields.io/badge/Version-0.19.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.21.0](https://img.shields.io/badge/AppVersion-1.21.0-informational?style=flat-square) +![Version: 0.19.1](https://img.shields.io/badge/Version-0.19.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.21.1](https://img.shields.io/badge/AppVersion-1.21.1-informational?style=flat-square) CloudNativePG Helm Chart diff --git a/charts/cloudnative-pg/templates/crds/crds.yaml b/charts/cloudnative-pg/templates/crds/crds.yaml index d15a9e50d..1a8687db6 100644 --- a/charts/cloudnative-pg/templates/crds/crds.yaml +++ b/charts/cloudnative-pg/templates/crds/crds.yaml @@ -69,6 +69,38 @@ spec: - barmanObjectStore - volumeSnapshot type: string + online: + description: Whether the default type of backup with volume snapshots + is online/hot (`true`, default) or offline/cold (`false`) Overrides + the default setting specified in the cluster field '.spec.backup.volumeSnapshot.online' + type: boolean + onlineConfiguration: + description: Configuration parameters to control the online/hot backup + with volume snapshots Overrides the default settings specified in + the cluster '.backup.volumeSnapshot.onlineConfiguration' stanza + properties: + immediateCheckpoint: + description: Control whether the I/O workload for the backup initial + checkpoint will be limited, according to the `checkpoint_completion_target` + setting on the PostgreSQL server. If set to true, an immediate + checkpoint will be used, meaning PostgreSQL will complete the + checkpoint as soon as possible. `false` by default. + type: boolean + waitForArchive: + default: true + description: If false, the function will return immediately after + the backup is completed, without waiting for WAL to be archived. + This behavior is only useful with backup software that independently + monitors WAL archiving. Otherwise, WAL required to make the + backup consistent might be missing and make the backup useless. + By default, or when this parameter is true, pg_backup_stop will + wait for WAL to be archived when archiving is enabled. On a + standby, this means that it will wait only when archive_mode + = always. If write activity on the primary is low, it may be + useful to run pg_switch_wal on the primary in order to trigger + an immediate segment switch. + type: boolean + type: object target: description: The policy to decide which instance should perform this backup. If empty, it defaults to `cluster.spec.backup.target`. Available @@ -151,6 +183,11 @@ spec: backupId: description: The ID of the Barman backup type: string + backupLabelFile: + description: Backup label file content as returned by Postgres in + case of online (hot) backups + format: byte + type: string backupName: description: The Name of the Barman backup type: string @@ -239,6 +276,10 @@ spec: method: description: The backup method being used type: string + online: + description: Whether the backup was online/hot (`true`) or offline/cold + (`false`) + type: boolean phase: description: The last backup status type: string @@ -338,6 +379,11 @@ spec: description: When the backup was terminated format: date-time type: string + tablespaceMapFile: + description: Tablespace map file content as returned by Postgres in + case of online (hot) backups + format: byte + type: string type: object required: - metadata @@ -1613,6 +1659,43 @@ spec: description: Labels are key-value pairs that will be added to .metadata.labels snapshot resources. type: object + online: + default: true + description: Whether the default type of backup with volume + snapshots is online/hot (`true`, default) or offline/cold + (`false`) + type: boolean + onlineConfiguration: + default: + immediateCheckpoint: false + waitForArchive: true + description: Configuration parameters to control the online/hot + backup with volume snapshots + properties: + immediateCheckpoint: + description: Control whether the I/O workload for the + backup initial checkpoint will be limited, according + to the `checkpoint_completion_target` setting on the + PostgreSQL server. If set to true, an immediate checkpoint + will be used, meaning PostgreSQL will complete the checkpoint + as soon as possible. `false` by default. + type: boolean + waitForArchive: + default: true + description: If false, the function will return immediately + after the backup is completed, without waiting for WAL + to be archived. This behavior is only useful with backup + software that independently monitors WAL archiving. + Otherwise, WAL required to make the backup consistent + might be missing and make the backup useless. By default, + or when this parameter is true, pg_backup_stop will + wait for WAL to be archived when archiving is enabled. + On a standby, this means that it will wait only when + archive_mode = always. If write activity on the primary + is low, it may be useful to run pg_switch_wal on the + primary in order to trigger an immediate segment switch. + type: boolean + type: object snapshotOwnerReference: default: none description: SnapshotOwnerReference indicates the type of @@ -1670,7 +1753,6 @@ spec: type: string type: array schemaOnly: - default: false description: 'When set to true, only the `pre-data` and `post-data` sections of `pg_restore` are invoked, avoiding data import. Default: `false`.' @@ -12341,6 +12423,38 @@ spec: - barmanObjectStore - volumeSnapshot type: string + online: + description: Whether the default type of backup with volume snapshots + is online/hot (`true`, default) or offline/cold (`false`) Overrides + the default setting specified in the cluster field '.spec.backup.volumeSnapshot.online' + type: boolean + onlineConfiguration: + description: Configuration parameters to control the online/hot backup + with volume snapshots Overrides the default settings specified in + the cluster '.backup.volumeSnapshot.onlineConfiguration' stanza + properties: + immediateCheckpoint: + description: Control whether the I/O workload for the backup initial + checkpoint will be limited, according to the `checkpoint_completion_target` + setting on the PostgreSQL server. If set to true, an immediate + checkpoint will be used, meaning PostgreSQL will complete the + checkpoint as soon as possible. `false` by default. + type: boolean + waitForArchive: + default: true + description: If false, the function will return immediately after + the backup is completed, without waiting for WAL to be archived. + This behavior is only useful with backup software that independently + monitors WAL archiving. Otherwise, WAL required to make the + backup consistent might be missing and make the backup useless. + By default, or when this parameter is true, pg_backup_stop will + wait for WAL to be archived when archiving is enabled. On a + standby, this means that it will wait only when archive_mode + = always. If write activity on the primary is low, it may be + useful to run pg_switch_wal on the primary in order to trigger + an immediate segment switch. + type: boolean + type: object schedule: description: The schedule does not follow the same format used in Kubernetes CronJobs as it includes an additional seconds specifier, From 11e520e2d0c7ce45035aeb2d501170c71a138b3b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 11:58:14 -0300 Subject: [PATCH 06/87] chore(deps): update helm/chart-testing-action action to v2.6.1 (#158) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d876f2f03..a1097c842 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -25,7 +25,7 @@ jobs: python-version: 3.7 - name: Set up chart-testing - uses: helm/chart-testing-action@e8788873172cb653a90ca2e819d79d65a66d4e76 # v2.4.0 + uses: helm/chart-testing-action@e6669bcd63d7cb57cb4380c33043eebe5d111992 # v2.6.1 - name: Run chart-testing (list-changed) id: list-changed From c4f98c1c501311008563e730e40e204841cd7bcf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:02:26 -0300 Subject: [PATCH 07/87] chore(deps): update actions/setup-python digest to 65d7f2d (#153) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a1097c842..64a449e31 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: with: version: v3.4.0 - - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4 + - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4 with: python-version: 3.7 From c4b0f26230d0baff08498441dfd3a65619382871 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:06:33 -0300 Subject: [PATCH 08/87] chore(deps): update christophebedard/tag-version-commit action to v1.7.0 (#155) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-tag.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 9d4844b68..e66b8aa60 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -28,7 +28,7 @@ jobs: - name: Create tag if: github.event.pull_request.merged == true && startsWith(${{ github.head_ref }}, "release/") - uses: christophebedard/tag-version-commit@ea0363ff76cae3e81c21695cdd21218204be290b # v1.6.3 + uses: christophebedard/tag-version-commit@57ffb155fc61c8ab098fcfa273469b532c1d4ce7 # v1.7.0 with: token: ${{ secrets.REPO_GHA_PAT }} version_regex: '^Release ([a-z-]+-v[0-9]+\.[0-9]+\.[0-9]+)' From d84ad82e0e008aaa3fb5c003d877d2d7dcfb005a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:58:45 +0100 Subject: [PATCH 09/87] Release cloudnative-pg-v0.20.0 (#170) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Niccolò Fei Co-authored-by: Niccolò Fei --- charts/cloudnative-pg/Chart.yaml | 4 +- charts/cloudnative-pg/README.md | 2 +- .../cloudnative-pg/templates/crds/crds.yaml | 688 +++++++++++++++++- 3 files changed, 677 insertions(+), 17 deletions(-) diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index ac24b73a0..7f6112602 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,12 +18,12 @@ name: cloudnative-pg description: CloudNativePG Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.19.1" +version: "0.20.0" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.21.1" +appVersion: "1.22.0" sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 21bf54d80..86e291fc5 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.19.1](https://img.shields.io/badge/Version-0.19.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.21.1](https://img.shields.io/badge/AppVersion-1.21.1-informational?style=flat-square) +![Version: 0.20.0](https://img.shields.io/badge/Version-0.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.22.0](https://img.shields.io/badge/AppVersion-1.22.0-informational?style=flat-square) CloudNativePG Helm Chart diff --git a/charts/cloudnative-pg/templates/crds/crds.yaml b/charts/cloudnative-pg/templates/crds/crds.yaml index 1a8687db6..95492809a 100644 --- a/charts/cloudnative-pg/templates/crds/crds.yaml +++ b/charts/cloudnative-pg/templates/crds/crds.yaml @@ -361,9 +361,13 @@ spec: name: description: Name is the snapshot resource name type: string + tablespaceName: + description: TablespaceName is the name of the snapshotted + tablespace. Only set when type is PG_TABLESPACE + type: string type: description: Type is tho role of the snapshot in the cluster, - such as PG_DATA and PG_WAL + such as PG_DATA, PG_WAL and PG_TABLESPACE type: string required: - name @@ -1705,6 +1709,13 @@ spec: - cluster - backup type: string + tablespaceClassName: + additionalProperties: + type: string + description: TablespaceClassName specifies the Snapshot Class + to be used for the tablespaces. defaults to the PGDATA Snapshot + Class, if set + type: object walClassName: description: WalClassName specifies the Snapshot Class to be used for the PG_WAL PersistentVolumeClaim. @@ -2046,6 +2057,34 @@ spec: - name type: object x-kubernetes-map-type: atomic + tablespaceStorage: + additionalProperties: + description: TypedLocalObjectReference contains enough + information to let you locate the typed referenced + object inside the same namespace. + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + description: Configuration of the storage for PostgreSQL + tablespaces + type: object walStorage: description: Configuration of the storage for PostgreSQL WAL (Write-Ahead Log) @@ -2582,7 +2621,13 @@ spec: type: string password: description: The reference to the password to be used to connect - to the server + to the server. If a password is provided, CloudNativePG creates + a PostgreSQL passfile at `/controller/external/NAME/pass` + (where "NAME" is the cluster's name). This passfile is automatically + referenced in the connection string when establishing a connection + to the remote PostgreSQL server from the current PostgreSQL + `Cluster`. This ensures secure and efficient password management + for external clusters. properties: key: description: The key of the secret to select from. Must @@ -2896,6 +2941,156 @@ spec: default: false description: Enable or disable the `PodMonitor` type: boolean + podMonitorMetricRelabelings: + description: The list of metric relabelings for the `PodMonitor`. + Applied to samples before ingestion. + items: + description: "RelabelConfig allows dynamic rewriting of the + label set for targets, alerts, scraped samples and remote + write samples. \n More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config" + properties: + action: + default: replace + description: "Action to perform based on the regex matching. + \n `Uppercase` and `Lowercase` actions require Prometheus + >= v2.36.0. `DropEqual` and `KeepEqual` actions require + Prometheus >= v2.41.0. \n Default: \"Replace\"" + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + description: "Modulus to take of the hash of the source + label values. \n Only applicable when the action is `HashMod`." + format: int64 + type: integer + regex: + description: Regular expression against which the extracted + value is matched. + type: string + replacement: + description: "Replacement value against which a Replace + action is performed if the regular expression matches. + \n Regex capture groups are available." + type: string + separator: + description: Separator is the string between concatenated + SourceLabels. + type: string + sourceLabels: + description: The source labels select values from existing + labels. Their content is concatenated using the configured + Separator and matched against the configured regular expression. + items: + description: LabelName is a valid Prometheus label name + which may only contain ASCII letters, numbers, as well + as underscores. + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + description: "Label to which the resulting string is written + in a replacement. \n It is mandatory for `Replace`, `HashMod`, + `Lowercase`, `Uppercase`, `KeepEqual` and `DropEqual` + actions. \n Regex capture groups are available." + type: string + type: object + type: array + podMonitorRelabelings: + description: The list of relabelings for the `PodMonitor`. Applied + to samples before scraping. + items: + description: "RelabelConfig allows dynamic rewriting of the + label set for targets, alerts, scraped samples and remote + write samples. \n More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config" + properties: + action: + default: replace + description: "Action to perform based on the regex matching. + \n `Uppercase` and `Lowercase` actions require Prometheus + >= v2.36.0. `DropEqual` and `KeepEqual` actions require + Prometheus >= v2.41.0. \n Default: \"Replace\"" + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + description: "Modulus to take of the hash of the source + label values. \n Only applicable when the action is `HashMod`." + format: int64 + type: integer + regex: + description: Regular expression against which the extracted + value is matched. + type: string + replacement: + description: "Replacement value against which a Replace + action is performed if the regular expression matches. + \n Regex capture groups are available." + type: string + separator: + description: Separator is the string between concatenated + SourceLabels. + type: string + sourceLabels: + description: The source labels select values from existing + labels. Their content is concatenated using the configured + Separator and matched against the configured regular expression. + items: + description: LabelName is a valid Prometheus label name + which may only contain ASCII letters, numbers, as well + as underscores. + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + description: "Label to which the resulting string is written + in a replacement. \n It is mandatory for `Replace`, `HashMod`, + `Lowercase`, `Uppercase`, `KeepEqual` and `DropEqual` + actions. \n Regex capture groups are available." + type: string + type: object + type: array type: object nodeMaintenanceWindow: description: Define a maintenance window for the Kubernetes nodes @@ -2925,6 +3120,12 @@ spec: postgresql: description: Configuration of the PostgreSQL server properties: + enableAlterSystem: + description: If this parameter is true, the user will be able + to invoke `ALTER SYSTEM` on this CloudNativePG Cluster. This + should only be used for debugging and troubleshooting. Defaults + to false. + type: boolean ldap: description: Options to specify LDAP configuration properties: @@ -3709,10 +3910,9 @@ spec: reapplied to the created PVCs. Size cannot be decreased. type: string storageClass: - description: StorageClass to use for database data (`PGDATA`). - Applied after evaluating the PVC template, if available. If - not specified, generated PVCs will be satisfied by the default - storage class + description: StorageClass to use for PVCs. Applied after evaluating + the PVC template, if available. If not specified, the generated + PVCs will use the default storage class type: string type: object superuserSecret: @@ -3732,6 +3932,273 @@ spec: is 3600 seconds (1 hour). format: int32 type: integer + tablespaces: + description: The tablespaces configuration + items: + description: TablespaceConfiguration is the configuration of a tablespace, + and includes the storage specification for the tablespace + properties: + name: + description: The name of the tablespace + type: string + owner: + description: Owner is the PostgreSQL user owning the tablespace + properties: + name: + type: string + type: object + storage: + description: The storage configuration for the tablespace + properties: + pvcTemplate: + description: Template to be used to generate the Persistent + Volume Claim + properties: + accessModes: + description: 'accessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) If the provisioner + or an external controller can support the specified + data source, it will create a new volume based on + the contents of the specified data source. When the + AnyVolumeDataSource feature gate is enabled, dataSource + contents will be copied to dataSourceRef, and dataSourceRef + contents will be copied to dataSource when dataSourceRef.namespace + is not specified. If the namespace is specified, then + dataSourceRef will not be copied to dataSource.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: 'dataSourceRef specifies the object from + which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty + API group (non core object) or a PersistentVolumeClaim + object. When this field is specified, volume binding + will only succeed if the type of the specified object + matches some installed volume populator or dynamic + provisioner. This field will replace the functionality + of the dataSource field and as such if both fields + are non-empty, they must have the same value. For + backwards compatibility, when namespace isn''t specified + in dataSourceRef, both fields (dataSource and dataSourceRef) + will be set to the same value automatically if one + of them is empty and the other is non-empty. When + namespace is specified in dataSourceRef, dataSource + isn''t set to the same value and must be empty. There + are three important differences between dataSource + and dataSourceRef: * While dataSource only allows + two specific types of objects, dataSourceRef allows + any non-core object, as well as PersistentVolumeClaim + objects. * While dataSource ignores disallowed values + (dropping them), dataSourceRef preserves all values, + and generates an error if a disallowed value is specified. + * While dataSource only allows local objects, dataSourceRef + allows objects in any namespaces. (Beta) Using this + field requires the AnyVolumeDataSource feature gate + to be enabled. (Alpha) Using the namespace field of + dataSourceRef requires the CrossNamespaceVolumeDataSource + feature gate to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace is + specified, a gateway.networking.k8s.io/ReferenceGrant + object is required in the referent namespace to + allow that namespace's owner to accept the reference. + See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource + feature gate to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: 'resources represents the minimum resources + the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify resource + requirements that are lower than previous value but + must still be higher than capacity recorded in the + status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + claims: + description: "Claims lists the names of resources, + defined in spec.resourceClaims, that are used + by this container. \n This is an alpha field and + requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can + only be set for containers." + items: + description: ResourceClaim references one entry + in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one + entry in pod.spec.resourceClaims of the + Pod where this field is used. It makes that + resource available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is + omitted for a container, it defaults to Limits + if that is explicitly specified, otherwise to + an implementation-defined value. Requests cannot + exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: 'storageClassName is the name of the StorageClass + required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume + is required by the claim. Value of Filesystem is implied + when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference to + the PersistentVolume backing this claim. + type: string + type: object + resizeInUseVolumes: + default: true + description: Resize existent PVCs, defaults to true + type: boolean + size: + description: Size of the storage. Required if not already + specified in the PVC template. Changes to this field are + automatically reapplied to the created PVCs. Size cannot + be decreased. + type: string + storageClass: + description: StorageClass to use for PVCs. Applied after + evaluating the PVC template, if available. If not specified, + the generated PVCs will use the default storage class + type: string + type: object + temporary: + default: false + description: When set to true, the tablespace will be added + as a `temp_tablespaces` entry in PostgreSQL, and will be available + to automatically house temp database objects, or other temporary + files. Please refer to PostgreSQL documentation for more information + on the `temp_tablespaces` GUC. + type: boolean + required: + - name + - storage + type: object + type: array topologySpreadConstraints: description: 'TopologySpreadConstraints specifies how to spread matching pods among the given topology. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/' @@ -4128,10 +4595,9 @@ spec: reapplied to the created PVCs. Size cannot be decreased. type: string storageClass: - description: StorageClass to use for database data (`PGDATA`). - Applied after evaluating the PVC template, if available. If - not specified, generated PVCs will be satisfied by the default - storage class + description: StorageClass to use for PVCs. Applied after evaluating + the PVC template, if available. If not specified, the generated + PVCs will use the default storage class type: string type: object required: @@ -4288,8 +4754,8 @@ spec: type: string currentPrimaryFailingSinceTimestamp: description: The timestamp when the primary was detected to be unhealthy - This field is reported when spec.failoverDelay is populated or during - online upgrades + This field is reported when `.spec.failoverDelay` is populated or + during online upgrades type: string currentPrimaryTimestamp: description: The timestamp when the last actual promotion to primary @@ -4303,8 +4769,15 @@ spec: type: array firstRecoverabilityPoint: description: The first recoverability point, stored as a date in RFC3339 - format + format. This field is calculated from the content of FirstRecoverabilityPointByMethod type: string + firstRecoverabilityPointByMethod: + additionalProperties: + format: date-time + type: string + description: The first recoverability point, stored as a date in RFC3339 + format, per backup method type + type: object healthyPVC: description: List of all the PVCs not dangling nor initializing items: @@ -4358,8 +4831,16 @@ spec: description: Stored as a date in RFC3339 format type: string lastSuccessfulBackup: - description: Stored as a date in RFC3339 format + description: Last successful backup, stored as a date in RFC3339 format + This field is calculated from the content of LastSuccessfulBackupByMethod type: string + lastSuccessfulBackupByMethod: + additionalProperties: + format: date-time + type: string + description: Last successful backup, stored as a date in RFC3339 format, + per backup method type + type: object latestGeneratedNode: description: ID of the latest generated node (used to avoid node name clashing) @@ -4459,6 +4940,11 @@ spec: description: The resource version of the PostgreSQL client-side CA secret version type: string + externalClusterSecretVersion: + additionalProperties: + type: string + description: The resource versions of the external cluster secrets + type: object managedRoleSecretVersion: additionalProperties: type: string @@ -4487,6 +4973,30 @@ spec: description: The resource version of the "postgres" user secret type: string type: object + tablespacesStatus: + description: TablespacesStatus reports the state of the declarative + tablespaces in the cluster + items: + description: TablespaceState represents the state of a tablespace + in a cluster + properties: + error: + description: Error is the reconciliation error, if any + type: string + name: + description: Name is the name of the tablespace + type: string + owner: + description: Owner is the PostgreSQL user owning the tablespace + type: string + state: + description: State is the latest reconciliation state + type: string + required: + - name + - state + type: object + type: array targetPrimary: description: Target primary instance, this is different from the previous one during a switchover or a failover @@ -4666,6 +5176,156 @@ spec: default: false description: Enable or disable the `PodMonitor` type: boolean + podMonitorMetricRelabelings: + description: The list of metric relabelings for the `PodMonitor`. + Applied to samples before ingestion. + items: + description: "RelabelConfig allows dynamic rewriting of the + label set for targets, alerts, scraped samples and remote + write samples. \n More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config" + properties: + action: + default: replace + description: "Action to perform based on the regex matching. + \n `Uppercase` and `Lowercase` actions require Prometheus + >= v2.36.0. `DropEqual` and `KeepEqual` actions require + Prometheus >= v2.41.0. \n Default: \"Replace\"" + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + description: "Modulus to take of the hash of the source + label values. \n Only applicable when the action is `HashMod`." + format: int64 + type: integer + regex: + description: Regular expression against which the extracted + value is matched. + type: string + replacement: + description: "Replacement value against which a Replace + action is performed if the regular expression matches. + \n Regex capture groups are available." + type: string + separator: + description: Separator is the string between concatenated + SourceLabels. + type: string + sourceLabels: + description: The source labels select values from existing + labels. Their content is concatenated using the configured + Separator and matched against the configured regular expression. + items: + description: LabelName is a valid Prometheus label name + which may only contain ASCII letters, numbers, as well + as underscores. + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + description: "Label to which the resulting string is written + in a replacement. \n It is mandatory for `Replace`, `HashMod`, + `Lowercase`, `Uppercase`, `KeepEqual` and `DropEqual` + actions. \n Regex capture groups are available." + type: string + type: object + type: array + podMonitorRelabelings: + description: The list of relabelings for the `PodMonitor`. Applied + to samples before scraping. + items: + description: "RelabelConfig allows dynamic rewriting of the + label set for targets, alerts, scraped samples and remote + write samples. \n More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config" + properties: + action: + default: replace + description: "Action to perform based on the regex matching. + \n `Uppercase` and `Lowercase` actions require Prometheus + >= v2.36.0. `DropEqual` and `KeepEqual` actions require + Prometheus >= v2.41.0. \n Default: \"Replace\"" + enum: + - replace + - Replace + - keep + - Keep + - drop + - Drop + - hashmod + - HashMod + - labelmap + - LabelMap + - labeldrop + - LabelDrop + - labelkeep + - LabelKeep + - lowercase + - Lowercase + - uppercase + - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual + type: string + modulus: + description: "Modulus to take of the hash of the source + label values. \n Only applicable when the action is `HashMod`." + format: int64 + type: integer + regex: + description: Regular expression against which the extracted + value is matched. + type: string + replacement: + description: "Replacement value against which a Replace + action is performed if the regular expression matches. + \n Regex capture groups are available." + type: string + separator: + description: Separator is the string between concatenated + SourceLabels. + type: string + sourceLabels: + description: The source labels select values from existing + labels. Their content is concatenated using the configured + Separator and matched against the configured regular expression. + items: + description: LabelName is a valid Prometheus label name + which may only contain ASCII letters, numbers, as well + as underscores. + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + type: string + type: array + targetLabel: + description: "Label to which the resulting string is written + in a replacement. \n It is mandatory for `Replace`, `HashMod`, + `Lowercase`, `Uppercase`, `KeepEqual` and `DropEqual` + actions. \n Regex capture groups are available." + type: string + type: object + type: array type: object pgbouncer: description: The PgBouncer configuration From 224777278c1eae5fde535d953d64868dbe9fb4cc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:45:21 -0300 Subject: [PATCH 10/87] chore(deps): update benjefferies/branch-protection-bot action to v1.1.2 (#167) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-tag.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index e66b8aa60..19dd3c12a 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -20,7 +20,7 @@ jobs: name: Temporarily disable "include administrators" branch protection if: ${{ always() && github.ref == 'refs/heads/main' }} id: disable_include_admins - uses: benjefferies/branch-protection-bot@a37e132671409118a53e37e9c81d63fd534bb2e7 # 1.0.9 + uses: benjefferies/branch-protection-bot@af281f37de86139d1c7a27b91176b5dc1c2c827c # v1.1.2 with: access_token: ${{ secrets.REPO_GHA_PAT }} branch: main @@ -35,7 +35,7 @@ jobs: dry_run: false - name: Enable "include administrators" branch protection - uses: benjefferies/branch-protection-bot@a37e132671409118a53e37e9c81d63fd534bb2e7 # 1.0.9 + uses: benjefferies/branch-protection-bot@af281f37de86139d1c7a27b91176b5dc1c2c827c # v1.1.2 if: ${{ always() && github.ref == 'refs/heads/main' }} with: access_token: ${{ secrets.REPO_GHA_PAT }} From 42ab86f7be5d65df87ae03cce255e8ff6a1905a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:53:56 -0300 Subject: [PATCH 11/87] chore(deps): update actions/setup-python action to v5 (#168) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 64a449e31..9e52d5051 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: with: version: v3.4.0 - - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4 + - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 with: python-version: 3.7 From f2fcf29ed7e5235ee1fe7c1ce0d7e466de12dae6 Mon Sep 17 00:00:00 2001 From: Matthias Baur Date: Thu, 4 Jan 2024 14:24:29 +0100 Subject: [PATCH 12/87] Automatically restart operator on config changes Signed-off-by: Matthias Baur --- charts/cloudnative-pg/templates/deployment.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/charts/cloudnative-pg/templates/deployment.yaml b/charts/cloudnative-pg/templates/deployment.yaml index 45619fa70..858248a66 100644 --- a/charts/cloudnative-pg/templates/deployment.yaml +++ b/charts/cloudnative-pg/templates/deployment.yaml @@ -31,8 +31,9 @@ spec: {{- include "cloudnative-pg.selectorLabels" . | nindent 6 }} template: metadata: - {{- with .Values.podAnnotations }} annotations: + checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} labels: From 6c2bad2242c78119ed16d184f8a4459e93a1bb32 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Thu, 1 Feb 2024 15:39:21 +0200 Subject: [PATCH 13/87] feat: integrate the Grafana Dashboard in the chart (#176) Move the Grafana dashboard inside the operator chart. Signed-off-by: Itay Grudev --- charts/cloudnative-pg/Chart.yaml | 2 +- charts/cloudnative-pg/README.md | 7 +- .../monitoring/grafana-dashboard.json | 6450 +++++++++++++++++ .../templates/grafana-dashboard.yaml | 12 + charts/cloudnative-pg/values.schema.json | 20 + charts/cloudnative-pg/values.yaml | 10 + 6 files changed, 6499 insertions(+), 2 deletions(-) create mode 100644 charts/cloudnative-pg/monitoring/grafana-dashboard.json create mode 100644 charts/cloudnative-pg/templates/grafana-dashboard.yaml diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index 7f6112602..27eaf026f 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,7 +18,7 @@ name: cloudnative-pg description: CloudNativePG Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.20.0" +version: "0.20.1" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 86e291fc5..3c58872d0 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.20.0](https://img.shields.io/badge/Version-0.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.22.0](https://img.shields.io/badge/AppVersion-1.22.0-informational?style=flat-square) +![Version: 0.20.1](https://img.shields.io/badge/Version-0.20.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.22.0](https://img.shields.io/badge/AppVersion-1.22.0-informational?style=flat-square) CloudNativePG Helm Chart @@ -35,6 +35,11 @@ CloudNativePG Helm Chart | image.repository | string | `"ghcr.io/cloudnative-pg/cloudnative-pg"` | | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | | imagePullSecrets | list | `[]` | | +| monitoring.grafanaDashboard.configMapName | string | `"cnpg-grafana-dashboard"` | The name of the ConfigMap containing the dashboard. | +| monitoring.grafanaDashboard.create | bool | `false` | | +| monitoring.grafanaDashboard.namespace | string | `""` | Allows overriding the namespace where the ConfigMap will be created, defaulting to the same one as the Release. | +| monitoring.grafanaDashboard.sidecarLabel | string | `"grafana_dashboard"` | Label that ConfigMaps should have to be loaded as dashboards. | +| monitoring.grafanaDashboard.sidecarLabelValue | string | `""` | Label value that ConfigMaps should have to be loaded as dashboards. | | monitoring.podMonitorEnabled | bool | `false` | Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. | | monitoringQueriesConfigMap.name | string | `"cnpg-default-monitoring"` | The name of the default monitoring configmap. | | monitoringQueriesConfigMap.queries | string | `"backends:\n query: |\n SELECT sa.datname\n , sa.usename\n , sa.application_name\n , states.state\n , COALESCE(sa.count, 0) AS total\n , COALESCE(sa.max_tx_secs, 0) AS max_tx_duration_seconds\n FROM ( VALUES ('active')\n , ('idle')\n , ('idle in transaction')\n , ('idle in transaction (aborted)')\n , ('fastpath function call')\n , ('disabled')\n ) AS states(state)\n LEFT JOIN (\n SELECT datname\n , state\n , usename\n , COALESCE(application_name, '') AS application_name\n , COUNT(*)\n , COALESCE(EXTRACT (EPOCH FROM (max(now() - xact_start))), 0) AS max_tx_secs\n FROM pg_catalog.pg_stat_activity\n GROUP BY datname, state, usename, application_name\n ) sa ON states.state = sa.state\n WHERE sa.usename IS NOT NULL\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - usename:\n usage: \"LABEL\"\n description: \"Name of the user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - state:\n usage: \"LABEL\"\n description: \"State of the backend\"\n - total:\n usage: \"GAUGE\"\n description: \"Number of backends\"\n - max_tx_duration_seconds:\n usage: \"GAUGE\"\n description: \"Maximum duration of a transaction in seconds\"\n\nbackends_waiting:\n query: |\n SELECT count(*) AS total\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks\n ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n WHERE NOT blocked_locks.granted\n metrics:\n - total:\n usage: \"GAUGE\"\n description: \"Total number of backends that are currently waiting on other queries\"\n\npg_database:\n query: |\n SELECT datname\n , pg_catalog.pg_database_size(datname) AS size_bytes\n , pg_catalog.age(datfrozenxid) AS xid_age\n , pg_catalog.mxid_age(datminmxid) AS mxid_age\n FROM pg_catalog.pg_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - size_bytes:\n usage: \"GAUGE\"\n description: \"Disk space used by the database\"\n - xid_age:\n usage: \"GAUGE\"\n description: \"Number of transactions from the frozen XID to the current one\"\n - mxid_age:\n usage: \"GAUGE\"\n description: \"Number of multiple transactions (Multixact) from the frozen XID to the current one\"\n\npg_postmaster:\n query: |\n SELECT EXTRACT(EPOCH FROM pg_postmaster_start_time) AS start_time\n FROM pg_catalog.pg_postmaster_start_time()\n metrics:\n - start_time:\n usage: \"GAUGE\"\n description: \"Time at which postgres started (based on epoch)\"\n\npg_replication:\n query: \"SELECT CASE WHEN (\n NOT pg_catalog.pg_is_in_recovery()\n OR pg_catalog.pg_last_wal_receive_lsn() = pg_catalog.pg_last_wal_replay_lsn())\n THEN 0\n ELSE GREATEST (0,\n EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp())))\n END AS lag,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up,\n (SELECT count(*) FROM pg_catalog.pg_stat_replication) AS streaming_replicas\"\n metrics:\n - lag:\n usage: \"GAUGE\"\n description: \"Replication lag behind primary in seconds\"\n - in_recovery:\n usage: \"GAUGE\"\n description: \"Whether the instance is in recovery\"\n - is_wal_receiver_up:\n usage: \"GAUGE\"\n description: \"Whether the instance wal_receiver is up\"\n - streaming_replicas:\n usage: \"GAUGE\"\n description: \"Number of streaming replicas connected to the instance\"\n\npg_replication_slots:\n query: |\n SELECT slot_name,\n slot_type,\n database,\n active,\n (CASE pg_catalog.pg_is_in_recovery()\n WHEN TRUE THEN pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_last_wal_receive_lsn(), restart_lsn)\n ELSE pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn)\n END) as pg_wal_lsn_diff\n FROM pg_catalog.pg_replication_slots\n WHERE NOT temporary\n metrics:\n - slot_name:\n usage: \"LABEL\"\n description: \"Name of the replication slot\"\n - slot_type:\n usage: \"LABEL\"\n description: \"Type of the replication slot\"\n - database:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - active:\n usage: \"GAUGE\"\n description: \"Flag indicating whether the slot is active\"\n - pg_wal_lsn_diff:\n usage: \"GAUGE\"\n description: \"Replication lag in bytes\"\n\npg_stat_archiver:\n query: |\n SELECT archived_count\n , failed_count\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_archived_time)), -1) AS seconds_since_last_archival\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_failed_time)), -1) AS seconds_since_last_failure\n , COALESCE(EXTRACT(EPOCH FROM last_archived_time), -1) AS last_archived_time\n , COALESCE(EXTRACT(EPOCH FROM last_failed_time), -1) AS last_failed_time\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_archived_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_archived_wal_start_lsn\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_failed_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_failed_wal_start_lsn\n , EXTRACT(EPOCH FROM stats_reset) AS stats_reset_time\n FROM pg_catalog.pg_stat_archiver\n metrics:\n - archived_count:\n usage: \"COUNTER\"\n description: \"Number of WAL files that have been successfully archived\"\n - failed_count:\n usage: \"COUNTER\"\n description: \"Number of failed attempts for archiving WAL files\"\n - seconds_since_last_archival:\n usage: \"GAUGE\"\n description: \"Seconds since the last successful archival operation\"\n - seconds_since_last_failure:\n usage: \"GAUGE\"\n description: \"Seconds since the last failed archival operation\"\n - last_archived_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving succeeded\"\n - last_failed_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving failed\"\n - last_archived_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Archived WAL start LSN\"\n - last_failed_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Last failed WAL LSN\"\n - stats_reset_time:\n usage: \"GAUGE\"\n description: \"Time at which these statistics were last reset\"\n\npg_stat_bgwriter:\n query: |\n SELECT checkpoints_timed\n , checkpoints_req\n , checkpoint_write_time\n , checkpoint_sync_time\n , buffers_checkpoint\n , buffers_clean\n , maxwritten_clean\n , buffers_backend\n , buffers_backend_fsync\n , buffers_alloc\n FROM pg_catalog.pg_stat_bgwriter\n metrics:\n - checkpoints_timed:\n usage: \"COUNTER\"\n description: \"Number of scheduled checkpoints that have been performed\"\n - checkpoints_req:\n usage: \"COUNTER\"\n description: \"Number of requested checkpoints that have been performed\"\n - checkpoint_write_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds\"\n - checkpoint_sync_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds\"\n - buffers_checkpoint:\n usage: \"COUNTER\"\n description: \"Number of buffers written during checkpoints\"\n - buffers_clean:\n usage: \"COUNTER\"\n description: \"Number of buffers written by the background writer\"\n - maxwritten_clean:\n usage: \"COUNTER\"\n description: \"Number of times the background writer stopped a cleaning scan because it had written too many buffers\"\n - buffers_backend:\n usage: \"COUNTER\"\n description: \"Number of buffers written directly by a backend\"\n - buffers_backend_fsync:\n usage: \"COUNTER\"\n description: \"Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)\"\n - buffers_alloc:\n usage: \"COUNTER\"\n description: \"Number of buffers allocated\"\n\npg_stat_database:\n query: |\n SELECT datname\n , xact_commit\n , xact_rollback\n , blks_read\n , blks_hit\n , tup_returned\n , tup_fetched\n , tup_inserted\n , tup_updated\n , tup_deleted\n , conflicts\n , temp_files\n , temp_bytes\n , deadlocks\n , blk_read_time\n , blk_write_time\n FROM pg_catalog.pg_stat_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of this database\"\n - xact_commit:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been committed\"\n - xact_rollback:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been rolled back\"\n - blks_read:\n usage: \"COUNTER\"\n description: \"Number of disk blocks read in this database\"\n - blks_hit:\n usage: \"COUNTER\"\n description: \"Number of times disk blocks were found already in the buffer cache, so that a read was not necessary (this only includes hits in the PostgreSQL buffer cache, not the operating system's file system cache)\"\n - tup_returned:\n usage: \"COUNTER\"\n description: \"Number of rows returned by queries in this database\"\n - tup_fetched:\n usage: \"COUNTER\"\n description: \"Number of rows fetched by queries in this database\"\n - tup_inserted:\n usage: \"COUNTER\"\n description: \"Number of rows inserted by queries in this database\"\n - tup_updated:\n usage: \"COUNTER\"\n description: \"Number of rows updated by queries in this database\"\n - tup_deleted:\n usage: \"COUNTER\"\n description: \"Number of rows deleted by queries in this database\"\n - conflicts:\n usage: \"COUNTER\"\n description: \"Number of queries canceled due to conflicts with recovery in this database\"\n - temp_files:\n usage: \"COUNTER\"\n description: \"Number of temporary files created by queries in this database\"\n - temp_bytes:\n usage: \"COUNTER\"\n description: \"Total amount of data written to temporary files by queries in this database\"\n - deadlocks:\n usage: \"COUNTER\"\n description: \"Number of deadlocks detected in this database\"\n - blk_read_time:\n usage: \"COUNTER\"\n description: \"Time spent reading data file blocks by backends in this database, in milliseconds\"\n - blk_write_time:\n usage: \"COUNTER\"\n description: \"Time spent writing data file blocks by backends in this database, in milliseconds\"\n\npg_stat_replication:\n primary: true\n query: |\n SELECT usename\n , COALESCE(application_name, '') AS application_name\n , COALESCE(client_addr::text, '') AS client_addr\n , COALESCE(client_port::text, '') AS client_port\n , EXTRACT(EPOCH FROM backend_start) AS backend_start\n , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), write_lsn) AS write_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), flush_lsn) AS flush_diff_bytes\n , COALESCE(pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), replay_lsn),0) AS replay_diff_bytes\n , COALESCE((EXTRACT(EPOCH FROM write_lag)),0)::float AS write_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM flush_lag)),0)::float AS flush_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM replay_lag)),0)::float AS replay_lag_seconds\n FROM pg_catalog.pg_stat_replication\n metrics:\n - usename:\n usage: \"LABEL\"\n description: \"Name of the replication user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - client_addr:\n usage: \"LABEL\"\n description: \"Client IP address\"\n - client_port:\n usage: \"LABEL\"\n description: \"Client TCP port\"\n - backend_start:\n usage: \"COUNTER\"\n description: \"Time when this process was started\"\n - backend_xmin_age:\n usage: \"COUNTER\"\n description: \"The age of this standby's xmin horizon\"\n - sent_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location sent on this connection\"\n - write_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location written to disk by this standby server\"\n - flush_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location flushed to disk by this standby server\"\n - replay_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location replayed into the database on this standby server\"\n - write_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written it\"\n - flush_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it\"\n - replay_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written, flushed and applied it\"\n\npg_settings:\n query: |\n SELECT name,\n CASE setting WHEN 'on' THEN '1' WHEN 'off' THEN '0' ELSE setting END AS setting\n FROM pg_catalog.pg_settings\n WHERE vartype IN ('integer', 'real', 'bool')\n ORDER BY 1\n metrics:\n - name:\n usage: \"LABEL\"\n description: \"Name of the setting\"\n - setting:\n usage: \"GAUGE\"\n description: \"Setting value\"\n"` | A string representation of a YAML defining monitoring queries. | diff --git a/charts/cloudnative-pg/monitoring/grafana-dashboard.json b/charts/cloudnative-pg/monitoring/grafana-dashboard.json new file mode 100644 index 000000000..13346f51d --- /dev/null +++ b/charts/cloudnative-pg/monitoring/grafana-dashboard.json @@ -0,0 +1,6450 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": false, + "keepTime": false, + "tags": [ + "cloudnativepg" + ], + "targetBlank": false, + "title": "Related Dashboards", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "liveNow": false, + "panels": [ + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 563, + "panels": [], + "title": "Row title", + "type": "row" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 562, + "panels": [], + "title": "Summary", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 0, + "y": 2 + }, + "id": 334, + "options": { + "alertInstanceLabelFilter": "{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", + "alertName": "", + "dashboardAlerts": false, + "folder": "", + "groupBy": [], + "groupMode": "default", + "maxItems": 20, + "sortOrder": 1, + "stateFilter": { + "error": true, + "firing": true, + "noData": false, + "normal": true, + "pending": true + }, + "viewMode": "list" + }, + "title": "Alerts", + "type": "alertlist" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 15, + "x": 3, + "y": 2 + }, + "id": 336, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "10.2.2", + "title": "Overview", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 18, + "y": 2 + }, + "id": 352, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "10.2.2", + "title": "Storage", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 21, + "y": 2 + }, + "id": 354, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "10.2.2", + "title": "Backups", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-blue", + "value": null + } + ] + }, + "unit": "dateTimeFromNow" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 3, + "y": 3 + }, + "id": 338, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "max(cnpg_pg_postmaster_start_time{namespace=~\"$namespace\",pod=~\"$instances\"})*1000", + "format": "time_series", + "hide": false, + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Last failover", + "transformations": [], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 5, + "y": 3 + }, + "id": 342, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(cnpg_pg_stat_database_xact_commit{namespace=~\"$namespace\",pod=~\"$instances\"}[$__interval])) + sum(rate(cnpg_pg_stat_database_xact_rollback{namespace=~\"$namespace\",pod=~\"$instances\"}[$__interval]))", + "interval": "", + "legendFormat": "TPS", + "range": true, + "refId": "TPS" + } + ], + "title": "TPS", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "CPU Utilisation from Requests", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 0.8 + }, + { + "color": "red", + "value": 0.9 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 8, + "y": 3 + }, + "id": 344, + "interval": "1m", + "links": [], + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{ namespace=\"$namespace\"}) / sum(kube_pod_container_resource_requests{job=\"kube-state-metrics\", namespace=\"$namespace\", resource=\"cpu\"})", + "format": "time_series", + "instant": true, + "intervalFactor": 2, + "refId": "A" + } + ], + "title": "CPU Utilisation", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Memory Utilisation from Requests", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 0.8 + }, + { + "color": "red", + "value": 0.9 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 11, + "y": 3 + }, + "id": 348, + "interval": "1m", + "links": [], + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(container_memory_working_set_bytes{job=\"kubelet\", metrics_path=\"/metrics/cadvisor\", namespace=\"$namespace\",container!=\"\", image!=\"\"}) / sum(max by(pod) (kube_pod_container_resource_requests{job=\"kube-state-metrics\", namespace=\"$namespace\", resource=\"memory\"}))", + "format": "time_series", + "instant": true, + "intervalFactor": 2, + "refId": "A" + } + ], + "title": "Memory Utilisation", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 30, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 1 + }, + { + "color": "orange", + "value": 10 + }, + { + "color": "red", + "value": 20 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 14, + "y": 3 + }, + "id": 465, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(cnpg_pg_replication_lag{namespace=~\"$namespace\",pod=~\"$instances\"})", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Replication Lag", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 1 + }, + { + "color": "orange", + "value": 10 + }, + { + "color": "red", + "value": 20 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 16, + "y": 3 + }, + "id": 467, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(cnpg_pg_stat_replication_write_lag_seconds{namespace=~\"$namespace\",pod=~\"$instances\"})", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Write Lag", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 60000000000 + }, + { + "color": "red", + "value": 80000000000 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 18, + "y": 3 + }, + "id": 358, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "cnpg_pg_database_size_bytes{namespace=\"$namespace\"}", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Database Size", + "transformations": [ + { + "id": "groupBy", + "options": { + "fields": { + "Value": { + "aggregations": [ + "max" + ], + "operation": "aggregate" + }, + "datname": { + "aggregations": [], + "operation": "groupby" + } + } + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Elapsed time since the last successful base backup.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "from": 1, + "result": { + "color": "semi-dark-orange", + "index": 0, + "text": "Invalid date" + }, + "to": 1e+42 + }, + "type": "range" + }, + { + "options": { + "from": -2147483648, + "result": { + "color": "red", + "index": 1, + "text": "N/A" + }, + "to": -1577847600 + }, + "type": "range" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "semi-dark-red", + "value": -108000 + }, + { + "color": "semi-dark-orange", + "value": -107999 + }, + { + "color": "#EAB839", + "value": -89999 + }, + { + "color": "green", + "value": -86399 + } + ] + }, + "unit": "dtdurations" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 21, + "y": 3 + }, + "id": 360, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "-(time() - max(cnpg_collector_last_available_backup_timestamp{namespace=\"$namespace\",pod=~\"$instances\"}))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Last Base Backup", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 0.8 + }, + { + "color": "red", + "value": 0.9 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 18, + "y": 5 + }, + "id": 356, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"} / kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"}))", + "format": "time_series", + "interval": "", + "legendFormat": "DATA", + "range": true, + "refId": "DATA" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"} / kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"}))", + "format": "time_series", + "interval": "", + "legendFormat": "WAL", + "range": true, + "refId": "WAL" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(\n sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-tbs.*\"}) \n /\n sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-tbs.*\"}) \n *\n on(namespace, persistentvolumeclaim) group_left(volume)\n kube_pod_spec_volumes_persistentvolumeclaims_info{pod=~\"$instances\"}\n)", + "hide": false, + "instant": false, + "legendFormat": "Tablespaces (max)", + "range": true, + "refId": "Max Tablespace" + } + ], + "title": "Volume Space Usage", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Computes the time since the last known WAL archival in the primary.\nWe ensure to ignore the metric in the replicas by using (1 - cnpg_pg_replication_in_recovery ) as a multiplicative factor. It will be 0 for replicas, 1 for the primary.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "color": "red", + "index": 0, + "text": "No backups" + } + }, + "type": "special" + }, + { + "options": { + "from": -1e+22, + "result": { + "color": "text", + "index": 1, + "text": "No data" + }, + "to": 0 + }, + "type": "range" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dtdurations" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 21, + "y": 5 + }, + "id": 362, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "max((1 - cnpg_pg_replication_in_recovery{namespace=~\"$namespace\",pod=~\"$instances\"}) * (time() - timestamp(cnpg_pg_stat_archiver_seconds_since_last_archival{namespace=~\"$namespace\",pod=~\"$instances\"}) +\ncnpg_pg_stat_archiver_seconds_since_last_archival{namespace=~\"$namespace\",pod=~\"$instances\"}))", + "format": "time_series", + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Last archived WAL", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-blue", + "value": null + } + ] + }, + "unit": "string" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 3, + "y": 6 + }, + "id": 340, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^full$/", + "values": false + }, + "text": {}, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "cnpg_collector_postgres_version{namespace=~\"$namespace\",pod=~\"$instances\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{pod}}", + "range": false, + "refId": "A" + } + ], + "title": "Version", + "transformations": [], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 1 + }, + { + "color": "orange", + "value": 10 + }, + { + "color": "red", + "value": 20 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 14, + "y": 6 + }, + "id": 466, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(cnpg_pg_stat_replication_flush_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"})", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Flush Lag", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 1 + }, + { + "color": "orange", + "value": 10 + }, + { + "color": "red", + "value": 20 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 16, + "y": 6 + }, + "id": 468, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(cnpg_pg_stat_replication_replay_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"})", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Replay Lag", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 80000000000 + }, + { + "color": "red", + "value": 90000000000 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 8, + "y": 7 + }, + "id": 346, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{ namespace=\"$namespace\"})", + "hide": false, + "interval": "", + "legendFormat": "Total", + "range": true, + "refId": "B" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Excluding cache", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 80000000000 + }, + { + "color": "red", + "value": 90000000000 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 11, + "y": 7 + }, + "id": 350, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(container_memory_working_set_bytes{pod=~\"$instances\", namespace=\"$namespace\", container!=\"\", image!=\"\"})", + "hide": false, + "interval": "", + "legendFormat": "Total", + "range": true, + "refId": "B" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "N/A" + } + }, + "type": "value" + }, + { + "options": { + "match": "null", + "result": { + "color": "red", + "index": 0, + "text": "No backups" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dateTimeAsIso" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 21, + "y": 7 + }, + "id": 364, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "max(cnpg_collector_first_recoverability_point{namespace=~\"$namespace\",pod=~\"$instances\"})*1000", + "format": "time_series", + "interval": "", + "legendFormat": "{{pod}}", + "range": true, + "refId": "A" + } + ], + "title": "First Recoverability Point", + "type": "stat" + }, + { + "collapsed": true, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 12, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 0, + "y": 20 + }, + "id": 191, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Instance", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 2, + "x": 3, + "y": 20 + }, + "id": 192, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Status", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 5, + "y": 20 + }, + "id": 193, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Clustering / replicas", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 2, + "x": 8, + "y": 20 + }, + "id": 384, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Zone", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 4, + "x": 10, + "y": 20 + }, + "id": 195, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Connections", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 14, + "y": 20 + }, + "id": 196, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Max Connections", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "gridPos": { + "h": 1, + "w": 3, + "x": 17, + "y": 20 + }, + "id": 197, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Wraparound", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 2, + "x": 20, + "y": 20 + }, + "id": 313, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Started", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 2, + "x": 22, + "y": 20 + }, + "id": 198, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Version", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 0, + "y": 21 + }, + "id": 61, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "\n \n

$instances

\n
", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "Down" + }, + "1": { + "index": 1, + "text": "Up" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red" + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 3, + "y": 21 + }, + "id": 33, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "min(kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"})", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "No" + }, + "1": { + "color": "green", + "index": 0, + "text": "Yes" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 5, + "y": 21 + }, + "id": 60, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "1 - cnpg_pg_replication_in_recovery{namespace=~\"$namespace\",pod=~\"$instances\"} + cnpg_pg_replication_is_wal_receiver_up{namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "transformations": [], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 1, + "x": 7, + "y": 21 + }, + "id": 229, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_replication_streaming_replicas{namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "transformations": [], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "This metric depends on exporting the: `topology.kubernetes.io/zone` label through kube-state-metrics (not enabled by default). Can be added by changing its configuration with:\n\n```yaml\nmetricLabelsAllowlist:\n - nodes=[topology.kubernetes.io/zone]\n```", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 8, + "y": 21 + }, + "id": 386, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^label_topology_kubernetes_io_zone$/", + "values": false + }, + "text": { + "valueSize": 18 + }, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "kube_pod_info{namespace=~\"$namespace\",pod=~\"$instances\"} * on(node,instance) group_left(label_topology_kubernetes_io_zone) kube_node_labels", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "transformations": [], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 10, + "y": 21 + }, + "id": 58, + "options": { + "legend": { + "calcs": [ + "last", + "mean" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "8.2.1", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum by (pod) (cnpg_backends_total{namespace=~\"$namespace\",pod=~\"$instances\"})", + "instant": false, + "interval": "", + "legendFormat": "-", + "refId": "A" + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "mappings": [], + "max": 100, + "min": 0, + "noValue": "<1%", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "#EAB839", + "value": 75 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 14, + "y": 21 + }, + "id": 32, + "options": { + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "100 * sum by (pod) (cnpg_backends_total{namespace=~\"$namespace\",pod=~\"$instances\"}) / sum by (pod) (cnpg_pg_settings_setting{name=\"max_connections\",namespace=~\"$namespace\",pod=~\"$instances\"})", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 2147483647, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "#EAB839", + "value": 200000000 + }, + { + "color": "red", + "value": 1000000000 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 17, + "y": 21 + }, + "id": 8, + "options": { + "displayMode": "lcd", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "text": {}, + "valueMode": "color" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "max by (pod) (cnpg_pg_database_xid_age{namespace=~\"$namespace\",pod=~\"$instances\"})", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "bargauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-blue" + } + ] + }, + "unit": "dateTimeFromNow" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 20, + "y": 21 + }, + "id": 314, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "cnpg_pg_postmaster_start_time{namespace=~\"$namespace\",pod=~\"$instances\"}*1000", + "format": "time_series", + "hide": false, + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "transformations": [], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-blue" + } + ] + }, + "unit": "string" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 22, + "y": 21 + }, + "id": 42, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^full$/", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "cnpg_collector_postgres_version{namespace=~\"$namespace\",pod=~\"$instances\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "transformations": [], + "type": "stat" + } + ], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Server Health", + "type": "row" + }, + { + "collapsed": true, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 41, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 0, + "y": 21 + }, + "id": 187, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Instance", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 3, + "y": 21 + }, + "id": 183, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Max Connections", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 6, + "y": 21 + }, + "id": 184, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Shared Buffers", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 9, + "y": 21 + }, + "id": 185, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Effective Cache Size", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 12, + "y": 21 + }, + "id": 186, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Work Mem", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 15, + "y": 21 + }, + "id": 188, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Maintenance Work Mem", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 18, + "y": 21 + }, + "id": 189, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Random Page Cost", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 3, + "x": 21, + "y": 21 + }, + "id": 190, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Sequential Page Cost", + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 0, + "y": 22 + }, + "id": 86, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "\n \n

$instances

\n
", + "mode": "html" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-purple" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 3, + "y": 22 + }, + "id": 30, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_settings_setting{name=\"max_connections\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-purple" + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 6, + "y": 22 + }, + "id": 24, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "max by (pod) (cnpg_pg_settings_setting{name=\"shared_buffers\",namespace=~\"$namespace\",pod=~\"$instances\"}) * max by (pod) (cnpg_pg_settings_setting{name=\"block_size\",namespace=~\"$namespace\",pod=~\"$instances\"})", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-purple" + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 22 + }, + "id": 57, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "max by (pod) (cnpg_pg_settings_setting{name=\"effective_cache_size\",namespace=~\"$namespace\",pod=~\"$instances\"}) * max by (pod) (cnpg_pg_settings_setting{name=\"block_size\",namespace=~\"$namespace\",pod=~\"$instances\"})", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-purple" + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 22 + }, + "id": 26, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_settings_setting{name=\"work_mem\",namespace=~\"$namespace\",pod=~\"$instances\"} * 1024", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-purple" + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 15, + "y": 22 + }, + "id": 47, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_settings_setting{name=\"maintenance_work_mem\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-purple" + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 18, + "y": 22 + }, + "id": 48, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_settings_setting{name=\"random_page_cost\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-purple" + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 21, + "y": 22 + }, + "id": 56, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "10.1.5", + "repeat": "instances", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_settings_setting{name=\"seq_page_cost\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-purple" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 31 + }, + "id": 150, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.1.5", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_settings_setting{namespace=~\"$namespace\",pod=~\"$instances\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Configurations", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "__name__": true, + "container": true, + "endpoint": true, + "instance": true, + "job": true, + "name": false, + "namespace": true, + "pod": false + }, + "indexByName": { + "Time": 0, + "Value": 9, + "__name__": 1, + "container": 2, + "endpoint": 3, + "instance": 4, + "job": 5, + "name": 7, + "namespace": 8, + "pod": 6 + }, + "renameByName": { + "__name__": "", + "name": "parameter" + } + } + }, + { + "id": "groupingToMatrix", + "options": { + "columnField": "pod", + "rowField": "parameter", + "valueField": "Value" + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "parameter\\pod": "parameter" + } + } + } + ], + "type": "table" + } + ], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Configuration", + "type": "row" + }, + { + "collapsed": true, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 10, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 47 + }, + "hiddenSeries": false, + "id": 273, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "9.4.7", + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{pod=~\"$instances\", namespace=~\"$namespace\"}) by (pod)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A", + "step": 10 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{pod=~\"$instances\", namespace=~\"$namespace\"})", + "hide": false, + "interval": "", + "legendFormat": "total", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:189", + "format": "short", + "logBase": 1, + "min": 0, + "show": true + }, + { + "$$hashKey": "object:190", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 2, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 47 + }, + "hiddenSeries": false, + "id": 275, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "9.4.7", + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "quota - requests", + "color": "#F2495C", + "dashes": true, + "fill": 0, + "hiddenSeries": true, + "hideTooltip": true, + "legend": true, + "linewidth": 2, + "stack": false + }, + { + "alias": "quota - limits", + "color": "#FF9830", + "dashes": true, + "fill": 0, + "hiddenSeries": true, + "hideTooltip": true, + "legend": true, + "linewidth": 2, + "stack": false + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(container_memory_working_set_bytes{pod=~\"$instances\", namespace=\"$namespace\", container!=\"\", image!=\"\"}) by (pod)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A", + "step": 10 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(container_memory_working_set_bytes{pod=~\"$instances\", namespace=\"$namespace\", container!=\"\", image!=\"\"})", + "hide": false, + "interval": "", + "legendFormat": "total", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Memory Usage (w/o cache)", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:246", + "format": "bytes", + "logBase": 1, + "min": 0, + "show": true + }, + { + "$$hashKey": "object:247", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 54 + }, + "id": 39, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(cnpg_backends_total{namespace=~\"$namespace\",pod=~\"$instances\"}) by (pod)", + "hide": false, + "interval": "", + "legendFormat": "total ({{pod}})", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(cnpg_backends_total{namespace=~\"$namespace\",pod=~\"$instances\"}) by (state, pod)", + "interval": "", + "legendFormat": "{{state}} ({{pod}})", + "refId": "A" + } + ], + "title": "Session States", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 62 + }, + "id": 50, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(rate(cnpg_pg_stat_database_xact_commit{namespace=~\"$namespace\",pod=~\"$instances\"}[5m])) by (pod)", + "interval": "", + "legendFormat": "committed ({{pod}})", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(rate(cnpg_pg_stat_database_xact_rollback{namespace=~\"$namespace\",pod=~\"$instances\"}[5m])) by (pod)", + "hide": false, + "interval": "", + "legendFormat": "rolled back ({{pod}})", + "refId": "B" + } + ], + "title": "Transactions [5m]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 62 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "max by (pod) (cnpg_backends_max_tx_duration_seconds{namespace=~\"$namespace\",pod=~\"$instances\"})", + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Longest Transaction", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 70 + }, + "id": 55, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "rate(cnpg_pg_stat_database_deadlocks{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "count ({{pod}})", + "refId": "B" + } + ], + "title": "Deadlocks [5m]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 70 + }, + "id": 54, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_backends_waiting_total{namespace=~\"$namespace\",pod=~\"$instances\"}", + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Blocked Queries", + "type": "timeseries" + } + ], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Operational Stats", + "type": "row" + }, + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 35, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Storage & I/O", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.7 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 13 + }, + "id": 424, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"} / kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"})", + "format": "time_series", + "interval": "", + "legendFormat": "{{persistentvolumeclaim}}", + "range": true, + "refId": "FREE_SPACE" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"} / kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"})", + "format": "time_series", + "interval": "", + "legendFormat": "{{persistentvolumeclaim}}", + "range": true, + "refId": "FREE_SPACE_WAL" + } + ], + "title": "Volume Space Usage: PGDATA and WAL", + "transformations": [], + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.8 + }, + { + "color": "red", + "value": 0.9 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 13 + }, + "id": 426, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.2.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max by(persistentvolumeclaim) (kubelet_volume_stats_inodes_used{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"} / kubelet_volume_stats_inodes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"})", + "format": "time_series", + "interval": "", + "legendFormat": "{{persistentvolumeclaim}}", + "range": true, + "refId": "FREE_INODES" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max by(persistentvolumeclaim) (kubelet_volume_stats_inodes_used{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"} / kubelet_volume_stats_inodes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"})", + "format": "time_series", + "interval": "", + "legendFormat": "{{persistentvolumeclaim}}", + "range": true, + "refId": "FREE_INODES_WAL" + } + ], + "title": "Volume Inode Usage: PGDATA and WAL", + "transformations": [], + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "#EAB839", + "value": 0.7 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 21 + }, + "id": 564, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "10.1.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-tbs.*\"}) \n/\nsum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-tbs.*\"}) \n*\non(namespace, persistentvolumeclaim) group_left(volume,pod)\nkube_pod_spec_volumes_persistentvolumeclaims_info{pod=~\"$instances\"}", + "format": "time_series", + "interval": "", + "legendFormat": "{{volume}}-{{pod}}", + "range": true, + "refId": "FREE_SPACE" + } + ], + "title": "Volume Space Usage: Tablespaces", + "transformations": [], + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 28 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(cnpg_pg_stat_database_tup_deleted{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", + "interval": "", + "legendFormat": "deleted", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(cnpg_pg_stat_database_tup_inserted{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", + "hide": false, + "interval": "", + "legendFormat": "inserted", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(cnpg_pg_stat_database_tup_fetched{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", + "hide": false, + "interval": "", + "legendFormat": "fetched", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(cnpg_pg_stat_database_tup_returned{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", + "hide": false, + "interval": "", + "legendFormat": "returned", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(rate(cnpg_pg_stat_database_tup_updated{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", + "hide": false, + "interval": "", + "legendFormat": "updated", + "range": true, + "refId": "E" + } + ], + "title": "Tuple I/O [5m]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 28 + }, + "id": 46, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "rate(cnpg_pg_stat_database_blks_hit{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", + "interval": "", + "legendFormat": "hit ({{pod}})", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "rate(cnpg_pg_stat_database_blks_read{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", + "hide": false, + "interval": "", + "legendFormat": "read ({{pod}})", + "range": true, + "refId": "B" + } + ], + "title": "Block I/O [5m]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 36 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "8.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "max by (datname) (cnpg_pg_database_size_bytes{datname!~\"template.*\",datname!=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"})", + "interval": "", + "legendFormat": " {{pod}}: {{datname}}", + "range": true, + "refId": "A" + } + ], + "title": "Database Size", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 36 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "rate(cnpg_pg_stat_database_temp_bytes{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", + "instant": false, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Temp Bytes [5m]", + "type": "timeseries" + }, + { + "collapsed": true, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 44 + }, + "id": 37, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 57 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_collector_pg_wal_archive_status{value=\"ready\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "interval": "", + "legendFormat": "ready ({{pod}})", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_collector_pg_wal_archive_status{value=\"done\",namespace=~\"$namespace\",pod=~\"$instances\"}", + "hide": false, + "interval": "", + "legendFormat": "done ({{pod}})", + "refId": "B" + } + ], + "title": "WAL Segment Archive Status", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 57 + }, + "id": 52, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "rate(cnpg_pg_stat_archiver_archived_count{namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", + "interval": "", + "legendFormat": "archived ({{pod}})", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "rate(cnpg_pg_stat_archiver_failed_count{namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", + "hide": false, + "interval": "", + "legendFormat": "failed ({{pod}})", + "refId": "B" + } + ], + "title": "Archiver Status [5m]", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 57 + }, + "id": 53, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_stat_archiver_seconds_since_last_archival{namespace=~\"$namespace\",pod=~\"$instances\"}", + "interval": "", + "legendFormat": "age ({{pod}})", + "refId": "A" + } + ], + "title": "Last Archive Age", + "type": "timeseries" + } + ], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Write Ahead Log", + "type": "row" + }, + { + "collapsed": true, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 45 + }, + "id": 18, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "#EAB839", + "value": 600 + }, + { + "color": "dark-red", + "value": 3600 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 21 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_replication_lag{namespace=~\"$namespace\",pod=~\"$instances\"}", + "instant": false, + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Replication Lag", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 21 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_stat_replication_write_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", + "instant": false, + "interval": "", + "legendFormat": "{{pod}} -> {{application_name}}", + "refId": "A" + } + ], + "title": "Write Lag", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 21 + }, + "id": 59, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_stat_replication_flush_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", + "instant": false, + "interval": "", + "legendFormat": "{{pod}} -> {{application_name}}", + "refId": "A" + } + ], + "title": "Flush Lag", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 21 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_stat_replication_replay_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", + "interval": "", + "legendFormat": "{{pod}} -> {{application_name}}", + "refId": "A" + } + ], + "title": "Replay Lag", + "type": "timeseries" + } + ], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Replication", + "type": "row" + }, + { + "collapsed": true, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 46 + }, + "id": 231, + "panels": [ + { + "cards": {}, + "color": { + "cardColor": "#b4ff00", + "colorScale": "sqrt", + "colorScheme": "interpolateOranges", + "exponent": 0.5, + "mode": "spectrum" + }, + "dataFormat": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 59 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 233, + "legend": { + "show": false + }, + "options": { + "calculate": true, + "calculation": {}, + "cellGap": 2, + "cellValues": {}, + "color": { + "exponent": 0.5, + "fill": "#b4ff00", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 128 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": false + }, + "rowsFrame": { + "layout": "auto" + }, + "showValue": "never", + "tooltip": { + "show": true, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false, + "unit": "s" + } + }, + "pluginVersion": "9.4.7", + "reverseYBuckets": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_collector_collection_duration_seconds{namespace=~\"$namespace\",pod=~\"$instances\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Collection Duration", + "tooltip": { + "show": true, + "showHistogram": false + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "yAxis": { + "format": "s", + "logBase": 1, + "show": true + }, + "yBucketBound": "auto" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 59 + }, + "id": 235, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_collector_last_collection_error{namespace=~\"$namespace\",pod=~\"$instances\"}", + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "Errors", + "type": "timeseries" + } + ], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Collector Stats", + "type": "row" + }, + { + "collapsed": true, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 47 + }, + "id": 239, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dateTimeAsIso" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 60 + }, + "id": 237, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_collector_first_recoverability_point{namespace=~\"$namespace\",pod=~\"$instances\"}*1000 > 0", + "format": "time_series", + "interval": "", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "title": "First Recoverability Point", + "type": "timeseries" + } + ], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Backups", + "type": "row" + }, + { + "collapsed": true, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 48 + }, + "id": 293, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": -1, + "drawStyle": "line", + "fillOpacity": 8, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 40 + }, + "id": 295, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "8.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_stat_bgwriter_checkpoints_req{namespace=~\"$namespace\",pod=~\"$instances\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "req/{{pod}}", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_stat_bgwriter_checkpoints_timed{namespace=~\"$namespace\",pod=~\"$instances\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "timed/{{pod}}", + "refId": "A" + } + ], + "title": "Requested/Timed", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": -1, + "drawStyle": "line", + "fillOpacity": 8, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 40 + }, + "id": 296, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "8.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_stat_bgwriter_checkpoint_write_time{namespace=~\"$namespace\",pod=~\"$instances\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "write/{{pod}}", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "cnpg_pg_stat_bgwriter_checkpoint_sync_time{namespace=~\"$namespace\",pod=~\"$instances\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "sync/{{pod}}", + "refId": "A" + } + ], + "title": "Write/Sync time", + "type": "timeseries" + } + ], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Checkpoints", + "type": "row" + } + ], + "refresh": "30s", + "revision": 1, + "schemaVersion": 38, + "tags": [ + "cloudnativepg" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "isNone": true, + "selected": false, + "text": "None", + "value": "" + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "cnpg_collector_up", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "namespace", + "options": [], + "query": { + "query": "cnpg_collector_up", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "/namespace=\"(?[^\"]+)/g", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "isNone": true, + "selected": false, + "text": "None", + "value": "" + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "cnpg_collector_up{namespace=~\"$namespace\"}", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "cluster", + "options": [], + "query": { + "query": "cnpg_collector_up{namespace=~\"$namespace\"}", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "/\\bcluster\\b=\"(?[^\"]+)/g", + "skipUrlSync": false, + "sort": 1, + "type": "query" + }, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "cnpg_collector_up{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "instances", + "options": [], + "query": { + "query": "cnpg_collector_up{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "/pod=\"(?[^\"]+)/g", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "nowDelay": "" + }, + "timezone": "", + "title": "CloudNativePG", + "uid": "cloudnative-pg", + "version": 1, + "weekStart": "" +} diff --git a/charts/cloudnative-pg/templates/grafana-dashboard.yaml b/charts/cloudnative-pg/templates/grafana-dashboard.yaml new file mode 100644 index 000000000..772530037 --- /dev/null +++ b/charts/cloudnative-pg/templates/grafana-dashboard.yaml @@ -0,0 +1,12 @@ +{{- if .Values.monitoring.grafanaDashboard.create -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.monitoring.grafanaDashboard.configMapName }} + namespace: {{ default .Release.Namespace .Values.monitoring.grafanaDashboard.namespace }} + labels: + {{ .Values.monitoring.grafanaDashboard.sidecarLabel }}: {{ .Values.monitoring.grafanaDashboard.sidecarLabelValue | quote }} +data: + cnp.json: |- +{{ .Files.Get "monitoring/grafana-dashboard.json" | indent 6 }} +{{- end -}} diff --git a/charts/cloudnative-pg/values.schema.json b/charts/cloudnative-pg/values.schema.json index b5a5b4622..24d314406 100644 --- a/charts/cloudnative-pg/values.schema.json +++ b/charts/cloudnative-pg/values.schema.json @@ -95,6 +95,26 @@ "monitoring": { "type": "object", "properties": { + "grafanaDashboard": { + "type": "object", + "properties": { + "configMapName": { + "type": "string" + }, + "create": { + "type": "boolean" + }, + "namespace": { + "type": "string" + }, + "sidecarLabel": { + "type": "string" + }, + "sidecarLabelValue": { + "type": "string" + } + } + }, "podMonitorEnabled": { "type": "boolean" } diff --git a/charts/cloudnative-pg/values.yaml b/charts/cloudnative-pg/values.yaml index 8849fad60..ce32e97d1 100644 --- a/charts/cloudnative-pg/values.yaml +++ b/charts/cloudnative-pg/values.yaml @@ -139,6 +139,16 @@ affinity: {} monitoring: # -- Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. podMonitorEnabled: false + grafanaDashboard: + create: false + # -- Allows overriding the namespace where the ConfigMap will be created, defaulting to the same one as the Release. + namespace: "" + # -- The name of the ConfigMap containing the dashboard. + configMapName: "cnpg-grafana-dashboard" + # -- Label that ConfigMaps should have to be loaded as dashboards. + sidecarLabel: "grafana_dashboard" + # -- Label value that ConfigMaps should have to be loaded as dashboards. + sidecarLabelValue: "" # Default monitoring queries monitoringQueriesConfigMap: From 1ead2b26555f238904e0bbded5d7ca14e09aa4bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:13:55 +0100 Subject: [PATCH 14/87] Release cloudnative-pg-v0.20.1 (#182) Signed-off-by: Jaime Silvela Co-authored-by: Jaime Silvela --- charts/cloudnative-pg/Chart.yaml | 2 +- charts/cloudnative-pg/README.md | 2 +- .../cloudnative-pg/templates/crds/crds.yaml | 254 ++++++++++++++++++ 3 files changed, 256 insertions(+), 2 deletions(-) diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index 27eaf026f..4181d541f 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -23,7 +23,7 @@ version: "0.20.1" # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.22.0" +appVersion: "1.22.1" sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 3c58872d0..81d189ed9 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.20.1](https://img.shields.io/badge/Version-0.20.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.22.0](https://img.shields.io/badge/AppVersion-1.22.0-informational?style=flat-square) +![Version: 0.20.1](https://img.shields.io/badge/Version-0.20.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.22.1](https://img.shields.io/badge/AppVersion-1.22.1-informational?style=flat-square) CloudNativePG Helm Chart diff --git a/charts/cloudnative-pg/templates/crds/crds.yaml b/charts/cloudnative-pg/templates/crds/crds.yaml index 95492809a..f040a3805 100644 --- a/charts/cloudnative-pg/templates/crds/crds.yaml +++ b/charts/cloudnative-pg/templates/crds/crds.yaml @@ -2313,6 +2313,254 @@ spec: x-kubernetes-map-type: atomic type: object type: array + ephemeralVolumeSource: + description: EphemeralVolumeSource allows the user to configure the + source of ephemeral volumes. + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC to provision + the volume. The pod in which this EphemeralVolumeSource is embedded + will be the owner of the PVC, i.e. the PVC will be deleted together + with the pod. The name of the PVC will be `-` where `` is the name from the `PodSpec.Volumes` + array entry. Pod validation will reject the pod if the concatenated + name is not valid for a PVC (for example, too long). \n An existing + PVC with that name that is not owned by the pod will *not* be + used for the pod to avoid using an unrelated volume by mistake. + Starting the pod is then blocked until the unrelated PVC is + removed. If such a pre-created PVC is meant to be used by the + pod, the PVC has to updated with an owner reference to the pod + once the pod exists. Normally this should not be necessary, + but it may be useful when manually reconstructing a broken cluster. + \n This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. \n Required, must not + be nil." + properties: + metadata: + description: May contain labels and annotations that will + be copied into the PVC when creating it. No other fields + are allowed and will be rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the PVC that + gets created from this template. The same fields as in a + PersistentVolumeClaim are also valid here. + properties: + accessModes: + description: 'accessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) If the provisioner + or an external controller can support the specified + data source, it will create a new volume based on the + contents of the specified data source. When the AnyVolumeDataSource + feature gate is enabled, dataSource contents will be + copied to dataSourceRef, and dataSourceRef contents + will be copied to dataSource when dataSourceRef.namespace + is not specified. If the namespace is specified, then + dataSourceRef will not be copied to dataSource.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: 'dataSourceRef specifies the object from + which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty + API group (non core object) or a PersistentVolumeClaim + object. When this field is specified, volume binding + will only succeed if the type of the specified object + matches some installed volume populator or dynamic provisioner. + This field will replace the functionality of the dataSource + field and as such if both fields are non-empty, they + must have the same value. For backwards compatibility, + when namespace isn''t specified in dataSourceRef, both + fields (dataSource and dataSourceRef) will be set to + the same value automatically if one of them is empty + and the other is non-empty. When namespace is specified + in dataSourceRef, dataSource isn''t set to the same + value and must be empty. There are three important differences + between dataSource and dataSourceRef: * While dataSource + only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim + objects. * While dataSource ignores disallowed values + (dropping them), dataSourceRef preserves all values, + and generates an error if a disallowed value is specified. + * While dataSource only allows local objects, dataSourceRef + allows objects in any namespaces. (Beta) Using this + field requires the AnyVolumeDataSource feature gate + to be enabled. (Alpha) Using the namespace field of + dataSourceRef requires the CrossNamespaceVolumeDataSource + feature gate to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace is specified, + a gateway.networking.k8s.io/ReferenceGrant object + is required in the referent namespace to allow that + namespace's owner to accept the reference. See the + ReferenceGrant documentation for details. (Alpha) + This field requires the CrossNamespaceVolumeDataSource + feature gate to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: 'resources represents the minimum resources + the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify resource + requirements that are lower than previous value but + must still be higher than capacity recorded in the status + field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + claims: + description: "Claims lists the names of resources, + defined in spec.resourceClaims, that are used by + this container. \n This is an alpha field and requires + enabling the DynamicResourceAllocation feature gate. + \n This field is immutable. It can only be set for + containers." + items: + description: ResourceClaim references one entry + in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one + entry in pod.spec.resourceClaims of the Pod + where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is omitted + for a container, it defaults to Limits if that is + explicitly specified, otherwise to an implementation-defined + value. Requests cannot exceed Limits. More info: + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: selector is a label query over volumes to + consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: 'storageClassName is the name of the StorageClass + required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume is + required by the claim. Value of Filesystem is implied + when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference to the + PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object ephemeralVolumesSizeLimit: description: EphemeralVolumesSizeLimit allows the user to set the limits for the ephemeral volumes @@ -3206,6 +3454,12 @@ spec: items: type: string type: array + pg_ident: + description: PostgreSQL User Name Maps rules (lines to be appended + to the pg_ident.conf file) + items: + type: string + type: array promotionTimeout: description: Specifies the maximum number of seconds to wait when promoting an instance to primary. Default value is 40000000, From c939e3e114ae4c3a5b7190577aeae6c625ef8d48 Mon Sep 17 00:00:00 2001 From: Jonathan Gonzalez V Date: Fri, 16 Feb 2024 21:58:50 +0100 Subject: [PATCH 15/87] Cluster Chart v0.0.1-alpha (#179) CNPG Cluster Chart v0.0.1 Initial prototype of the cluster chart that focuses in ease-of-use and a simple disaster recovery procedure. The objective of the chart is to provide a really easy to use interface for cluster recovery and a convention over configuration approach. Closes: #178 Signed-off-by: Itay Grudev itay.grudev@essentim.com --- .github/actions/deploy-operator/action.yml | 10 + .github/actions/setup-kind/action.yml | 24 ++ .../actions/verify-ready-instances/action.yml | 32 +++ .github/workflows/continuous-delivery.yml | 60 ----- .../workflows/tests-cluster-standalone.yml | 36 +++ .github/workflows/tests-operator.yml | 41 ++++ CODEOWNERS | 4 +- README.md | 33 +-- charts/cloudnative-pg/Chart.yaml | 4 +- charts/cloudnative-pg/README.md | 2 +- charts/cluster/.gitignore | 1 + charts/cluster/.helmignore | 23 ++ charts/cluster/Chart.yaml | 31 +++ charts/cluster/README.md | 213 +++++++++++++++++ charts/cluster/README.md.gotmpl | 147 ++++++++++++ charts/cluster/docs/Getting Started.md | 106 +++++++++ charts/cluster/docs/Recovery.md | 27 +++ charts/cluster/examples/basic.yaml | 5 + charts/cluster/examples/custom-queries.yaml | 23 ++ charts/cluster/examples/postgis.yaml | 6 + charts/cluster/examples/recovery-backup.yaml | 22 ++ .../examples/recovery-object_store.yaml | 30 +++ charts/cluster/examples/standalone-s3.yaml | 19 ++ charts/cluster/templates/NOTES.txt | 68 ++++++ charts/cluster/templates/_backup.tpl | 18 ++ .../templates/_barman_object_store.tpl | 58 +++++ charts/cluster/templates/_bootstrap.tpl | 46 ++++ charts/cluster/templates/_colorize.tpl | 12 + charts/cluster/templates/_helpers.tpl | 69 ++++++ .../cluster/templates/backup-azure-creds.yaml | 11 + .../backup-azure-recovery-creds.yaml | 11 + .../templates/backup-google-creds.yaml | 8 + .../backup-google-recovery-creds.yaml | 8 + charts/cluster/templates/backup-s3-creds.yaml | 9 + .../templates/backup-s3-recovery-creds.yaml | 9 + charts/cluster/templates/cluster.yaml | 64 +++++ charts/cluster/templates/pooler.yaml | 15 ++ .../cluster/templates/scheduled-backups.yaml | 17 ++ charts/cluster/templates/tests/ping.yaml | 37 +++ charts/cluster/templates/user-metrics.yaml | 17 ++ charts/cluster/values.yaml | 223 ++++++++++++++++++ 41 files changed, 1516 insertions(+), 83 deletions(-) create mode 100644 .github/actions/deploy-operator/action.yml create mode 100644 .github/actions/setup-kind/action.yml create mode 100644 .github/actions/verify-ready-instances/action.yml delete mode 100644 .github/workflows/continuous-delivery.yml create mode 100644 .github/workflows/tests-cluster-standalone.yml create mode 100644 .github/workflows/tests-operator.yml create mode 100644 charts/cluster/.gitignore create mode 100644 charts/cluster/.helmignore create mode 100644 charts/cluster/Chart.yaml create mode 100644 charts/cluster/README.md create mode 100644 charts/cluster/README.md.gotmpl create mode 100644 charts/cluster/docs/Getting Started.md create mode 100644 charts/cluster/docs/Recovery.md create mode 100644 charts/cluster/examples/basic.yaml create mode 100644 charts/cluster/examples/custom-queries.yaml create mode 100644 charts/cluster/examples/postgis.yaml create mode 100644 charts/cluster/examples/recovery-backup.yaml create mode 100644 charts/cluster/examples/recovery-object_store.yaml create mode 100644 charts/cluster/examples/standalone-s3.yaml create mode 100644 charts/cluster/templates/NOTES.txt create mode 100644 charts/cluster/templates/_backup.tpl create mode 100644 charts/cluster/templates/_barman_object_store.tpl create mode 100644 charts/cluster/templates/_bootstrap.tpl create mode 100644 charts/cluster/templates/_colorize.tpl create mode 100644 charts/cluster/templates/_helpers.tpl create mode 100644 charts/cluster/templates/backup-azure-creds.yaml create mode 100644 charts/cluster/templates/backup-azure-recovery-creds.yaml create mode 100644 charts/cluster/templates/backup-google-creds.yaml create mode 100644 charts/cluster/templates/backup-google-recovery-creds.yaml create mode 100644 charts/cluster/templates/backup-s3-creds.yaml create mode 100644 charts/cluster/templates/backup-s3-recovery-creds.yaml create mode 100644 charts/cluster/templates/cluster.yaml create mode 100644 charts/cluster/templates/pooler.yaml create mode 100644 charts/cluster/templates/scheduled-backups.yaml create mode 100644 charts/cluster/templates/tests/ping.yaml create mode 100644 charts/cluster/templates/user-metrics.yaml create mode 100644 charts/cluster/values.yaml diff --git a/.github/actions/deploy-operator/action.yml b/.github/actions/deploy-operator/action.yml new file mode 100644 index 000000000..a998f06eb --- /dev/null +++ b/.github/actions/deploy-operator/action.yml @@ -0,0 +1,10 @@ +name: Deploy the CNPG Operator +description: Deploys the CNPG Operator to a Kubernetes cluster +runs: + using: composite + steps: + - name: Deploy the operator + shell: bash + run: | + helm upgrade --install cnpg --namespace cnpg-system \ + --create-namespace charts/cloudnative-pg --wait diff --git a/.github/actions/setup-kind/action.yml b/.github/actions/setup-kind/action.yml new file mode 100644 index 000000000..bb88f85b9 --- /dev/null +++ b/.github/actions/setup-kind/action.yml @@ -0,0 +1,24 @@ +name: Setup Kind +description: Sets up a kind cluster and installs Helm and kubectl +outputs: + helm-path: + description: The path to the Helm binary + value: ${{ steps.helm.outputs.helm-path }} + kubectl-path: + description: The path to the kubectl binary + value: ${{ steps.kubectl.outputs.kubectl-path }} +runs: + using: composite + steps: + - id: helm + name: Set up Helm + uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3.5 + with: + version: v3.6.2 + + - id: kubectl + name: Install kubectl + uses: azure/setup-kubectl@901a10e89ea615cf61f57ac05cecdf23e7de06d8 # v3.2 + + - name: Create kind cluster + uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0 diff --git a/.github/actions/verify-ready-instances/action.yml b/.github/actions/verify-ready-instances/action.yml new file mode 100644 index 000000000..7a848e7f3 --- /dev/null +++ b/.github/actions/verify-ready-instances/action.yml @@ -0,0 +1,32 @@ +name: Verifies that a CNPG cluster has a certain amount of ready instances +description: Verifies that a CNPG cluster has a certain amount of ready instances +inputs: + cluster-name: + description: The name of the cluster to verify + required: true + default: database-cluster + ready-instances: + description: The amount of ready instances to wait for + required: true + default: "3" + +runs: + using: composite + steps: + - name: Wait for cluster to become ready + shell: bash + run: | + ITER=0 + while true; do + if [[ $ITER -ge 300 ]]; then + echo "Cluster not ready" + exit 1 + fi + READY_INSTANCES=$(kubectl get cluster ${INPUT_CLUSTER_NAME} -o jsonpath='{.status.readyInstances}') + if [[ "$READY_INSTANCES" == ${INPUT_READY_INSTANCES} ]]; then + echo "Cluster up and running" + break + fi + sleep 1 + (( ++ITER )) + done diff --git a/.github/workflows/continuous-delivery.yml b/.github/workflows/continuous-delivery.yml deleted file mode 100644 index ef39c8e83..000000000 --- a/.github/workflows/continuous-delivery.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: continuous-delivery - -on: - pull_request: - branches-ignore: - - 'gh-pages' - -jobs: - install_deploy: - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - fetch-depth: 0 - - - name: Set up Helm - uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3.5 - with: - version: v3.6.2 - - - name: Create kind cluster - uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0 - - - name: Deploy using helm chart - run: | - helm upgrade --install cnpg --namespace cnpg-system \ - --create-namespace charts/cloudnative-pg --wait - - - name: Install kubectl - uses: azure/setup-kubectl@901a10e89ea615cf61f57ac05cecdf23e7de06d8 # v3.2 - - - name: Deploy a cluster - run: | - cat < diff --git a/charts/cluster/.gitignore b/charts/cluster/.gitignore new file mode 100644 index 000000000..0742ed461 --- /dev/null +++ b/charts/cluster/.gitignore @@ -0,0 +1 @@ +examples/*.test.yaml \ No newline at end of file diff --git a/charts/cluster/.helmignore b/charts/cluster/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/charts/cluster/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/cluster/Chart.yaml b/charts/cluster/Chart.yaml new file mode 100644 index 000000000..9cac5cbc9 --- /dev/null +++ b/charts/cluster/Chart.yaml @@ -0,0 +1,31 @@ +# +# Copyright The CloudNativePG Contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +apiVersion: v2 +name: cluster +description: Deploys and manages a CloudNativePG cluster and its associated resources. +icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg +type: application +version: 0.0.1 +sources: + - https://github.com/cloudnative-pg/charts +keywords: + - postgresql + - postgres + - database +home: https://cloudnative-pg.io +maintainers: + - name: itay-grudev + email: itay+cloudnativepg-charts+github.com@grudev.com diff --git a/charts/cluster/README.md b/charts/cluster/README.md new file mode 100644 index 000000000..d334c4389 --- /dev/null +++ b/charts/cluster/README.md @@ -0,0 +1,213 @@ +# cluster + +![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +> **Warning** +> ### This chart is under active development. +> ### Advised caution when using in production! + +A note on the chart's purpose +----------------------------- + +This is an opinionated chart that is designed to provide a subset of simple, stable and safe configurations using the +CloudNativePG operator. It is designed to provide a simple way to perform recovery operations to decrease your RTO. + +It is not designed to be a one size fits all solution. If you need a more complicated setup we strongly recommend that +you either: + +* use the operator directly +* create your own chart +* use Kustomize to modify the chart's resources + +**_Note_** that the latter option carries it's own risks as the chart configuration may change, especially before it +reaches a stable release. + +That being said, we welcome PRs that improve the chart, but please keep in mind that we don't plan to support every +single configuration that the operator provides and we may reject PRs that add too much complexity and maintenance +difficulty to the chart. + +Getting Started +--------------- + +### Installing the Operator +Skip this step if the CNPG operator is already installed in your cluster. + +```console +helm repo add cnpg https://cloudnative-pg.github.io/charts +helm upgrade --install cnpg \ +--namespace cnpg-system \ +--create-namespace \ +cnpg/cloudnative-pg +``` + +### Setting up a CNPG Cluster + +```console +helm repo add cnpg https://cloudnative-pg.github.io/charts +helm upgrade --install cnpg \ +--namespace cnpg-database \ +--create-namespace \ +--values values.yaml \ +cnpg/cluster +``` + +A more detailed guide can be found here: [Getting Started](docs/Getting Started.md) + +Cluster Configuration +--------------------- + +### Database types + +Currently the chart supports two database types. These are configured via the `type` parameter. These are: +* `postgresql` - A standard PostgreSQL database. +* `postgis` - A PostgreSQL database with the PostGIS extension installed. + +Depending on the type the chart will use a different Docker image and fill in some initial setup, like extension installation. + +### Modes of operation + +The chart has three modes of operation. These are configured via the `mode` parameter: +* `standalone` - Creates new or updates an existing CNPG cluster. This is the default mode. +* `replica` - Creates a replica cluster from an existing CNPG cluster. **_Note_ that this mode is not yet supported.** +* `recovery` - Recovers a CNPG cluster from a backup, object store or via pg_basebackup. + +### Backup configuration + +CNPG implements disaster recovery via [Barman](https://pgbarman.org/). The following section configures the barman object +store where backups will be stored. Barman performs backups of the cluster filesystem base backup and WALs. Both are +stored in the specified location. The backup provider is configured via the `backups.provider` parameter. The following +providers are supported: + +* S3 or S3-compatible stores, like MinIO +* Microsoft Azure Blob Storage +* Google Cloud Storage + +Additionally you can specify the following parameters: +* `backups.retentionPolicy` - The retention policy for backups. Defaults to `30d`. +* `backups.scheduledBackups` - An array of scheduled backups containing a name and a crontab schedule. Example: +```yaml +backups: + scheduledBackups: + - name: daily-backup + schedule: "0 0 0 * * *" # Daily at midnight + backupOwnerReference: self +``` + +Each backup adapter takes it's own set of parameters, listed in the [Configuration options](#Configuration-options) section +below. Refer to the table for the full list of parameters and place the configuration under the appropriate key: `backup.s3`, +`backup.azure`, or `backup.google`. + +Recovery +-------- + +There is a separate document outlining the recovery procedure here: **[Recovery](docs/recovery.md)** + +Examples +-------- + +There are several configuration examples in the [examples](examples) directory. Refer to them for a basic setup and +refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentation/current/) for more advanced configurations. + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| backups.azure.connectionString | string | `""` | | +| backups.azure.containerName | string | `""` | | +| backups.azure.inheritFromAzureAD | bool | `false` | | +| backups.azure.path | string | `"/"` | | +| backups.azure.serviceName | string | `"blob"` | | +| backups.azure.storageAccount | string | `""` | | +| backups.azure.storageKey | string | `""` | | +| backups.azure.storageSasToken | string | `""` | | +| backups.destinationPath | string | `""` | Overrides the provider specific default path. Defaults to: S3: s3:// Azure: https://..core.windows.net/ Google: gs:// | +| backups.enabled | bool | `false` | You need to configure backups manually, so backups are disabled by default. | +| backups.endpointURL | string | `""` | Overrides the provider specific default endpoint. Defaults to: S3: https://s3..amazonaws.com" | +| backups.google.applicationCredentials | string | `""` | | +| backups.google.bucket | string | `""` | | +| backups.google.gkeEnvironment | bool | `false` | | +| backups.google.path | string | `"/"` | | +| backups.provider | string | `"s3"` | One of `s3`, `azure` or `google` | +| backups.retentionPolicy | string | `"30d"` | Retention policy for backups | +| backups.s3.accessKey | string | `""` | | +| backups.s3.bucket | string | `""` | | +| backups.s3.path | string | `"/"` | | +| backups.s3.region | string | `""` | | +| backups.s3.secretKey | string | `""` | | +| backups.scheduledBackups[0].backupOwnerReference | string | `"self"` | Backup owner reference | +| backups.scheduledBackups[0].name | string | `"daily-backup"` | Scheduled backup name | +| backups.scheduledBackups[0].schedule | string | `"0 0 0 * * *"` | Schedule in cron format | +| cluster.additionalLabels | object | `{}` | | +| cluster.affinity | object | `{"topologyKey":"topology.kubernetes.io/zone"}` | Affinity/Anti-affinity rules for Pods See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-AffinityConfiguration | +| cluster.annotations | object | `{}` | | +| cluster.certificates | string | `nil` | The configuration for the CA and related certificates See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-CertificatesConfiguration | +| cluster.enableSuperuserAccess | bool | `true` | When this option is enabled, the operator will use the SuperuserSecret to update the postgres user password. If the secret is not present, the operator will automatically create one. When this option is disabled, the operator will ignore the SuperuserSecret content, delete it when automatically created, and then blank the password of the postgres user by setting it to NULL. | +| cluster.imageName | string | `""` | Name of the container image, supporting both tags (:) and digests for deterministic and repeatable deployments: :@sha256: | +| cluster.imagePullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never or IfNotPresent. If not defined, it defaults to IfNotPresent. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | +| cluster.imagePullSecrets | list | `[]` | The list of pull secrets to be used to pull the images See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-LocalObjectReference | +| cluster.initdb | object | `{}` | BootstrapInitDB is the configuration of the bootstrap process when initdb is used See: https://cloudnative-pg.io/documentation/current/bootstrap/ See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-bootstrapinitdb | +| cluster.instances | int | `3` | Number of instances | +| cluster.logLevel | string | `"info"` | The instances' log level, one of the following values: error, warning, info (default), debug, trace | +| cluster.monitoring.customQueries | list | `[]` | | +| cluster.monitoring.enablePodMonitor | bool | `false` | | +| cluster.postgresql | string | `nil` | Configuration of the PostgreSQL server See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration | +| cluster.primaryUpdateMethod | string | `"switchover"` | Method to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated. It can be switchover (default) or in-place (restart). | +| cluster.primaryUpdateStrategy | string | `"unsupervised"` | Strategy to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated: it can be automated (unsupervised - default) or manual (supervised) | +| cluster.priorityClassName | string | `""` | | +| cluster.resources | string | `nil` | Resources requirements of every generated Pod. Please refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ for more information. We strongly advise you use the same setting for limits and requests so that your cluster pods are given a Guaranteed QoS. See: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ | +| cluster.storage.size | string | `"8Gi"` | | +| cluster.storage.storageClass | string | `""` | | +| cluster.superuserSecret | string | `""` | | +| fullnameOverride | string | `""` | Override the full name of the chart | +| mode | string | `"standalone"` | Cluster mode of operation. Available modes: * `standalone` - default mode. Creates new or updates an existing CNPG cluster. * `replica` - Creates a replica cluster from an existing CNPG cluster. # TODO * `recovery` - Same as standalone but creates a cluster from a backup, object store or via pg_basebackup. | +| nameOverride | string | `""` | Override the name of the chart | +| pooler.enabled | bool | `false` | Whether to enable PgBouncer | +| pooler.instances | int | `3` | Number of PgBouncer instances | +| pooler.parameters | object | `{"default_pool_size":"25","max_client_conn":"1000"}` | PgBouncer configuration parameters | +| pooler.poolMode | string | `"transaction"` | PgBouncer pooling mode | +| recovery.azure.connectionString | string | `""` | | +| recovery.azure.containerName | string | `""` | | +| recovery.azure.inheritFromAzureAD | bool | `false` | | +| recovery.azure.path | string | `"/"` | | +| recovery.azure.serviceName | string | `"blob"` | | +| recovery.azure.storageAccount | string | `""` | | +| recovery.azure.storageKey | string | `""` | | +| recovery.azure.storageSasToken | string | `""` | | +| recovery.backupName | string | `""` | Backup Recovery Method | +| recovery.clusterName | string | `""` | Object Store Recovery Method | +| recovery.destinationPath | string | `""` | Overrides the provider specific default path. Defaults to: S3: s3:// Azure: https://..core.windows.net/ Google: gs:// | +| recovery.endpointURL | string | `""` | Overrides the provider specific default endpoint. Defaults to: S3: https://s3..amazonaws.com" Leave empty if using the default S3 endpoint | +| recovery.google.applicationCredentials | string | `""` | | +| recovery.google.bucket | string | `""` | | +| recovery.google.gkeEnvironment | bool | `false` | | +| recovery.google.path | string | `"/"` | | +| recovery.method | string | `"backup"` | Available recovery methods: * `backup` - Recovers a CNPG cluster from a CNPG backup (PITR supported) Needs to be on the same cluster in the same namespace. * `object_store` - Recovers a CNPG cluster from a barman object store (PITR supported). * `pg_basebackup` - Recovers a CNPG cluster viaa streaming replication protocol. Useful if you want to migrate databases to CloudNativePG, even from outside Kubernetes. # TODO | +| recovery.pitrTarget.time | string | `""` | Time in RFC3339 format | +| recovery.provider | string | `"s3"` | One of `s3`, `azure` or `google` | +| recovery.s3.accessKey | string | `""` | | +| recovery.s3.bucket | string | `""` | | +| recovery.s3.path | string | `"/"` | | +| recovery.s3.region | string | `""` | | +| recovery.s3.secretKey | string | `""` | | +| type | string | `"postgresql"` | Type of the CNPG database. Available types: * `postgresql` * `postgis` | + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| itay-grudev | | | + +Features that require feedback +------------------------------ + +Please raise a ticket tested any of the following features and they have worked. +Alternatively a ticket and a PR if you have found that something needs a change to work properly. + +- [ ] Google Cloud Storage Backups +- [ ] Google Cloud Storage Recovery + +TODO +---- +* IAM Role for S3 Service Account +* Automatic provisioning of a Alert Manager configuration + diff --git a/charts/cluster/README.md.gotmpl b/charts/cluster/README.md.gotmpl new file mode 100644 index 000000000..956e1431e --- /dev/null +++ b/charts/cluster/README.md.gotmpl @@ -0,0 +1,147 @@ +{{ template "chart.header" . }} + + +{{ template "chart.deprecationWarning" . }} + + +{{ template "chart.badgesSection" . }} + + +> **Warning** +> ### This chart is under active development. +> ### Advised caution when using in production! + + +A note on the chart's purpose +----------------------------- + +This is an opinionated chart that is designed to provide a subset of simple, stable and safe configurations using the +CloudNativePG operator. It is designed to provide a simple way to perform recovery operations to decrease your RTO. + +It is not designed to be a one size fits all solution. If you need a more complicated setup we strongly recommend that +you either: + +* use the operator directly +* create your own chart +* use Kustomize to modify the chart's resources + +**_Note_** that the latter option carries it's own risks as the chart configuration may change, especially before it +reaches a stable release. + +That being said, we welcome PRs that improve the chart, but please keep in mind that we don't plan to support every +single configuration that the operator provides and we may reject PRs that add too much complexity and maintenance +difficulty to the chart. + + +Getting Started +--------------- + +### Installing the Operator +Skip this step if the CNPG operator is already installed in your cluster. + +```console +helm repo add cnpg https://cloudnative-pg.github.io/charts +helm upgrade --install cnpg \ +--namespace cnpg-system \ +--create-namespace \ +cnpg/cloudnative-pg +``` + +### Setting up a CNPG Cluster + +```console +helm repo add cnpg https://cloudnative-pg.github.io/charts +helm upgrade --install cnpg \ +--namespace cnpg-database \ +--create-namespace \ +--values values.yaml \ +cnpg/cluster +``` + +A more detailed guide can be found here: [Getting Started](docs/Getting Started.md) + + +Cluster Configuration +--------------------- + +### Database types + +Currently the chart supports two database types. These are configured via the `type` parameter. These are: +* `postgresql` - A standard PostgreSQL database. +* `postgis` - A PostgreSQL database with the PostGIS extension installed. + +Depending on the type the chart will use a different Docker image and fill in some initial setup, like extension installation. + +### Modes of operation + +The chart has three modes of operation. These are configured via the `mode` parameter: +* `standalone` - Creates new or updates an existing CNPG cluster. This is the default mode. +* `replica` - Creates a replica cluster from an existing CNPG cluster. **_Note_ that this mode is not yet supported.** +* `recovery` - Recovers a CNPG cluster from a backup, object store or via pg_basebackup. + +### Backup configuration + +CNPG implements disaster recovery via [Barman](https://pgbarman.org/). The following section configures the barman object +store where backups will be stored. Barman performs backups of the cluster filesystem base backup and WALs. Both are +stored in the specified location. The backup provider is configured via the `backups.provider` parameter. The following +providers are supported: + +* S3 or S3-compatible stores, like MinIO +* Microsoft Azure Blob Storage +* Google Cloud Storage + +Additionally you can specify the following parameters: +* `backups.retentionPolicy` - The retention policy for backups. Defaults to `30d`. +* `backups.scheduledBackups` - An array of scheduled backups containing a name and a crontab schedule. Example: +```yaml +backups: + scheduledBackups: + - name: daily-backup + schedule: "0 0 0 * * *" # Daily at midnight + backupOwnerReference: self +``` + +Each backup adapter takes it's own set of parameters, listed in the [Configuration options](#Configuration-options) section +below. Refer to the table for the full list of parameters and place the configuration under the appropriate key: `backup.s3`, +`backup.azure`, or `backup.google`. + + +Recovery +-------- + +There is a separate document outlining the recovery procedure here: **[Recovery](docs/recovery.md)** + + +Examples +-------- + +There are several configuration examples in the [examples](examples) directory. Refer to them for a basic setup and +refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentation/current/) for more advanced configurations. + + +{{ template "chart.requirementsSection" . }} + + +{{ template "chart.valuesSection" . }} + + +{{ template "chart.maintainersSection" . }} + + +Features that require feedback +------------------------------ + +Please raise a ticket tested any of the following features and they have worked. +Alternatively a ticket and a PR if you have found that something needs a change to work properly. + +- [ ] Google Cloud Storage Backups +- [ ] Google Cloud Storage Recovery + + +TODO +---- +* IAM Role for S3 Service Account +* Automatic provisioning of a Alert Manager configuration + + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/cluster/docs/Getting Started.md b/charts/cluster/docs/Getting Started.md new file mode 100644 index 000000000..54dad419d --- /dev/null +++ b/charts/cluster/docs/Getting Started.md @@ -0,0 +1,106 @@ +# Getting Started + +The CNPG cluster chart follows a convention over configuration approach. This means that the chart will create a reasonable +CNPG setup with sensible defaults. However, you can override these defaults to create a more customized setup. Note that +you still need to configure backups and monitoring separately. The chart will not install a Prometheus stack for you. + +_**Note,**_ that this is an opinionated chart. It does not support all configuration options that CNPG supports. If you +need a highly customized setup, you should manage your cluster via a Kubernetes CNPG cluster manifest instead of this chart. +Refer to the [CNPG documentation](https://cloudnative-pg.io/documentation/current/) in that case. + +## Installing the operator + +To begin, make sure you install the CNPG operator in you cluster. It can be installed via a Helm chart as shown below or +ir can be installed via a Kubernetes manifest. For more information see the [CNPG documentation](https://cloudnative-pg.io/documentation/current/installation_upgrade/). + +```console +helm repo add cnpg https://cloudnative-pg.github.io/charts +helm upgrade --install cnpg \ + --namespace cnpg-system \ + --create-namespace \ + cnpg/cloudnative-pg +``` + +## Creating a cluster configuration + +Once you have the operator installed, the next step is to prepare the cluster configuration. Whether this will be manged +via a GitOps solution or directly via Helm is up to you. The following sections outlines the important steps in both cases. + +### Choosing the database type + +Currently the chart supports two database types. These are configured via the `type` parameter. These are: +* `postgresql` - A standard PostgreSQL database. +* `postgis` - A PostgreSQL database with the PostGIS extension installed. + +Depending on the type the chart will use a different Docker image and fill in some initial setup, like extension installation. + +### Choosing the mode of operation + +The chart has three modes of operation. These are configured via the `mode` parameter. If this is your first cluster, you +are likely looking for the `standalone` option. +* `standalone` - Creates new or updates an existing CNPG cluster. This is the default mode. +* `replica` - Creates a replica cluster from an existing CNPG cluster. **_Note_ that this mode is not yet supported.** +* `recovery` - Recovers a CNPG cluster from a backup, object store or via pg_basebackup. + +### Backup configuration + +Most importantly you should configure your backup storage. + +CNPG implements disaster recovery via [Barman](https://pgbarman.org/). The following section configures the barman object +store where backups will be stored. Barman performs backups of the cluster filesystem base backup and WALs. Both are +stored in the specified location. The backup provider is configured via the `backups.provider` parameter. The following +providers are supported: + +* S3 or S3-compatible stores, like MinIO +* Microsoft Azure Blob Storage +* Google Cloud Storage + +Additionally you can specify the following parameters: +* `backups.retentionPolicy` - The retention policy for backups. Defaults to `30d`. +* `backups.scheduledBackups` - An array of scheduled backups containing a name and a crontab schedule. Example: + ```yaml + backups: + scheduledBackups: + - name: daily-backup + schedule: "0 0 0 * * *" # Daily at midnight + backupOwnerReference: self + ``` + +Each backup adapter takes it's own set of parameters, listed in the [Configuration options](../README.md#Configuration-options) section +below. Refer to the table for the full list of parameters and place the configuration under the appropriate key: `backup.s3`, +`backup.azure`, or `backup.google`. + +### Cluster configuration + +There are several important cluster options. Here are the most important ones: + +`cluster.instances` - The number of instances in the cluster. Defaults to `1`, but you should set this to `3` for production. +`cluster.imageName` - This allows you to override the Docker image used for the cluster. The chart will choose a default + for you based on the setting you chose for `type`. If you need to run a configuration that is not supported, you can + create your own Docker image. You can use the [postgres-containers](https://github.com/cloudnative-pg/postgres-containers) + repository for a starting point. + You will likely need to set your own repository access credentials via: `cluster.imagePullPolicy` and `cluster.imagePullSecrets`. +`cluster.storage.size` - The size of the persistent volume claim for the cluster. Defaults to `8Gi`. Every instance will + have it's own persistent volume claim. +`cluster.storage.storageClass` - The storage class to use for the persistent volume claim. +`cluster.resources` - The resource limits and requests for the cluster. You are strongly advised to use the same values + for both limits and requests to ensure a [Guaranteed QoS](https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/#guaranteed). +`cluster.affinity.topologyKey` - The chart sets it to `topology.kubernetes.io/zone` by default which is useful if you are + running a production cluster in a multi AZ cluster (highly recommended). If you are running a single AZ cluster, you may + want to change that to `kubernetes.io/hostname` to ensure that cluster instances are not provisioned on the same node. +`cluster.postgresql` - Allows you to override PostgreSQL configuration parameters example: + ```yaml + cluster: + postgresql: + max_connections: "200" + shared_buffers: "2GB" + ``` +`cluster.initSQL` - Allows you to run custom SQL queries during the cluster initialization. This is useful for creating +extensions, schemas and databases. Note that these are as a superuser. + +For a full list - refer to the Helm chart [configuration options](../README.md#Configuration-options). + +## Examples + +There are several configuration examples in the [examples](../examples) directory. Refer to them for a basic setup and +refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentation/current/) for more advanced configurations. diff --git a/charts/cluster/docs/Recovery.md b/charts/cluster/docs/Recovery.md new file mode 100644 index 000000000..6a1be6593 --- /dev/null +++ b/charts/cluster/docs/Recovery.md @@ -0,0 +1,27 @@ +Recovery +======== + +This chart can be used to initiate a recovery operation of a CNPG cluster no matter if it was created with the chart or not. + +CNPG does not support recovery in-place. Instead you need to create a new cluster that will be bootstrapped from the existing one or from a backup. + +You can find more information about the recovery process in the [CNPG documentation](https://cloudnative-pg.io/documentation/current/backup_recovery). + +There are 3 types of recovery possible with CNPG: +* Recovery from a backup object in the same Kubernetes namespace. +* Recovery from a Barman Object Store, that could be located anywhere. +* Streaming replication from an operating cluster using `pg_basebackup` (not supported by the chart yet). + +When performing a recovery you are strongly advised to use the same configuration and PostgreSQL version as the original cluster. + +To begin, create a `values.yaml` that contains the following: + +1. Set `mode: recovery` to indicate that you want to perform bootstrap the new cluster from an existing one. +2. Set the `recovery.method` to the type of recovery you want to perform. +3. Set either the `recovery.backupName` or the Barman Object Store configuration - i.e. `recovery.provider` and appropriate S3, Azure or GCS configuration. +4. Optionally set the `recovery.pitrTarget.time` in RFC3339 format to perform a point-in-time recovery. +4. Retain the identical PostgreSQL version and configuration as the original cluster. +5. Make sure you don't use the same backup section name as the original cluster. We advise you change the `path` within the storage location if you want to reuse the same storage location/bucket. + One pattern is adding a version number at the end of the path, e.g. `/v1` or `/v2` after each recovery procedure. + +Example recovery configurations can be found in the [examples](../examples) directory. diff --git a/charts/cluster/examples/basic.yaml b/charts/cluster/examples/basic.yaml new file mode 100644 index 000000000..5b608c267 --- /dev/null +++ b/charts/cluster/examples/basic.yaml @@ -0,0 +1,5 @@ +mode: standalone +cluster: + instances: 1 +backups: + enabled: false diff --git a/charts/cluster/examples/custom-queries.yaml b/charts/cluster/examples/custom-queries.yaml new file mode 100644 index 000000000..1e6ef16f6 --- /dev/null +++ b/charts/cluster/examples/custom-queries.yaml @@ -0,0 +1,23 @@ +type: postgresql +mode: standalone + +cluster: + instances: 1 + monitoring: + customQueries: + - name: "pg_cache_hit" + query: | + SELECT + current_database() as datname, + sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratio + FROM pg_statio_user_tables; + metrics: + - datname: + usage: "LABEL" + description: "Name of the database database" + - ratio: + usage: GAUGE + description: "Cache hit ratio" + +backups: + enabled: false \ No newline at end of file diff --git a/charts/cluster/examples/postgis.yaml b/charts/cluster/examples/postgis.yaml new file mode 100644 index 000000000..6c686dc62 --- /dev/null +++ b/charts/cluster/examples/postgis.yaml @@ -0,0 +1,6 @@ +type: postgis +mode: standalone +cluster: + instances: 1 +backups: + enabled: false \ No newline at end of file diff --git a/charts/cluster/examples/recovery-backup.yaml b/charts/cluster/examples/recovery-backup.yaml new file mode 100644 index 000000000..d11187f5c --- /dev/null +++ b/charts/cluster/examples/recovery-backup.yaml @@ -0,0 +1,22 @@ +mode: recovery + +recovery: + method: backup + backupName: "database-clustermarket-database-daily-backup-1683244800" + +cluster: + instances: 1 + +backups: + provider: s3 + s3: + region: "eu-west-1" + bucket: "db-backups" + path: "/v1-restore" + accessKey: "AWS_S3_ACCESS_KEY" + secretKey: "AWS_S3_SECRET_KEY" + scheduledBackups: + - name: daily-backup # Daily at midnight + schedule: "0 0 0 * * *" # Daily at midnight + backupOwnerReference: self + retentionPolicy: "30d" \ No newline at end of file diff --git a/charts/cluster/examples/recovery-object_store.yaml b/charts/cluster/examples/recovery-object_store.yaml new file mode 100644 index 000000000..92722a159 --- /dev/null +++ b/charts/cluster/examples/recovery-object_store.yaml @@ -0,0 +1,30 @@ +mode: recovery + +recovery: + method: object_store + serverName: "cluster-name-to-recover-from" + provider: s3 + s3: + region: "eu-west-1" + bucket: "db-backups" + path: "/v1-restore" + accessKey: "AWS_S3_ACCESS_KEY" + secretKey: "AWS_S3_SECRET_KEY" + +cluster: + instances: 1 + +backups: + endpointURL: "https://cm-db-chart-test.ams3.digitaloceanspaces.com" + provider: s3 + s3: + region: "eu-west-1" + bucket: "db-backups" + path: "/v1-restore" + accessKey: "AWS_S3_ACCESS_KEY" + secretKey: "AWS_S3_SECRET_KEY" + scheduledBackups: + - name: daily-backup # Daily at midnight + schedule: "0 0 0 * * *" # Daily at midnight + backupOwnerReference: self + retentionPolicy: "30d" \ No newline at end of file diff --git a/charts/cluster/examples/standalone-s3.yaml b/charts/cluster/examples/standalone-s3.yaml new file mode 100644 index 000000000..bf1794d06 --- /dev/null +++ b/charts/cluster/examples/standalone-s3.yaml @@ -0,0 +1,19 @@ +mode: standalone + +cluster: + instances: 1 + +backups: + enabled: true + provider: s3 + s3: + region: "eu-west-1" + bucket: "db-backups" + path: "/v1" + accessKey: "AWS_S3_ACCESS_KEY" + secretKey: "AWS_S3_SECRET_KEY" + scheduledBackups: + - name: daily-backup # Daily at midnight + schedule: "0 0 0 * * *" # Daily at midnight + backupOwnerReference: self + retentionPolicy: "30d" diff --git a/charts/cluster/templates/NOTES.txt b/charts/cluster/templates/NOTES.txt new file mode 100644 index 000000000..28c0e6172 --- /dev/null +++ b/charts/cluster/templates/NOTES.txt @@ -0,0 +1,68 @@ +{{ if .Release.IsInstall }} +The {{ include "cluster.color-info" (include "cluster.fullname" .) }} has been installed successfully. +{{ else if .Release.IsUpgrade }} +The {{ include "cluster.color-info" (include "cluster.fullname" .) }} has been upgraded successfully. +{{ end }} + + ██████ ██ ██ ████ ██ ██ ██ ███████ ████████ + ██░░░░██ ░██ ░██░██░██ ░██ ░██ ░░ ░██░░░░██ ██░░░░░░██ + ██ ░░ ░██ ██████ ██ ██ ░██░██░░██ ░██ ██████ ██████ ██ ██ ██ █████ ░██ ░██ ██ ░░ +░██ ░██ ██░░░░██░██ ░██ ██████░██ ░░██ ░██ ░░░░░░██ ░░░██░ ░██░██ ░██ ██░░░██░███████ ░██ +░██ ░██░██ ░██░██ ░██ ██░░░██░██ ░░██░██ ███████ ░██ ░██░░██ ░██ ░███████░██░░░░ ░██ █████ +░░██ ██ ░██░██ ░██░██ ░██░██ ░██░██ ░░████ ██░░░░██ ░██ ░██ ░░████ ░██░░░░ ░██ ░░██ ░░░░██ + ░░██████ ███░░██████ ░░██████░░██████░██ ░░███░░████████ ░░██ ░██ ░░██ ░░██████░██ ░░████████ + ░░░░░░ ░░░ ░░░░░░ ░░░░░░ ░░░░░░ ░░ ░░░ ░░░░░░░░ ░░ ░░ ░░ ░░░░░░ ░░ ░░░░░░░░ + +Cheatsheet +---------- + +Run Helm Tests: +{{ include "cluster.color-info" (printf "helm test --namespace %s %s" .Release.Namespace .Release.Name) }} + +Get a list of all base backups: +{{ include "cluster.color-info" (printf "kubectl --namespace %s get backups --selector cnpg.io/cluster=%s" .Release.Namespace (include "cluster.fullname" .)) }} + +Connect to the cluster's primary instance: +{{ include "cluster.color-info" (printf "kubectl --namespace %s exec --stdin --tty services/%s-rw -- bash" .Release.Namespace (include "cluster.fullname" .)) }} + +Configuration +------------- + +{{- $redundancyColor := "" -}} +{{- if lt (int .Values.cluster.instances) 2 }} + {{- $redundancyColor = "error" -}} +{{- else if lt (int .Values.cluster.instances) 3 -}} + {{- $redundancyColor = "warning" -}} +{{- else -}} + {{- $redundancyColor = "ok" -}} +{{- end }} + +{{ $scheduledBackups := (first .Values.backups.scheduledBackups).name }} +{{- range (rest .Values.backups.scheduledBackups) -}} + {{ $scheduledBackups = printf "%s, %s" $scheduledBackups .name }} +{{- end -}} + +╭───────────────────┬────────────────────────────────────────────╮ +│ Configuration │ Value │ +┝━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥ +│ Cluster mode │ {{ (printf "%-42s" .Values.mode) }} │ +│ Type │ {{ (printf "%-42s" .Values.type) }} │ +│ Image │ {{ include "cluster.color-info" (printf "%-42s" (include "cluster.imageName" .)) }} │ +│ Instances │ {{ include (printf "%s%s" "cluster.color-" $redundancyColor) (printf "%-42s" (toString .Values.cluster.instances)) }} │ +│ Backups │ {{ include (printf "%s%s" "cluster.color-" (ternary "ok" "error" .Values.backups.enabled)) (printf "%-42s" (ternary "Enabled" "Disabled" .Values.backups.enabled)) }} │ +│ Backup Provider │ {{ (printf "%-42s" (title .Values.backups.provider)) }} │ +│ Scheduled Backups │ {{ (printf "%-42s" $scheduledBackups) }} │ +│ Storage │ {{ (printf "%-42s" .Values.cluster.storage.size) }} │ +│ Storage Class │ {{ (printf "%-42s" (default "Default" .Values.cluster.storage.storageClass)) }} │ +│ PGBouncer │ {{ (printf "%-42s" (ternary "Enabled" "Disabled" .Values.pooler.enabled)) }} │ +╰───────────────────┴────────────────────────────────────────────╯ + +{{ if not .Values.backups.enabled }} + {{- include "cluster.color-error" "Warning! Backups not enabled. Recovery will not be possible! Do not use this configuration in production.\n" }} +{{ end -}} + +{{ if lt (int .Values.cluster.instances) 2 }} + {{- include "cluster.color-error" "Warning! Instance failure will lead to downtime and/or data loss!\n" }} +{{- else if lt (int .Values.cluster.instances) 3 -}} + {{- include "cluster.color-warning" "Warning! Single instance redundancy available only. Instance failure will put the cluster at risk.\n" }} +{{ end -}} diff --git a/charts/cluster/templates/_backup.tpl b/charts/cluster/templates/_backup.tpl new file mode 100644 index 000000000..cb76d9b74 --- /dev/null +++ b/charts/cluster/templates/_backup.tpl @@ -0,0 +1,18 @@ +{{- define "cluster.backup" -}} +backup: +{{- if .Values.backups.enabled }} + target: "prefer-standby" + retentionPolicy: {{ .Values.backups.retentionPolicy }} + barmanObjectStore: + wal: + compression: gzip + encryption: AES256 + data: + compression: gzip + encryption: AES256 + jobs: 2 + + {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.backups }} + {{- include "cluster.barmanObjectStoreConfig" $d | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/cluster/templates/_barman_object_store.tpl b/charts/cluster/templates/_barman_object_store.tpl new file mode 100644 index 000000000..96278f11a --- /dev/null +++ b/charts/cluster/templates/_barman_object_store.tpl @@ -0,0 +1,58 @@ +{{- define "cluster.barmanObjectStoreConfig" -}} + +{{- if .scope.endpointURL }} + endpointURL: {{ .scope.endpointURL }} +{{- end }} + +{{- if .scope.destinationPath }} + destinationPath: {{ .scope.destinationPath }} +{{- end }} + +{{- if eq .scope.provider "s3" }} + {{- if empty .scope.endpointURL }} + endpointURL: "https://s3.{{ required "You need to specify S3 region if endpointURL is not specified." .scope.s3.region }}.amazonaws.com" + {{- end }} + {{- if empty .scope.destinationPath }} + destinationPath: "s3://{{ required "You need to specify S3 bucket if destinationPath is not specified." .scope.s3.bucket }}{{ .scope.s3.path }}" + {{- end }} + s3Credentials: + accessKeyId: + name: {{ .chartFullname }}-backup-s3{{ .secretSuffix }}-creds + key: ACCESS_KEY_ID + secretAccessKey: + name: {{ .chartFullname }}-backup-s3{{ .secretSuffix }}-creds + key: ACCESS_SECRET_KEY +{{- else if eq .scope.provider "azure" }} + {{- if empty .scope.destinationPath }} + destinationPath: "https://{{ required "You need to specify Azure storageAccount if destinationPath is not specified." .scope.azure.storageAccount }}.{{ .scope.azure.serviceName }}.core.windows.net/{{ .scope.azure.containerName }}{{ .scope.azure.path }}" + {{- end }} + azureCredentials: + {{- if .scope.azure.connectionString }} + connectionString: + name: {{ .chartFullname }}-backup-azure{{ .secretSuffix }}-creds + key: AZURE_CONNECTION_STRING + {{- else }} + storageAccount: + name: {{ .chartFullname }}-backup-azure{{ .secretSuffix }}-creds + key: AZURE_STORAGE_ACCOUNT + {{- if .scope.azure.storageKey }} + storageKey: + name: {{ .chartFullname }}-backup-azure{{ .secretSuffix }}-creds + key: AZURE_STORAGE_KEY + {{- else }} + storageSasToken: + name: {{ .chartFullname }}-backup-azure{{ .secretSuffix }}-creds + key: AZURE_STORAGE_SAS_TOKEN + {{- end }} + {{- end }} +{{- else if eq .scope.provider "google" }} + {{- if empty .scope.destinationPath }} + destinationPath: "gs://{{ required "You need to specify Google storage bucket if destinationPath is not specified." .scope.google.bucket }}{{ .scope.google.path }}" + {{- end }} + googleCredentials: + gkeEnvironment: {{ .scope.google.gkeEnvironment }} + applicationCredentials: + name: {{ .chartFullname }}-backup-google{{ .secretSuffix }}-creds + key: APPLICATION_CREDENTIALS +{{- end -}} +{{- end -}} diff --git a/charts/cluster/templates/_bootstrap.tpl b/charts/cluster/templates/_bootstrap.tpl new file mode 100644 index 000000000..214ad391b --- /dev/null +++ b/charts/cluster/templates/_bootstrap.tpl @@ -0,0 +1,46 @@ +{{- define "cluster.bootstrap" -}} +bootstrap: +{{- if eq .Values.mode "standalone" }} + initdb: + {{- with .Values.cluster.initdb }} + {{- with (omit . "postInitApplicationSQL") }} + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- end }} + postInitApplicationSQL: + {{- if eq .Values.type "postgis" }} + - CREATE EXTENSION IF NOT EXISTS postgis; + - CREATE EXTENSION IF NOT EXISTS postgis_topology; + - CREATE EXTENSION IF NOT EXISTS fuzzystrmatch; + - CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder; + {{- else if eq .Values.type "timescaledb" }} + - CREATE EXTENSION IF NOT EXISTS timescaledb; + {{- end }} + {{- with .Values.cluster.initdb }} + {{- range .postInitApplicationSQL }} + {{- printf "- %s" . | nindent 6 }} + {{- end -}} + {{- end -}} +{{- else if eq .Values.mode "recovery" }} + recovery: + {{- with .Values.recovery.pitrTarget.time }} + recoveryTarget: + targetTime: {{ . }} + {{- end }} + {{- if eq .Values.recovery.method "backup" }} + backup: + name: {{ .Values.recovery.backupName }} + {{- else if eq .Values.recovery.method "object_store" }} + source: objectStoreRecoveryCluster + {{- end }} + +externalClusters: + - name: objectStoreRecoveryCluster + barmanObjectStore: + serverName: {{ .Values.recovery.serverName }} + {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.recovery "secretSuffix" "-recovery" -}} + {{- include "cluster.barmanObjectStoreConfig" $d | nindent 4 }} +{{- else }} + {{ fail "Invalid cluster mode!" }} +{{- end }} +{{- end }} diff --git a/charts/cluster/templates/_colorize.tpl b/charts/cluster/templates/_colorize.tpl new file mode 100644 index 000000000..35b2ba1cd --- /dev/null +++ b/charts/cluster/templates/_colorize.tpl @@ -0,0 +1,12 @@ +{{- define "cluster.color-error" }} + {{- printf "\033[0;31m%s\033[0m" . -}} +{{- end }} +{{- define "cluster.color-ok" }} + {{- printf "\033[0;32m%s\033[0m" . -}} +{{- end }} +{{- define "cluster.color-warning" }} + {{- printf "\033[0;33m%s\033[0m" . -}} +{{- end }} +{{- define "cluster.color-info" }} + {{- printf "\033[0;34m%s\033[0m" . -}} +{{- end }} \ No newline at end of file diff --git a/charts/cluster/templates/_helpers.tpl b/charts/cluster/templates/_helpers.tpl new file mode 100644 index 000000000..b00846d60 --- /dev/null +++ b/charts/cluster/templates/_helpers.tpl @@ -0,0 +1,69 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "cluster.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "cluster.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "cluster.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "cluster.labels" -}} +helm.sh/chart: {{ include "cluster.chart" . }} +{{ include "cluster.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "cluster.selectorLabels" -}} +app.kubernetes.io/name: {{ include "cluster.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Cluster Image Name +If a custom imageName is available, use it, otherwise use the defaults based on the .Values.type +*/}} +{{- define "cluster.imageName" -}} + {{- if .Values.cluster.imageName -}} + {{- .Values.cluster.imageName -}} + {{- else if eq .Values.type "postgresql" -}} + {{- "ghcr.io/cloudnative-pg/postgresql:15.2" -}} + {{- else if eq .Values.type "postgis" -}} + {{- "ghcr.io/cloudnative-pg/postgis:14" -}} + {{- else if eq .Values.type "timescaledb" -}} + {{ fail "You need to provide your own cluster.imageName as an official timescaledb image doesn't exist yet." }} + {{- else -}} + {{ fail "Invalid cluster type!" }} + {{- end }} +{{- end -}} diff --git a/charts/cluster/templates/backup-azure-creds.yaml b/charts/cluster/templates/backup-azure-creds.yaml new file mode 100644 index 000000000..19a651eb3 --- /dev/null +++ b/charts/cluster/templates/backup-azure-creds.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.backups.enabled (eq .Values.backups.provider "azure") }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "cluster.fullname" . }}-backup-azure-creds +data: + AZURE_CONNECTION_STRING: {{ .Values.backups.azure.connectionString | b64enc | quote }} + AZURE_STORAGE_ACCOUNT: {{ .Values.backups.azure.storageAccount | b64enc | quote }} + AZURE_STORAGE_KEY: {{ .Values.backups.azure.storageKey | b64enc | quote }} + AZURE_STORAGE_SAS_TOKEN: {{ .Values.backups.azure.storageSasToken | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/cluster/templates/backup-azure-recovery-creds.yaml b/charts/cluster/templates/backup-azure-recovery-creds.yaml new file mode 100644 index 000000000..b4aecb558 --- /dev/null +++ b/charts/cluster/templates/backup-azure-recovery-creds.yaml @@ -0,0 +1,11 @@ +{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "azure") }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "cluster.fullname" . }}-backup-azure-recovery-creds +data: + AZURE_CONNECTION_STRING: {{ .Values.recovery.azure.connectionString | b64enc | quote }} + AZURE_STORAGE_ACCOUNT: {{ .Values.recovery.azure.storageAccount | b64enc | quote }} + AZURE_STORAGE_KEY: {{ .Values.recovery.azure.storageKey | b64enc | quote }} + AZURE_STORAGE_SAS_TOKEN: {{ .Values.recovery.azure.storageSasToken | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/cluster/templates/backup-google-creds.yaml b/charts/cluster/templates/backup-google-creds.yaml new file mode 100644 index 000000000..252a27064 --- /dev/null +++ b/charts/cluster/templates/backup-google-creds.yaml @@ -0,0 +1,8 @@ +{{- if and .Values.backups.enabled (eq .Values.backups.provider "google") }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "cluster.fullname" . }}-backup-google-creds +data: + APPLICATION_CREDENTIALS: {{ .Values.backups.google.applicationCredentials | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/cluster/templates/backup-google-recovery-creds.yaml b/charts/cluster/templates/backup-google-recovery-creds.yaml new file mode 100644 index 000000000..942bb897b --- /dev/null +++ b/charts/cluster/templates/backup-google-recovery-creds.yaml @@ -0,0 +1,8 @@ +{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "google") }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "cluster.fullname" . }}-backup-google-recovery-creds +data: + APPLICATION_CREDENTIALS: {{ .Values.recovery.google.applicationCredentials | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/cluster/templates/backup-s3-creds.yaml b/charts/cluster/templates/backup-s3-creds.yaml new file mode 100644 index 000000000..b906d2453 --- /dev/null +++ b/charts/cluster/templates/backup-s3-creds.yaml @@ -0,0 +1,9 @@ +{{- if and .Values.backups.enabled (eq .Values.backups.provider "s3") }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "cluster.fullname" . }}-backup-s3-creds +data: + ACCESS_KEY_ID: {{ required ".Values.backups.s3.accessKey is required, but not specified." .Values.backups.s3.accessKey | b64enc | quote }} + ACCESS_SECRET_KEY: {{ required ".Values.backups.s3.secretKey is required, but not specified." .Values.backups.s3.secretKey | b64enc | quote }} +{{- end }} diff --git a/charts/cluster/templates/backup-s3-recovery-creds.yaml b/charts/cluster/templates/backup-s3-recovery-creds.yaml new file mode 100644 index 000000000..9cc615fcd --- /dev/null +++ b/charts/cluster/templates/backup-s3-recovery-creds.yaml @@ -0,0 +1,9 @@ +{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "s3") }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "cluster.fullname" . }}-backup-s3-recovery-creds +data: + ACCESS_KEY_ID: {{ required ".Values.recovery.s3.accessKey is required, but not specified." .Values.recovery.s3.accessKey | b64enc | quote }} + ACCESS_SECRET_KEY: {{ required ".Values.recovery.s3.secretKey is required, but not specified." .Values.recovery.s3.secretKey | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/cluster/templates/cluster.yaml b/charts/cluster/templates/cluster.yaml new file mode 100644 index 000000000..4ec251698 --- /dev/null +++ b/charts/cluster/templates/cluster.yaml @@ -0,0 +1,64 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: {{ include "cluster.fullname" . }} + {{- with .Values.cluster.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "cluster.labels" . | nindent 4 }} + {{- with .Values.cluster.additionalLabels }} + {{ toYaml . | nindent 4 }} + {{- end }} +spec: + instances: {{ .Values.cluster.instances }} + imageName: {{ include "cluster.imageName" . }} + imagePullPolicy: {{ .Values.cluster.imagePullPolicy }} + {{- with .Values.cluster.imagePullSecrets}} + imagePullSecrets: + {{- . | toYaml | nindent 4 }} + {{- end }} + storage: + size: {{ .Values.cluster.storage.size }} + storageClass: {{ .Values.cluster.storage.storageClass }} + + {{- with .Values.cluster.resources }} + resources: + {{- toYaml . | nindent 4 }} + {{ end }} + {{- with .Values.cluster.affinity }} + affinity: + {{- toYaml . | nindent 4 }} + {{- end }} + priorityClassName: {{ .Values.cluster.priorityClassName }} + + primaryUpdateMethod: {{ .Values.cluster.primaryUpdateMethod }} + primaryUpdateStrategy: {{ .Values.cluster.primaryUpdateStrategy }} + logLevel: {{ .Values.cluster.logLevel }} + {{- with .Values.cluster.certificates }} + certificates: + {{- toYaml . | nindent 4 }} + {{ end }} + enableSuperuserAccess: {{ .Values.cluster.enableSuperuserAccess }} + superuserSecret: {{ .Values.cluster.superuserSecret }} + + postgresql: + shared_preload_libraries: + {{- if eq .Values.type "timescaledb" }} + - timescaledb + {{- end }} + {{- with .Values.cluster.postgresql }} + parameters: + {{- toYaml . | nindent 6 }} + {{ end }} + + monitoring: + enablePodMonitor: {{ .Values.cluster.monitoring.enablePodMonitor }} + {{- if not (empty .Values.cluster.monitoring.customQueries) }} + customQueriesConfigMap: + - name: {{ include "cluster.fullname" . }}-monitoring + key: custom-queries + {{- end }} + {{ include "cluster.bootstrap" . | nindent 2 }} + {{ include "cluster.backup" . | nindent 2 }} diff --git a/charts/cluster/templates/pooler.yaml b/charts/cluster/templates/pooler.yaml new file mode 100644 index 000000000..5a606d96d --- /dev/null +++ b/charts/cluster/templates/pooler.yaml @@ -0,0 +1,15 @@ +{{ if .Values.pooler.enabled }} +apiVersion: postgresql.cnpg.io/v1 +kind: Pooler +metadata: + name: {{ include "cluster.fullname" . }}-pooler-rw +spec: + cluster: + name: {{ include "cluster.fullname" . }} + instances: {{ .Values.pooler.instances }} + type: rw + pgbouncer: + poolMode: {{ .Values.pooler.poolMode }} + parameters: + {{- .Values.pooler.parameters | toYaml | nindent 6 }} +{{ end }} \ No newline at end of file diff --git a/charts/cluster/templates/scheduled-backups.yaml b/charts/cluster/templates/scheduled-backups.yaml new file mode 100644 index 000000000..36fbc4471 --- /dev/null +++ b/charts/cluster/templates/scheduled-backups.yaml @@ -0,0 +1,17 @@ +{{ if .Values.backups.enabled }} +{{ $context := . -}} +{{ range .Values.backups.scheduledBackups -}} +--- +apiVersion: postgresql.cnpg.io/v1 +kind: ScheduledBackup +metadata: + name: {{ include "cluster.fullname" $context }}-{{ .name }} + labels: {{ include "cluster.labels" $context | nindent 4 }} +spec: + immediate: true + schedule: {{ .schedule }} + backupOwnerReference: {{ .backupOwnerReference }} + cluster: + name: {{ include "cluster.fullname" $context }} +{{ end -}} +{{ end }} diff --git a/charts/cluster/templates/tests/ping.yaml b/charts/cluster/templates/tests/ping.yaml new file mode 100644 index 000000000..95a474630 --- /dev/null +++ b/charts/cluster/templates/tests/ping.yaml @@ -0,0 +1,37 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "cluster.fullname" . }}-ping-test + labels: + app.kubernetes.io/component: database-ping-test + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + template: + metadata: + name: {{ include "cluster.fullname" . }}-ping-test + labels: + app.kubernetes.io/component: database-ping-test + spec: + restartPolicy: Never + containers: + - name: alpine + image: alpine:3.17 + command: [ 'sh' ] + env: + - name: PGUSER + valueFrom: + secretKeyRef: + name: {{ include "cluster.fullname" . }}-app + key: username + - name: PGPASS + valueFrom: + secretKeyRef: + name: {{ include "cluster.fullname" . }}-app + key: password + args: + - "-c" + - >- + apk add postgresql-client && + psql "postgresql://$PGUSER:$PGPASS@{{ include "cluster.fullname" . }}-rw.{{ .Release.Namespace }}.svc.cluster.local:5432" -c 'SELECT 1' diff --git a/charts/cluster/templates/user-metrics.yaml b/charts/cluster/templates/user-metrics.yaml new file mode 100644 index 000000000..e01039661 --- /dev/null +++ b/charts/cluster/templates/user-metrics.yaml @@ -0,0 +1,17 @@ +{{- if not (empty .Values.cluster.monitoring.customQueries) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "cluster.fullname" . }}-monitoring + labels: + cnpg.io/reload: "" + {{- include "cluster.labels" . | nindent 4 }} +data: + custom-queries: | + {{- range .Values.cluster.monitoring.customQueries }} + {{ .name }}: + query: {{ .query | quote }} + metrics: + {{- .metrics | toYaml | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml new file mode 100644 index 000000000..dec1fc9b4 --- /dev/null +++ b/charts/cluster/values.yaml @@ -0,0 +1,223 @@ +# -- Override the name of the chart +nameOverride: "" +# -- Override the full name of the chart +fullnameOverride: "" + +### +# -- Type of the CNPG database. Available types: +# * `postgresql` +# * `postgis` +type: postgresql + +### +# -- Cluster mode of operation. Available modes: +# * `standalone` - default mode. Creates new or updates an existing CNPG cluster. +# * `replica` - Creates a replica cluster from an existing CNPG cluster. # TODO +# * `recovery` - Same as standalone but creates a cluster from a backup, object store or via pg_basebackup. +mode: standalone + +recovery: + ## + # -- Available recovery methods: + # * `backup` - Recovers a CNPG cluster from a CNPG backup (PITR supported) Needs to be on the same cluster in the same namespace. + # * `object_store` - Recovers a CNPG cluster from a barman object store (PITR supported). + # * `pg_basebackup` - Recovers a CNPG cluster viaa streaming replication protocol. Useful if you want to + # migrate databases to CloudNativePG, even from outside Kubernetes. # TODO + method: backup + + ## -- Point in time recovery target. Specify one of the following: + pitrTarget: + # -- Time in RFC3339 format + time: "" + + ## + # -- Backup Recovery Method + backupName: "" # Name of the backup to recover from. Required if method is `backup`. + + ## + # -- Object Store Recovery Method + clusterName: "" + # -- Overrides the provider specific default endpoint. Defaults to: + # S3: https://s3..amazonaws.com" + # Leave empty if using the default S3 endpoint + endpointURL: "" + # -- Overrides the provider specific default path. Defaults to: + # S3: s3:// + # Azure: https://..core.windows.net/ + # Google: gs:// + destinationPath: "" + # -- One of `s3`, `azure` or `google` + provider: s3 + s3: + region: "" + bucket: "" + path: "/" + accessKey: "" + secretKey: "" + azure: + path: "/" + connectionString: "" + storageAccount: "" + storageKey: "" + storageSasToken: "" + containerName: "" + serviceName: blob + inheritFromAzureAD: false + google: + path: "/" + bucket: "" + gkeEnvironment: false + applicationCredentials: "" + + +cluster: + # -- Number of instances + instances: 3 + + # -- Name of the container image, supporting both tags (:) and digests for deterministic and repeatable deployments: + # :@sha256: + imageName: "" # Default value depends on type (postgresql/postgis/timescaledb) + + # -- Image pull policy. One of Always, Never or IfNotPresent. If not defined, it defaults to IfNotPresent. Cannot be updated. + # More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + imagePullPolicy: IfNotPresent + + # -- The list of pull secrets to be used to pull the images + # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-LocalObjectReference + imagePullSecrets: [] + + storage: + size: 8Gi + storageClass: "" + + # -- Resources requirements of every generated Pod. + # Please refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ for more information. + # We strongly advise you use the same setting for limits and requests so that your cluster pods are given a Guaranteed QoS. + # See: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ + resources: + # limits: + # cpu: 2000m + # memory: 8Gi + # requests: + # cpu: 2000m + # memory: 8Gi + + priorityClassName: "" + + # -- Method to follow to upgrade the primary server during a rolling update procedure, after all replicas have been + # successfully updated. It can be switchover (default) or in-place (restart). + primaryUpdateMethod: switchover + + # -- Strategy to follow to upgrade the primary server during a rolling update procedure, after all replicas have been + # successfully updated: it can be automated (unsupervised - default) or manual (supervised) + primaryUpdateStrategy: unsupervised + + # -- The instances' log level, one of the following values: error, warning, info (default), debug, trace + logLevel: "info" + + # -- Affinity/Anti-affinity rules for Pods + # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-AffinityConfiguration + affinity: + topologyKey: topology.kubernetes.io/zone + + # -- The configuration for the CA and related certificates + # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-CertificatesConfiguration + certificates: + + # -- When this option is enabled, the operator will use the SuperuserSecret to update the postgres user password. + # If the secret is not present, the operator will automatically create one. + # When this option is disabled, the operator will ignore the SuperuserSecret content, delete it when automatically created, + # and then blank the password of the postgres user by setting it to NULL. + enableSuperuserAccess: true + superuserSecret: "" + + monitoring: + enablePodMonitor: false + customQueries: [] + # - name: "pg_cache_hit_ratio" + # query: "SELECT current_database() as datname, sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratio FROM pg_statio_user_tables;" + # metrics: + # - datname: + # usage: "LABEL" + # description: "Name of the database" + # - ratio: + # usage: GAUGE + # description: "Cache hit ratio" + + # -- Configuration of the PostgreSQL server + # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration + postgresql: + + # -- BootstrapInitDB is the configuration of the bootstrap process when initdb is used + # See: https://cloudnative-pg.io/documentation/current/bootstrap/ + # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-bootstrapinitdb + initdb: {} + # database: app + # owner: "" # Defaults to the database name + # secret: "" # Name of the secret containing the initial credentials for the owner of the user database. If empty a new secret will be created from scratch + # postInitSQL: + # - CREATE EXTENSION IF NOT EXISTS vector; + + additionalLabels: {} + annotations: {} + + +backups: + # -- You need to configure backups manually, so backups are disabled by default. + enabled: false + + # -- Overrides the provider specific default endpoint. Defaults to: + # S3: https://s3..amazonaws.com" + endpointURL: "" # Leave empty if using the default S3 endpoint + + # -- Overrides the provider specific default path. Defaults to: + # S3: s3:// + # Azure: https://..core.windows.net/ + # Google: gs:// + destinationPath: "" + # -- One of `s3`, `azure` or `google` + provider: s3 + s3: + region: "" + bucket: "" + path: "/" + accessKey: "" + secretKey: "" + azure: + path: "/" + connectionString: "" + storageAccount: "" + storageKey: "" + storageSasToken: "" + containerName: "" + serviceName: blob + inheritFromAzureAD: false + google: + path: "/" + bucket: "" + gkeEnvironment: false + applicationCredentials: "" + + scheduledBackups: + - + # -- Scheduled backup name + name: daily-backup + # -- Schedule in cron format + schedule: "0 0 0 * * *" + # -- Backup owner reference + backupOwnerReference: self + + # -- Retention policy for backups + retentionPolicy: "30d" + +pooler: + # -- Whether to enable PgBouncer + enabled: false + # -- PgBouncer pooling mode + poolMode: transaction + # -- Number of PgBouncer instances + instances: 3 + # -- PgBouncer configuration parameters + parameters: + max_client_conn: "1000" + default_pool_size: "25" From c03c05cbf3ec8cb752dd3ead4ec7ef2923143c83 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:32:18 +0100 Subject: [PATCH 16/87] chore(deps): update helm/kind-action action to v1.9.0 (#186) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/actions/setup-kind/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-kind/action.yml b/.github/actions/setup-kind/action.yml index bb88f85b9..347864b17 100644 --- a/.github/actions/setup-kind/action.yml +++ b/.github/actions/setup-kind/action.yml @@ -21,4 +21,4 @@ runs: uses: azure/setup-kubectl@901a10e89ea615cf61f57ac05cecdf23e7de06d8 # v3.2 - name: Create kind cluster - uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0 + uses: helm/kind-action@99576bfa6ddf9a8e612d83b513da5a75875caced # v1.9.0 From 946699d20cdb93d46722ba084d0e94a9025d307c Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 21 Feb 2024 07:21:33 +0200 Subject: [PATCH 17/87] Release cluster-0.0.1 Updated release CI Signed-off-by: Itay Grudev --- .github/workflows/release-pr.yml | 42 +++--- .github/workflows/release-publish.yml | 62 +++++++-- .github/workflows/release-tag.yml | 43 ------- RELEASE.md | 179 ++++++++++++++++++-------- dashboard.png | Bin 798572 -> 0 bytes 5 files changed, 198 insertions(+), 128 deletions(-) delete mode 100644 .github/workflows/release-tag.yml delete mode 100644 dashboard.png diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 304eb5120..856d8a743 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -1,31 +1,31 @@ -# Create a PR for a release when a commit is pushed on a release/v* branch - +## +# Create a PR for a release when a commit is pushed on a release/*-v* branch to support the releases of both the +# operator and cluster charts name: release-pr on: push: branches: - - release/*-v* + - release/*-v* + +permissions: + pull-requests: write jobs: - pull-request: - runs-on: ubuntu-22.04 + create-pull-request: + runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Get tag + - name: Create Pull Request + id: create-pr + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - echo "TAG=${GITHUB_REF##*/}" >> $GITHUB_ENV - - - name: Pull Request - id: open-pr - uses: repo-sync/pull-request@7e79a9f5dc3ad0ce53138f01df2fad14a04831c5 # v2.12.1 - with: - destination_branch: "main" - github_token: ${{ secrets.GITHUB_TOKEN }} - pr_body: "Automated PR. Will trigger the ${{ env.TAG }} release when approved." - pr_label: release - pr_title: "Release ${{ env.TAG }}" - + TAG="${GITHUB_REF##*/}" + TITLE="Release ${TAG}" + BODY="Automated PR. Will trigger the ${TAG} release when approved." + LABEL=release + ASSIGNEE=${{ github.actor }} + gh pr create --title "${TITLE}" --body "${BODY}" --label "${LABEL}" --assignee "@${ASSIGNEE}" || + gh pr edit --title "${TITLE}" --body "${BODY}" --add-label "${LABEL}" diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 5527f8212..891d78207 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -2,21 +2,67 @@ name: release-publish on: push: - tags: - - '*-v*' + branches: + - main + +permissions: + contents: write # Required for pushing the Helm charts to the gh-pages branch + packages: write # Required for GHCR access + id-token: write # Required for signing jobs: release: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: - fetch-depth: 0 + fetch-depth: 0 # important for fetching all history to run comparison against + + - name: Fetch history + run: git fetch --prune - - name: Publish Helm charts - uses: stefanprodan/helm-gh-pages@0ad2bb377311d61ac04ad9eb6f252fb68e207260 # v1.7.0 + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Set up Helm + uses: azure/setup-helm@29960d0f5f19214b88e1d9ba750a9914ab0f1a2f # v4.0.0 with: - helm_version: 3.4.0 - token: "${{ secrets.REPO_GHA_PAT }}" + version: v3.14.1 + + - name: Run chart-releaser + uses: helm/chart-releaser-action@a917fd15b20e8b64b94d9158ad54cd6345335584 # v1.6.0 + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + CR_GENERATE_RELEASE_NOTES: true + + - name: Login to GitHub Container Registry + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0 + - name: Push charts to GHCR + env: + COSIGN_EXPERIMENTAL: 1 + # when filling gaps with previously released charts, cr would create + # nothing in .cr-release-packages/, and the original globbing character + # would be preserved, causing a non-zero exit. Set nullglob to fix this + run: | + shopt -s nullglob + for pkg in .cr-release-packages/*; do + if [ -z "${pkg:-}" ]; then + break + fi + helm push "${pkg}" oci://ghcr.io/"${GITHUB_REPOSITORY_OWNER}"/charts + file=${pkg##*/} + name=${file%-*} + version=${file%.*} + version=${version#*-} + cosign sign ghcr.io/"${GITHUB_REPOSITORY_OWNER}"/charts/"${name}":"${version}" + done diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml deleted file mode 100644 index 19dd3c12a..000000000 --- a/.github/workflows/release-tag.yml +++ /dev/null @@ -1,43 +0,0 @@ -# Create a tag when a PR on a release/v* branch is merged - -name: release-tag - -on: - pull_request: - types: - - closed - branches: - - main - -jobs: - tag: - runs-on: ubuntu-22.04 - steps: - - - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Temporarily disable "include administrators" branch protection - if: ${{ always() && github.ref == 'refs/heads/main' }} - id: disable_include_admins - uses: benjefferies/branch-protection-bot@af281f37de86139d1c7a27b91176b5dc1c2c827c # v1.1.2 - with: - access_token: ${{ secrets.REPO_GHA_PAT }} - branch: main - enforce_admins: false - - - name: Create tag - if: github.event.pull_request.merged == true && startsWith(${{ github.head_ref }}, "release/") - uses: christophebedard/tag-version-commit@57ffb155fc61c8ab098fcfa273469b532c1d4ce7 # v1.7.0 - with: - token: ${{ secrets.REPO_GHA_PAT }} - version_regex: '^Release ([a-z-]+-v[0-9]+\.[0-9]+\.[0-9]+)' - dry_run: false - - - name: Enable "include administrators" branch protection - uses: benjefferies/branch-protection-bot@af281f37de86139d1c7a27b91176b5dc1c2c827c # v1.1.2 - if: ${{ always() && github.ref == 'refs/heads/main' }} - with: - access_token: ${{ secrets.REPO_GHA_PAT }} - branch: main - enforce_admins: ${{ steps.disable_include_admins.outputs.initial_status }} diff --git a/RELEASE.md b/RELEASE.md index 79a3c69e9..18111f24f 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,7 +1,8 @@ -# How To Release +Release Process +=============== This repo contains two helm charts: [cloudnative-pg](./charts/cloudnative-pg) -and [cnpg-sandbox](./charts/cnpg-sandbox). Both the charts are available +and [cluster](./charts/cluster). Both the charts are available through a single [repository](https://cloudnative-pg.github.io/charts), but should be released separately as their versioning might be unlinked, and the latter depends on the former. @@ -11,59 +12,125 @@ release of the CloudNativePG operator. I.e. even if we have several release branches in CNPG, we will only target the most advanced point release (e.g. 1.17.1) -## How to release cloudnative-pg +## How to release the `cloudnative-pg` chart -In order to create a new release of the `cloudnative-pg` chart, -follow these steps: +In order to create a new release of the `cloudnative-pg` chart, follow these steps: -1. take note of the current value of the release: see `.version` - in `charts/cloudnative-pg/Chart.yaml` -1. decide which version to create, depending on the kind of jump of the - CloudNativePG release, following semver semantics. - For this document, let's call it `X.Y.Z` -1. create a branch named `release/cloudnative-pg-vX.Y.Z` and switch to it -1. update the `.version` in the [Chart.yaml](./charts/cloudnative-pg/Chart.yaml) file to `"X.Y.Z"` -1. update everything else as required, e.g. if releasing due to a new - cloudnative-pg version being released, you might want to update the - following: - 1. `.appVersion` in the [Chart.yaml](./charts/cloudnative-pg/Chart.yaml) file - 1. [crds.yaml](./charts/cloudnative-pg/templates/crds/crds.yaml), whose - content can be built using [kustomize](https://kustomize.io/) from the - cloudnative-pg repo using kustomize - [remoteBuild](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/remoteBuild.md) - running: `kustomize build - https://github.com/cloudnative-pg/cloudnative-pg/tree/release-1.16/config/helm/\?ref=v1.16.0`, - **take care to set the correct release branch and version as ref** - (v1.15.1 in the example - command). \ - It might be easier to run `kustomize build config/helm` from the - `cloudnative-pg` repo, with the desired release branch checked out, and - copy the result to `./charts/cloudnative-pg/templates/crds/crds.yaml`. - 1. NOTE: please keep the guards for `.Values.crds.create`, i.e. - `{{- if .Values.crds.create }}` and `{{- end }}` after you copy the CRD - into `templates/crds/crds.yaml`. - 1. to update the files in the - [templates](./charts/cloudnative-pg/templates) directory, you can diff - the previous CNPG release yaml against the new one, to find what - should be updated (e.g. `vimdiff - https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.15.0.yaml - https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.15.1.yaml`) \ - Or, from the `cloudnative-pg` repo, with the desired release branch checked out, - `vimdiff releases/cnpg-1.15.0.yaml releases/cnpg-1.15.1.yaml` - 1. update [values.yaml](./charts/cloudnative-pg/values.yaml) if needed - 1. NOTE: updating `values.yaml` just for the CNPG verision may not be - necessary, as the value should default to the `appVersion` in `Chart.yaml` -1. run `make docs schema` to regenerate the docs and the values schema in case it is needed -1. `git commit -S -s -m "Release cloudnative-pg-vX.Y.Z" --edit` and add all - the informations you wish below the commit message. -1. `git push --set-upstream origin release/cloudnative-pg-vX.Y.Z` -1. a PR named `Release cloudnative-pg-vX.Y.Z` will be automatically created -1. wait for all the checks to pass -1. two approvals are required in order to merge the PR, if you are a - maintainer approve the PR yourself and ask for another approval, otherwise - ask for two approvals directly. -1. merge the pr squashing all commits and **taking care to keep the commit - message to be `Release cloudnative-pg-vX.Y.Z`** -1. a tag `cloudnative-pg-vX.Y.Z` will be automatically created by an action, - which will ten trigger the release action, check they both are successful. -1. once done you should be able to run helm repo `helm repo add cnpg https://cloudnative-pg.github.io/charts; helm repo update; helm search repo cnpg` and be able to see the new version `vX.Y.Z` as `CHART VERSION` for `cloudnative-pg` +1. Take note of the current value of the release: see `.version` in `charts/cloudnative-pg/Chart.yaml` +2. Decide which version to create, depending on the kind of jump of the CloudNativePG release, following semver + semantics. For this document, let's call it `X.Y.Z` + ```bash + NEW_VERSION="X.Y.Z" + ``` +3. Create a branch named `release/cloudnative-pg-X.Y.Z` and switch to it: + ```bash + git checkout -b release/cluster-$NEW_VERSION + ``` +4. Update the `.version` in the [Chart.yaml](./charts/cloudnative-pg/Chart.yaml) file to `"X.Y.Z"` + ```bash + sed -i -E "s/^version: ([0-9]+.?)+/version: $APP_VERSION/" charts/cloudnative-pg/Chart.yaml + ``` +5. Update everything else as required, e.g. if releasing due to a new `cloudnative-pg` version being released, you might + want to update the following: + 1. `.appVersion` in the [Chart.yaml](./charts/cloudnative-pg/Chart.yaml) file + 2. [crds.yaml](./charts/cloudnative-pg/templates/crds/crds.yaml), which can be built using + [kustomize](https://kustomize.io/) from the `cloudnative-pg` repo using kustomize + [remoteBuild](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/remoteBuild.md) + running: + ```bash + VERSION=v1.16.0 + kustomize build https://github.com/cloudnative-pg/cloudnative-pg/tree/release-1.16/config/helm/\?ref=v1.16.0 + ``` + It might be easier to run `kustomize build config/helm` from the `cloudnative-pg` repo, with the desired release + branch checked out, and copy the result to `./charts/cloudnative-pg/templates/crds/crds.yaml`. + 3. NOTE: please keep the guards for `.Values.crds.create`, i.e. + `{{- if .Values.crds.create }}` and `{{- end }}` after you copy the CRD into `templates/crds/crds.yaml`. + 4. To update the files in the [templates](./charts/cloudnative-pg/templates) directory, you can diff the previous + CNPG release yaml against the new one, to find what should be updated (e.g. + ```bash + OLD_VERSION=1.15.0 + NEW_VERSION=1.15.1 + vimdiff \ + "https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-${OLD_VERSION}.yaml" \ + "https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-${NEW_VERSION}.yaml" + ``` + Or from the `cloudnative-pg` repo, with the desired release branch checked out: + ```bash + vimdiff releases/cnpg-1.15.0.yaml releases/cnpg-1.15.1.yaml + ``` + 5. Update [values.yaml](./charts/cloudnative-pg/values.yaml) if needed + 6. NOTE: updating `values.yaml` just for the CNPG version may not be necessary, as the value should default to the + `appVersion` in `Chart.yaml` +6. Run `make docs schema` to regenerate the docs and the values schema in case it is needed + ```bash + make docs schema + ``` +7. Commit and add the relevant information you wish in the commit message. + ```bash + git commit -S -s -m "Release cloudnative-pg-v$NEW_VERSION" --edit + ``` +8. Push the new branch + ```bash + git push --set-upstream origin release/cloudnative-pg-v$NEW_VERSION + ``` +9. A PR named `Release cloudnative-pg-X.Y.Z` should be automatically created +10. Wait for all the checks to pass +11. Two approvals are required in order to merge the PR, if you are a maintainer approve the PR yourself and ask for + another approval, otherwise ask for two approvals directly. +12. Merge the PR squashing all commits and **taking care to keep the commit message to be `Release cloudnative-pg-X.Y.Z`** +13. A release `cloudnative-pg-X.Y.Z` should be automatically created by an action, which will then trigger the release + action. Verify they both are successful. +14. Once done you should be able to run: + ```bash + helm repo add cnpg https://cloudnative-pg.github.io/charts + helm repo update + helm search repo cnpg + ``` + and be able to see the new version `X.Y.Z` as `CHART VERSION` for `cloudnative-pg` + +## How to release the `cluster` chart + +In order to create a new release of the `cluster` chart, follow these steps: + +1. Take note of the current value of the release: see `.version` in `charts/cluster/Chart.yaml` +2. Decide which version to create, depending on the kind of changes and backwards compatibility, following semver + semantics. For this document, let's call it `X.Y.Z` + ```bash + NEW_VERSION="X.Y.Z" + ``` +3. Create a branch: named `release/cluster-X.Y.Z` and switch to it + ```bash + git checkout -b release/cluster-$NEW_VERSION + ``` +4. Update the `.version` in the [Chart.yaml](./charts/cluster/Chart.yaml) file to `"X.Y.Z"` + ```bash + sed -i -E "s/^version: ([0-9]+.?)+/version: $APP_VERSION/" charts/cluster/Chart.yaml + ``` +5. Run `make docs schema` to regenerate the docs and the values schema in case it is needed + ```bash + make docs schema + ``` +6. Commit and add the relevant information you wish in the commit message. + ```bash + git commit -S -s -m "Release cluster-v$NEW_VERSION" --edit + ``` +7. Push the new branch + ```bash + git push --set-upstream origin release/cloudnative-pg-v$NEW_VERSION + ``` +8. A PR should be automatically created +9. Wait for all the checks to pass +10. Two approvals are required in order to merge the PR, if you are a + maintainer approve the PR yourself and ask for another approval, otherwise + ask for two approvals directly. +11. Merge the PR squashing all commits and **taking care to keep the commit + message to be `Release cluster-X.Y.Z`** +12. A release `cluster-X.Y.Z` should be automatically created by an action, which will ten trigger the release action. + Verify they both are successful. +13. Once done you should be able to run: + ```bash + helm repo add cnpg https://cloudnative-pg.github.io/charts + helm repo update + helm search repo cnpg + ``` + and be able to see the new version `X.Y.Z` as `CHART VERSION` for `cluster` diff --git a/dashboard.png b/dashboard.png deleted file mode 100644 index 2fd7e5c32df1805da3edb289c1c8ad2ab1c66c6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 798572 zcmb@ucU049+b*i(pd+X_BSk?#$5&CJfCZ73*bor~6$qWENDZM#FG&FZA$f&W98W$a(KZqGW)^JnZL*Uyiyl9UH?IcRA@ zps{2&H2Lm_Jso@A(UBBhVx(z$ut^1$+QOWD&rWTU; zo<$t`zUF4st^e@{*N${>^mH15zpY3SB=uBW_lwx$tcuy*dd7{;umeZ&W^msH&qX$~)k&Ti&4Hb7Orde9i zZvG=z{Lt`oKCZb9-+f>uQ+wg!f}W;wdJ3xLe`E{)wx%bvcdN>>pQJO+i%%wDsL0FC z!fpS|l{Br7L#r-#DhvDN@BJwIa_#8Itm;2t#K6HAK zL0ccMk^a!~{yz~a0V^|C+p}V^khbuJ68TnX_3D&!Wb6Oj<4%Wd{7L#VV7(R#ivNrq z?ASHiJ2Wvh>J?EJX!rA7W*MEYp}+IfmHRK0g2E2n5T;4n{()nM>^w#K0yAkLnNFF< zp}X%rPux8Hj|Ah;{TqIil{n5lnbcB@FI(1sc_gy^pE<9VID5PaB-LpCa=plghG}-i z*suCUZEGQYJN>Ece(>K+rGOcu7kH3xvlu}LQlxk%no>6dk!+GU>!Cg z9x=qY-DLMZ7(%1Mc}M4U$=zk(RGtrP8|cVtjJAbIzZfx?k3*)y;eC^|k51>tV6+q9 zm8KBZhe8F}&*EvVhrS1Pc?QM(ElX}lT%RD-ogXisQ!y>}I;xDfV0GTAQx@F|KS1t< z?k4+v3JKsI86D7QKfn4r6z&}n72xOU92}eV(H08A31hV3-_KVAlCoZy>Ti)at#Cdp zpSi-@vu`C++~KAc`}-@|-%RcePuJs?DVOy|Rqa*rpsrJRKgj2v75X)7s`oW%{kAjj zqIdcX-f*go3LxCjuhtBx9^03c0hKnV7v%a%3C3#y+NT7fFXGS7pzyc+izf4axZ5Uj zA2<=Ve??W6=%yC=`zy}hJzLg2=+*$c=wJvlrXM$|SaV$ZVxVN&>7zl%=4FHQd(d3( z@wxhu%kIvVx=y7vjM&e0w|%{{Nb%VL?;&y`{^eat>O$KEPI^K9`0rMN*?_N6~O|ujx{N3#BQ*#fBy4LLaaL<38a#tU4%AOM)KS>O- z|9wh-FJ~P1&r?4CeoB|@11m-ECIDOeWdmAX@*epbv_bQQJ87meNi+Gxh6^uNAP44D zg>ru{CcW(4=)7vqyR$y7lyjx_`=fea1Rm9>fA_u>ivLFEFicnx-K+YAFJ5@Nvaf`D z#^dSQlLnZrJ!uercM#eh3~4I*jDd1YqTd%(z5)=`<_<7GAj8h}Q*4yES8jZ?+IGtv?-yUPSvBw4C1c_sgW!tu;$YjG40=oM5*C z2T?C`7x^w>rd=<-?cDp$>-gsqZmEfxVRrXr?4UMa{ke*2>4#aDxrPz_8)c~aTcsvO z@Q01zkubx#r%9Kfi|P}C*EeLp&Bf<*uHHP2`}OTKZ=LHucVn3`GUNZRsY(BDrpEk5 zZ2Mjkl8 zr_c!Mb~jN9BFg^sjRS0KNZxm=+x4-p=LEWT2mwTu#m@3&!@?)_LhBy2Unac!_Nxw_ z5IZd2IKOE=K^66dC|)Z61ww|%9#n@^{f-YAZkOG3K2J*CX*9takNMY1EVC6nbxJvy zqX& zUbMY^k@GbpK3%HgmMHgW(#Lq#y)exIHlcAK-t zn6liBxv4SJDQRQHw&YDy^;2yf;SD`cUhQjZ2pnlB>R$pjr^m-n?JIjSI|N1>Bu1c`mXllUQe87OMrj|r;7rirE{#=|jzK=}|77gL3yZLFt z53+-G4PV-`$Lkht)!fN-4Tk;TsNNj(WQ+D#k{)qpB67OzMr@#SbMO1^qa?O*#QVoQ zcCSTVaCy^`dk{O_M*0~+t0cP(c%QyS(=uJYq3_<1h;EKXa%LT8my0P9zp%Vsj{^;f zHm1A4w##lJzc5Idx=ic->|cxDFG0J$yN{e&74eUW63OQ+teg{`g^jX%9ai8`hk)2B zO?aq@cTHGbh9M%zBtqjSYB6WjFt{`MEHw)sco61Rktpq=q0PaD7HQc^pNbm!8VdC1 zM0o0)<8RY;WcnjUdH4#ioWgzcVR4a3%O!eOUcZ8d)y@~bvs{kw9nJHcXCzQEPjwx$ zb{c*0EpSp3!P2nKJDl;sg@3zayTDMrJwoV%E`aN*MOB&@!=J=N^?J5=lb7#Vi=Otx z(1ky@AeByC*0E(t0bez~q4Fh(O(FMs^yW!IIyJam_QJB$&wsEAZ?0oar=dxOuZk~) z`9GV+%S)SiL)vF#5XrJtaoiz!jb_1w>LZVE_82Grb0ICz?a)fm!tX|sg*HgUOkAA& z7pD>TW>EnHMxu#15|LuK^9y}l|CLV>ux*_DjJ*;rL(<%L>D7={kxgFk2HjoP1r2^Y zOI|)$=+2Q^hCt-@(U1ae&}AH1%xX_D@nDwS9kD?RiyqPaPyux_d~3SSVIK;bKjL7b zVPnhj>)f*Y#Nj3q(IEtP0fTKoFSO((AS%IwL}-A+KGE+nT&>B!1cWSDwCLy!67zF- zVtAR`x08qw4b*bT5Zq2D4~Uz7?!yjl0}t9PG`DR4S?V!ChljA_yRD-xoN#9nsP5Q} z&#UHvLxIDi#p9zzRXQQ>KN;EA)?Q=T-R)KUb$<%BQ%NJdhUk^O`!X})<75e87WvlxKs0FC z+qz`@natAA@-JQS3m&6wdM-1++Rvhzx}Z=C0%$=Xy|9Z4A?bW4yP_ zFDrQUR3ycRoV1yv%{g96e>L0kaNF~Q?XN|=kY&eZZ&}Wr4)L8CB4`VL#2dY9LI9Y; zjMBzF`J2wGWcDi@^s-I_jvZ90(gMNl_K}#PTb@2f#?DRt%jTtsL3EQ7-zEM`AmYp(Kt2@7gkDJHJ!sgr3q_3xLo`kFvMTy5b z#9v~UCGm==p9yDpNO~@r;SMvZMdcU<23tZ-wuPKu-|G`JKj=V<=-`OLi|Aj7$POz; z`DeO!9?}4!ywv|VhbEGy4p%qPjSsEm4&~BFK{a5&W+ImG+;};#5bvtq0-5xRlOmS zJer$PE05_W+&+Nsa)ZnfeFds!JA2nx-!Km)p;9%Ir1N>U?B}quF@ZKFQn#t-tVgeY z|1zlT@aB!yFO0D+i3j|C7B|YI9q~t3X7gc%&i1{EVa>~ZRNaQh<8g9UZAa~AKS1jk zoh^{=gpq31KSoO(7HqilUx*t#m{$X~4SjhY_%F6F(=2~7$$+Ykk_vk^(}xYn1ch?9sl zfm5kIR0N4&o*=6tYPqu_T|SdX{Siy?NN9VilN0H3O`wj%l@l&}1xX0j8MN^%Z8omG zzIvyFGTyYm33?5blhK%`Vf)sbJP{L@)|yCw_?mBf)zZZTOZFwE1SsU_$JD^vL=VlI})_Cq~40#9I zdN!tj{%Dku_z@e0`MN95vy1Yt9(eb9+ESBT(vB|O#+z3xon%c=A?E!hj1&rc{+pku zAd9)yQK1!b6s7ox0l5}$^$YbR>QUm?>gemF;TSjx#vfG9tT*v?`vo=OV)puLU8i53 zmNff#Q(Ax2;5Ns;RddA)q3;gc{XAZtm*4RkBBK9p@-5R!2=r=p_NAxe`)N>l zX>loFE>Sas)w%7KJcrXZT$o2e_V@QKT#$DyMyUrUzr5AV`gdEFvbNPBaN7 znl0kMj}@*6tp~IiaHa$88T%%(_*fRH>ltn{Lv!nKW|mcEnS3(Li0)Tp>>@$Hv2DfZ z2Z~zMxw_8Pjr1NDrQhpswdYj0d6Q92ec_D+g7dXOp*!~C=uvzdA0nE^PN7WPyKp#u z5l+_pOi=nBuQwkRJe(*D5L8U&oxMj$Lr_GD*yKYVkN*OObJc3ieFQWI{)XNBFRO13 z9p6EFq^l}631{sTED4LJyjS)kca%?4_6p5_o@{VA9zVj^E-`9=y@F&M`c>>e4)?ok zA1Jqa;+s6};LBfBZ});VYlYpT z(N}V8p>NdFtg5EjsT0Z_tD$Xh`k_TdO06kqu)hVQ`J*PIB@+&MP2d$fdA%MV=1=1@ zNX%`GJa?opQDBOq)sB~B2W*B4O+v}%rlh{dy*6>LOi}YYG&c+M>H|C%K9M+5&X1xR z9Pju`P41qgj8X}LXSk0aGJ!CleD9Ld`91)AfT)IRiEa0AI1y=_I4` zZI|zfU1-r8OVpCq$1L}H8)w)CeM>O}Zz=cW^*wf!|3N5lSm;QF#D1|9jKa0$y!opO~>OKDCTHOP4?`N1n)5Nyr)EE>J9 zzx%+(u`p(dZA;mPoNd&p%-#sdo|Tnpnqp^$g7i?(GSKlnpb(6Gu^D^vUzI9@h7vue zq4!KfS6UY8w3@CTw7PddCKT$zRPGLtqvFuUoZ-s!T;Utn1x^DKnlVOU>^~kc7QCnF z**vv!7TR)(SO4vSMn9QK7~dt0WxZB!g^xG_jgGpe;9Q>5w{$m9!<8N4ej{he<*=cW zFsVT}2mJNRw-FHsJy&=Iu6GE_{Yqb1>}cgXLVQH*#=J!AQK1KLYyo67UF$w&a0Sf~ zK^UQCDe<(E3YT^6`%+@K-Dz!{(W32DgNp;{dcW_k4p9oVRWDT(-!1y~lqIO$q22ux z1{VF`h*QI{gCQ#m=caf^f|z+8G=uhVPM&nxrj2Q+v*P{g)Sx9TAL7!S9fdX4BGRWm z*NiTGwS{b<<^x}^4EPp))lk}Y{4ce$S(LRRND`5;*2)mYfqEFw@=WF;$z`&wli_V5 zWwiUUIjKEcAr7g#c4}51^CbWceOaMasRmR}WaNug&V4K|1A>%%iDR}-0<=`GraK0M zPKhRP%a9#$;q7v2_S(!1BoyC`K3i9IQ&bgDy;1fcWhb+yVPdOcSp-Fw*mC9h2{Zmg zX3B25ifc%&OXX6m5k%iGyj6)_K7ogm_fk+Z;wlh!_1-C*%JYKU4Vz#pt}wwYBjU95o^eu=t8-b#HY((q-yi@O4`p!MB*tY?RX!gdPdXXM7zHi6Yq?or+3zn6NeL%i z&?Dri+Cf+CwSM3V8ugx}wn?V+wNb!0!{%_*XmdL#kFZ7jtzAbnVbaW@uX~%v1H)6d zb}==LX=NtfUmZwS{bARre|Cl|2W#;mof(xL(38v|XjosEe@0a)i@xxc9_Xa^)6RY5%{q03ph69)aMZi5O1O(*^76FUfIed41%fIeq&pHydht z8b-I+qPK;V=^kz3(+?_da=4=f4+&+KpMO@P^C~xA|0$So%(;G&24D`EENU7=nxP)_ z@kvE$R6IKPj17szig`9kYo~^9Y%wq~R6D=i=B1)!tF_u|vbcQP+72int`dGh3_5KDB*t?tp5MN1?82}if~o-x^>>uzF|oLM@~JD$SA96fyng_m5wVt@Go^A4QK?M#kv^P^{o8S z5dckV*|WVUAwtY$#hj>{uC>RCi^rM;X5XUuZ2E;{P1rFuYGhWBJ>;LaQ{Gd940gc}GWO;}+ZB&Y5TYFFWPPvpwQ`_P>OKpt&E?0V z=g)hCa#lU5te&w6v0t`8 zsDPb1EWDIagB^;UeqmR*xF9Se%Clo|iWr<}vz7kGlbiXilBCRuoov!H1loXS4i72r zY{!#&MOBU;M`=SRnF=B!4&l$zmvjWKDb6;nsf|HT|0GZpX@?4%(A0<9F|-#Qs@t}7 z9?%eUF+|UtBvxgTd|m-oYTl*F3+avjSwkQ4`;Mx^(=b8?C<_n6KxyQ*lo8y1Z zIICkxiI%P!w!(ezAQuCx^*<}D_?UyJT3e-($yLhu%#NZ>pr%7j`FCIN9%=bE0@YcN zY^0hV7h8>3!<#B&U^$6lLyRZ_q%{#i-X#RZWlv!1eHYq?FA*-L_2uOO4 z6({`ZR^Ahv8J~MA^~Z|p-{ys-GEkK9j#dwO_mP~T-8Do96Xlxzqee>(!~GE5d1;nM zcu!MQ(posR-^qF|e2e6ZC!_Ctf!TN-)>qvsJyd~Zj_PEYM6eahiwDmj+9RxJXJ*&gOGcemXqV5LgG6we0#! ztP=0|6P|e(?sO=xmD!edcI#T81eG)sJhDW3X+Q?nn0C=y$>fi7d{*>WQKB*Ec(X>@cg|JzpM z!mU!@_r}bapQgYZC*k;6@6nHt}XRmB-)m}28uHuBO#Pl_sDX7msJ za7o2^01SCq8`Z_Gh%E<8#u3@;6MR3Nz{{-aU8*(X?c0vy_^~c(sr> z&k#sn#c0X{w9zrsp!`9-?ndZvO#|HE3bp)36(2rhO^ujch_~MBDspnco${j>jSuxl zj*=|7{92y*C_y^|`_0&ie!c))?7L7-GdCL#kC@U#@%H3hqryK-v+}vX+|{&2Ki% z8i)tyl%Dqn1NfNz!0YvTkS)jILt_hB@>`ugw?Q4NMTbgnb<})$#;TpnYrP2cxtj&l z;Dm0a$m@08{E;D;#wVypf90?7=(SGoZG`K=PmG_(owNIXOnOZ}ASXLqy1Ys!ZH>2+ z)~jhVVJ^Fl*(`8oGHF}O^FQaI73=#Phmo&!#Z^K8Xd_mX0%bC6ubBz#<^_`uL(elu z<-|3kui5pocgUMgCiHkkqVthYhIs}nwKv-c71mOnsAHaCiaJ^o) z5#B)y5969kw9i2kyR#!Ug?~&>?YkOS<>+Kipn}LQg}JC z)OEoIiyY<6*WaWh8&@ta$D^|-&E$@_4ox=H4?u~I=5PizI|xSD2!41hmi5S^KB{g#~R^B$LrL`6M+Kz zTmzJFn4_8?4Cff_-Y9)cG^Ai@Xr+SkIrv||cJ6r@u6bol`<82d3dT|K(?;CAPvx!B`UM3$5jxVqKEZ{g>aHr`mZ_tIqY8!!GR*S2w^&v0;%!(wE64 zWoE0ysvJV{D`wmPtfDSrb3s>heJxNx;Nqt2W9PO9i9Uczz zN2>h*ys8c9ytTJ1I~@LudDE0d3yu;|g$EGS^iKPb+|rQO*Ymz(O}90N-?XoaVANA% zlIhCLo)A=&NV<{|A0NUd3dMSxsIUN$1b@zoT^)KSFRSKb`MH1lBT%w_JI`GBmaUbz zUQr?12dhjkE!CRn)%sWOlb2d=ovObPCQTtL5fnFAg8zILMYmyZQ#p}}B7x<1tO~AQ z`~;O3&sWp#(;YANlvlgyHP}yHpxB?jdgd5zns zzjz}F20(RxFP7<@ujl6tBv107tbU#Zsp7oE0V#&N$@PR@ZaK^9cNBWE8rgyL+kjcA zshOC-Tohv3gr^gdY=hat7*4T5~JAqewJZuuB2eWWuUvC0C~^uRxKTyN5EAA-~0j0-_<^LVI=e$vnt zY{>{uz&873qr>Z&*ASNvWW}>1;6@BVo;6&rBDP&==NF7PyOeI7i9-YUMvx6ea%+T1 z`E{jeaG>-^e<3~*$q<;2MJT`-V|CLr{P%J)@w%a<8MsIJT={HxhsLJ3W56@W%ba3t zz|qEh@Zd(-03RtzOB6&&czJSyL#kriC%qh7Y(5DGXnc%)%`M%?u#v?gh@}5U9_g4X zX7%?I6B!uw60(Jsr0-A;{FNzWPZ8LvfgpEE^6urgDw?;jEaO+fywUuH|4ckUK_Hfl?_F^?^vOe#@%~=M z6?L)gjc+}Tsn0$gZ7e4M&-2y`pp0@mHO#{Mj?@R(Kfn1wODLBjwOpAQRr27WJ;N%$(f@wlte{1Vh1 zW@0F|*=B-Xc($d)2D9!K9^_Zjoq`JRVnw956nI;Y8E{k6$_nTY;-YMe$U2+Bu#w;^ z`sKWNeQtbW>K1xKToVDtb)_&OKHdNxLxjLSDTN+N!N!atdHNWBAdq7al4}De5wz8r zs-8!ekbU0;h1qU^a<`lY9+UL90aiU$8+$|BQ6gLpC6OXx7hc3VajZKe+bTJbx-g%Q z&HN*mNX{?a_Av>6TKR=e1S{u`=lt`G?gsmc_OhS1(tY;71ym5NX&Kyjw6?XWGA6Ji zoj^DS#Z2arIvx9X5(z=xv0##x5mKgGOoD>#_E=46;of>vO3fEI0Lu3x1uYmSA5VbR z_~i_O8cLiC7Sj}@<2BJ9kb|+?-9`(((@3{!* z(rHT0)oI@dxYpUc@`V?8qeD5xOIoNE`WYi$2AUX`A4pUtU7xM=(6;IXjAVd9h<^D) zy_Cm_8JeU$Ru9}p^<-I%>XiBjEJr(s3$HX@N**prk#@dOd3(8`XRhBnE_xMIHneZ0 zNO*g*%)ofLNQjC2D&}Nq=LuigPTFW8DX0A2LnJVGjb3p%VEj#g)%f*+*w33yngeX# zc~INgodXfIkTzf+)XVs-rFMBFl-Gq4K}}fNpLQ=Z3xMa7Y_NNs>fWcqu}A=PC%(}L zAA>+x0-(lXyv4<-KuJ7x!bXoK-!Ar^t8cFUM#Sc%7Uz(DN$0~24wyh#1b3EQ-G#7E zJ1$lc?9?w`QWv}6g+oCo)`uRt4=yF&>)B9p}1>RtWBXw zb!+Z1Xi3^vHPnVGpWT(sa|OWw>_W0_r&+*tc7t;+*Pol578fJc$!RFpnr%jK%U&Ll zR(s8_e7q@A3deiDd&-195b@aIPMmiTH#j66k;A2g-BCM#R5gi?RYlNJqsWz>&oD|;L=ZlQvhKfrT3$Q<=?i?`M z^7f9j8C4PNSOmYoxyB-|@pcu5Lx1^y@pkO;zL`x=zI3{5?@^QjjP?yj+91}j0nl0< zKpjrb)Lx;NpD(;|rCyY*4%xp2Z6`1Fwbx~~@tC!zAd~pW#m5Vdy)6)unM*BuD^s1q z=|xIQf%Vig_x03`Ow3*it7 z8hLb7SK@kp!G>}TIqwd*O;58-nmI-Oc2&Y%Q$EZ)Jh=k8A`CM|i7OX&(-H!~Y8YDx z6)Azw#ky=}S9TDY;-dqx6;)Dve~&f*KJwzxy&jej5tfx}x{IWf--AC1Uj3@r)*zzT zJ#^&Gjd;Vhb}@G2q~0@6032D`tWIx=>Rzwzb9{Su-*C$L~hlegus)bDqIJY-Oj6&&b|v7I0us>r*E;koNY2{`3&c% z9vgMBN%Z`06*T;@OmAN*!v{jNZz0g?cp_KH} z>Eq1K)mL)>laNp-nWXV^MRAf$n}%NxGZXbqzPYQjNmbK~m-BthKzYm=m19HPOJwbA zh;-F=RN0~MS$n_qN)Dcc(->0xUj&`8whr&(>LhF{RBTI6ZmP)?1<4KyY|?BFi$-cg zPd^;y3>?}Q-3Z&QuAU?E?r5*0L~M;1)op%lV;Pl^)M(KcCW*1*{;&E=<-hBBENR-- zSwBy3)}%G}uRZMq+65@CVZSL1>$g9b$sq&G)oNbs5_8g>(=0GA5zXe@X@Q`XO%m>j zjTxf80&FW`bv-5nd-DEwd+QT71yA=Zs5v>X8pC_HPSwjt>>nq?X694fHpSloYE87k z`!^XYp+u+ZlX@sohsOm%Cp5VWhko9Cl0p}&sH>Ye6SYh?|}e9cAWFoJPuM+jMRYSC3@^!iD$TQ#Ny zGH!bl%$s?--|#_LMZ<77W5DUmi#IQeGO#@8D>Iz9Xx&TjotMZZdiK?r8nu^OvUgDZ zw5CXGu9g!V@s#PY`9H_?LLq?K*{)W#3*mAF_@SQ}K~gIj$5g*Zl1A}RYuSL>NkKBV+vOD~aJx0- zEr5r9?u}fTPWRd^K4$DdS!jbv7cbEyS4UgTyK>_^dehEEZ&EbdzR|nPEjAwLVa8d_ z+>3RAOhWU$qmbLRw`QUZm#caM_&IVnjpT*_O!)^+qs*j5dQM=fGypr(S|{YskL#Jj z=x;Id;<08~WF0M!(hFY+TXImfb<__4?>-fYuAsrpKX*F4tB}3hJ5}Em;HW<2!opXn zm|oxhkv}(nlQ(_^e)54R^b@0$;54EP3Jjmt%~RX$(zM(jL{iE8jr%CwyDlX=e4_~e z!^^U%TD+@_DslF8=?>|KqPmw{E9hxGxWc?FUes%PA@qIeMuhlb0L$F814z zJIAT?2De7V1QEiWxhs^j$i*i5@w!r@C=G-W?$~enFuA9!)tg)gN&x8&QQb83_VyoZ zVvac9@vxtEN=djx_o{q~C$T1Gq+S)rG6=zF7dzt)2xd*GkGtAGnP(sCW&sn>1u0=N z@z*zhkOzLtby%$ChFzvyoh+xlZ#Lc=i=tKxGo^G3?LVH?GNFw}qV->I@Y;KS7A4tj zrXm@E6pbAvxz=+as~^d2F25rlHrg}|?GH&5}zr=RxN$FN-S<Yp>_GI;dc-ONuV%DvxRFt`sp>L0`y zW2QPmX2Z`^hA~ejb|_t6E{8}SgW7^HJ=H>NGD#=zY`Qec5+p6YOf9XSl9cMoKU$>& z4liAm3YvgAb-WanT|2K^ocE2(C6j*E%v?WiEBmP;1R&e(?xeQ2q|WWS6$W5~y^{s7 zM3;bZO-z1BzO}z;mz>NlkXF&X32XKF0U}}}S!d9JPh9yDai*D^D{8I|7tZk9$S2|1 z(02piT=$`OzU6JjYd+IsuSLS9>`LiCu}ETZqdn(n$A8|$;k@@SlNkKhL(MbWUMn9(($GKALYN*?fvPGQ;ZsjWn+mHltkyhT`BG(r5SX^$eF8Z2@n}MSTuc;H zQavaw*}P5I0%3Ay!#_Wn3Rry{HWhh3jp;2HG?2JPn=5^K1jrn~Ou{v0zm7BV z7nkTvpd3B$%&it>bt-XrcP0FMt^78)T<_s&2Xd8D>DReH{C?lDd}PKqZ#R&Q?E72} z4;YrHeqgOJNM*sm?BdMYH{!r4joK>TT4q^8T|ZvT)XTlrV0>egS$OEX`2W)n zTD=mXQS@P9HQKt~166;A)?xEpZNiok!y6QgXW_GRT5U8m6ymRvi}Iug4u^dE)VTlE z<^(yMru>sNam7yrAM;Zgcn^wOtl5`>@pMPf*4Sp?i zs{3>$dr0%}5hTEF-IU~^gzpzLErBc;b}pg%gj8Pph;!5He!*``s2{Uw>GSsR)`Vkr6NeI1E2gPC_;FWvFG2?9`*YkjS^xBMco{I?ch zw48pw{gNouETLy!I=aR!hTaMpO{GRYQ;^POVI2aulou0eO;NS|)izQQsQ%5<9}oc-cHs;Cad^q%he9Kwobt_X5)lisurH2}sfSRFp9d ztveT066Dj@0edvS&h?DBNlt1zIcv%z;Vu_9_zs#X{OJ5#w~ME4KNOhxqKaSa)~$KN z-;NYa8xo~`NZ#e~;6K-$F#^{dwO5s#v`uPO74k5KQ&~Bs-EGnr>{&%L%*AVoW1RDb ziL+z^jnF&Yn}>7FLYPr~v1c^EOF`S4t_4|uY`g0jS=O8Z(7GZIo%|=MJ=aVmPn@#& zPA>q-8L#^n{VL_m65pcHwJEfPlsIo}K*Ro72Ny{om0u41WNT)3fnrzT)uAD>`r)o~ zFS9eXuTtH}D~^g6-Pqap!UTP-s1kp)7wS=7XGB_VQ|-GBr1IXsff0%uLv%X~Xc z_>wnqkKe@+r|CZ(y&Zg$pME{islt2;j`o-a{M(?Fj% z3>Wl5C9(4p>A9(g#_IfdxRxk_^Xszd@>6fQ{d^Ld3|(>W4zXqR=Tt7_iDzF-k&TcP za13hrROh++mp2NC@B!|5(Bo&=Y)qznz+65JH{_1!%FPTtOkVLZu)+EJhB&*75W}Xo zQ8-Uqav87cy*%DVYnt&Nx`el7SzG;X#lr}O@M0xsalzdTgoL9rSI16szL54!+rC57a7W2`>FI)w+ipdiWWnGbbB zxaVnwg1J$^(KWaZdP$8#A1!PWJ$!hv010*-Kq_US6Tj`#zMeZxp|a_LAl!<#r&jc= z-ABpcgN4)1dykdjdXe%MMtQL$s5SZ&GqM1f?(`2&?Ho0bAcj`I<}uM^b#cFe(o>MP z74{GF4hxy;Hn__ebV5R;ApK{oK(etA$Trc};pVhw%@|-tpj1iRFR{*zCe9lupI)Zg z&qaBJT^%0b#2_7Bl2f%2k|$NUoK3Q)J=GE^0~?NdhC7RFc*Y1`!6dc`b-sF^foM>U zDj=P=>vtj5t_?BDx4#^xwHEd&yR%MFg))n)!W{5l$cH<94AyGkG~#{t(1r3*K>s>X(5>i&pcEo}5T~+5;bu9)^~% z0!bI_mDNcQc5N=(6Ec{a^wA%jQ*_+wG z^c!O5x?Y}gVLt<^ZphxUL8(`Cu=MrS{!LoqUr#x_+KW_rb1kx6uYCJ++(8vL)}@)6 z$9Z@xP%T>Z3@&zQTSw^SMrqeQNJy`FngwMwgf2XMx>VlGKNEB$0V+0Lp;2f}gGljW zd20+KOMZ_p&luaW!^TteZ6+%?ykN+|a z%%dsy*t=Oxmj>If9A!Iasp~Z_hkrCOw!vEa*}t1LaxUom7T>wr0?8QM3HrUzgU*pP z?rEYYsvWT_k=ERCUOg7ACyIKXV%S(qlP*KbB2aSIB+%ZILndG{t>R|)F=NS zm{k?ycGSaKAHvG1W93F@9x2AQgB!lw9*Y_+A`)U|MugZsjA;3;Ioa3O6**v`X0rfj zOQB(8Q{JbLo<|lKc}AJN0|ysOYrYo%j(oOEG`vo_x%wbi#>e_A1;L@F#p zN_DRAwn;o$7M&Dub#Jb)?jijVk1`va9!GzakhQ$~TAfYErH;}Z%+bLIvaFe?W6@)O zRJi$hEJwYjDez74eD4tzBd`;jn$hW1dEE|8_uW{AG zW8Y^B#FLj!IKIjY8|(RaeW<569BW_mV4M25Zy4zW#eTa5y+iJOUrt^@GTQ@$@-)&b zD{Z~({B!v;9uYOd(7$M_tFPohx+GF!*e+B)6oS?jEZWFNz8~~_xBpxYIJn~}-)$`7 z#`X`E#_nW^6Em+UZKF$ic*~0Z;dVht=ve20aR}opo`O>Qv`_AQdQWi; zY`VxhQR_!sXlm!TDBY422E+_DprY`8v>FB~_A0h0h%Pi^ZxSiR+MUvkF5}NVAGD{R z?>aF}E~y3h*f0uI^ksA;8S&CuY;o%^XcyLRIgxj-RIdci+a-5={&+rapKIkuzAyIb z0082(-5K=;Q4W5Z%6h+AUDT zdTH!PKnJ)+Rl5-UuUOsp&cbUIJ5Vqo@}12~oiQ!)cY9BdeR&UR-R_JBO&Ik3235mn z9K$oF7N$Iy(#u!ZS)U=EI8%4nQ+=dd+HCKQV+-2VwdAyrHvLeUS}bRjtk#~n_*ck@ z4}LG-st#te3M7Ay`ky#wq8ypDamV9rEd~v_ereDnH5u&#?nnypHFeZKu~39;bS9!JBlF$cw_(y)&%#MzdM$8Y>T zrS)WO^iBE!=hS#;iy+r5rUwAzU%J!=RL9J|vYkTVCQe37ylzOGdAvLCkgd1e`VJri z1kQykjcB_jdGGnp!Q-{{A)h~Od>nhtjAPP+Kc)9yL-MO@`xuhy-x)CTsI$7ri9bi6 z8ZYZyHZ7)C$UHNUTp zQu<$88b*CHqCNlI((v0F_Tz;ZevIz=#t)Wp=aqB8Xxa23_A>D%o1%AlKJ;)|6Q9SS z9OvG7ZT?R8zfbjt+H9!y>JHna2;N@$Snytz6jDx-9*RXhtq~B|HMW3B)@)xiC z;s%`fb#%P|TTL_^FOi%!naZGXv7| z|NB$?y`48|i2t9fU)uw)cJ&MYm#Yt0yL!J*O@xUS@6nk#RMgus7}0n{BI-EaW-}TB zUtj;v9Zyg2UBcyp*YfceAAe)tQJv$PTF@2r*@nj~`g8lq$Jn<4;6E35T&Su!-c7@8 z-71|qLnEPWBD$7{?0H*{aSi6i6aILT^Og%)>+>&*wbGXrB!6#_js9n!KOZ}yvHJUj z`r2df{qwO)8`ddG9Aag7hAGpUd}Yjm4*u?2$NrDKB0fHM%j_3{@l4;1$;TGam;3aV z75;L3J4jYsIiZ%b<{ZgW^@EQGH z0K@iFvzN#IK!&la9eoE^_v~96tmi<1MQmRa0qE!-S))=b+e1-~U+?GejEMN(8!gIC zsruSV#TL~9H|)mThVGD%cayrSqGk;D*Z*UM&#%DVE4_ZLDc`HW9vnsYzr6>?je43q zAsI_(7KIfD-5t#j_pGEo27*C4-3yfJZPs>p49k45$S@+{c8Zz0paCbDC<-jlvU*nN}vTO^5pt*rd_CcV!YTf{9C*{PpC z`r=ia;bTjA()hUlhqpJ6hkAYg$3rMf>SPI7Ix1x^Th`%pN+nAvJ5z-0%08GeI#S6} zvSb^jP?pIyc4H|!BiqPeNDKypvCJ@LzArkx&-=U|zt89UeLFpV|M_FQUia&|ukF6B z=k>hTa%?P`-c4@6{Srj*3Bs5Hh4s7Vi6Btb#u2|nf}@#OGo(a5B0hmu6PLNoab))i zWJ;6X)Uy=&9SCN_|J@w-|5TfOqNuYN15h5Kk$kGJ0?vI-{27ncDcvTZ0|Z!m?ZTdd zs`T8qWw|1Ix}1Ko|0$e@b)lZ2e2n+|Gyz7*v(&i9q<~!#cK!X|o+#{yB9|W;6><3x z&*1{N#DA4Z{@Cmvp5eR5dF{ux!%JgG=94|v{#uEAj3>G+ce2F(E1HJCKl0yR6zq_p zXB8CTnqsghZxrxs3~v^jbYq2ecdqO|bOdh4l?q;p83Pya@$9GbJ&0|+3i>Iud`Bp4 zd+O1gkV%d$=Wu{E39M^({jlE!-rPEZg{RefTOC_u!GhaeJ8T+*!bTSU~k`m4S6SVGD#y%FU zp&>#!>PsFa(W(EpFxb_GUr!#x)*bp8x-lmFo$?O^JeCV)wlWlM4Vd7k))Z zP@0ds^&S6KJNOg$3HPuC12lsFMg*CLe;Mp_|L4%J)&Z_=hqSSpqAYs6U;ENIvGYGc z6sNlO)C02JX4(8X>%9BxwmN8711!SpQBuraBdf!;ENl-d9xKG{lao7i72NYP9RJ-^ z_%@8{Fs=Qt?QshBT)C#wbkI-H=3Ibq)9llgh`o#e1)qL)miY#ipPik&I%WWa^J8IiUn^87kHv|7kcU?hR2R&Qj~PX z?#d{g6iRt>ue}NpXyur0nk3)Bm(EifaPcg@y`$B16Bp)m=?nUpi?bGq^m~`q zXhU>Y4#`L4yEUtX!3tgii2W^_!76Zp`lK^EVWq7fD~7nZ9t z(pqb$g*VuCwL+z?J<@cWSjMM-{Eudw)s{2Je%lsO!D_$x3Bhg-%|GZu>t6ao0#T2f zPR<03Zgt?=Fguuzscqoj_bs1{f*9Ix>be@fK3$13n%ogqLj7MSh{%AL18Y zDuG?jh<3!=O z0yWKY=4kq{NrHoHo>Q9V3whAR6`rjJyURsmKkfLIyz8bCEY+d-YMu$pr`)<3ZMx#W zX>@8x7SYDLT^wGL;t7s#>uidT5(KZ)1+Q}w^HuQgJOd?m{a(xVogm6SjyyV!d?V1E zo3?lFBTLCQ#$9{l$5cs;ANcJXtMfHIxtN-$;HS(&AiO80RtbDajMy582pq)wzRPy7QL zapwNdo$2+1URPsgjoQ!qZOO7r=bHT3Ites5|HXT7q>z}Qf7ZmA0Zd|7N$44t_hw_t z0SdyGo9dGbN0d+)n$Vtn-tG{Cf}?2*((uPUS+#~3aK$7 zhI?van>c1_`kf^B?kv?}g`(6RjMZF_@~*gsNwL&A@7OWt1+*1S)|cB!C)u#;V&(El zAdb)NcNOVfx#6p?2@+U+MvT(NliFRFQ;G7iaqYPXVQaB~ILW!W6&&PIgSbT?_6mj;Y$}Lsy6xZFypINwIQg5W-6u+B5zh zrS`OK+L{N%NCD)dEt?CAF-~5|1i9VPSt}U7fs^aeYX@w9!yMXk6~a_A#3ZGz)AuNm zT-C1CE|{b8=vMT5V8(qhdrvnos$tj2<%e)7yIhZ6AKm=5oH6%>(*?qiS(SHjqXqG6 zdAbVHwinJ0Z-73K>|6bU*zx}KK_4t>FQbc5Z_Wv}AZ)T~$sc%79U>=aGhOZ47wqwo zM77jBzfwg6ht+W6>)N(&WKB~x zK4`WMC>*J%KI}^KeDb}?uWJ^8?&!eVh~yC*+UPALC$H^>l-avlx>hE>U9d%1MNrZ2 z`oSJt8L8Z=(5}X++{@zNB?^s<_v4VT?sE#Yf;L)a>og=QmsK1)ulLXaXavS6%&Vm_@o|Vzj;U1f7RUu{z?&W0&lvVsAp=- z`q#?Z8xpj=2}CgL7HT2Dy61XSHUG9xAYic(A#gpT4gt| zj{9g+s}3Q;Wu1!n(jG90-e^xqow_m{yh<=0zBjM0h}a??4~eO&1*3ZI#pmD1>bb{& z-or;iAKrBXAO*^)(kf1D)@R>*EN(kQ0}c+RU#G-3kMQiy$-x(HZC}~2m+=euV@%M} z@Y;!*P+Qs5JUBD!eihgzgWHx)DxW#yzxfX4t&vU#dC)tJs%%3d9=dOul9(2TIx$r^ z#y8kr2eaW-xUizVLx8ozygH}qR-HR+8OAy}aCD0k>$y0&Fl&{5&(h-3Ceo5Dl6~;O zYB@D|Rqn3nwB?jf$J3jJWR0ZrJhb=} zzfN2K@e7H29Z*_J_{P9j6OpJ|-gN)Zq0a#M%KzaFCTC&l&Wztn$sBSKb*;r|^|}Fh zaQc#Q_dGDh@VRvm7>)KxNGv4n zj2|$>G#L=%sYV{gutkp9s|(V(i+8Rg`dwk7;P}}D-yOG(qYc8ir)I+ixVfe}-D7Ar ziyEFwzzB{(%$B`^d?G0X!!1PZw;s3xr<2|buZMhVDn;@@%Urr7JDs%T9c}&31vuVK zcP*GzuT1akS6VmwvfpfU_J%3;b2S!!Xd|ehAk)<^PhGIY$U!BYBCKk++_PBTc0wy| zPRP0Jg7$g0r+8q}p^k`h_pbyPmc=RCTn&aE`{_$O=M@|IId^#=SUsz=ND8w(A)lQc zLbaZJZmWazTP$MZkP}rx9dF4LAkAlHq|LrR7|O~CsFFNskoU#4>1JSf*S2^m=r+Do z9wZu6utuZ{a347&Y8KF4fIO&+(S8ti7A5cD>TuBrd?=lmXcF6Px*+WJrEl0EX2cC?jh(YJ(^r;|%LX(iq#_QOmdbxeVtM z@>WZvv3mA~Zp2e<22EI(x6FmGqLMTDmkUEsHZYp%ZLK`*Nz`TX31YyhJ5IOLc(f^s{=|HZ;X;52 z7Xb98)FpSgbkN4|+jMYi^PmO8)PT0r;<**CZz{XHxi2^e(P}+4SO-RfiMhA(02t(C zCJq1y` zf@dNrP_-OIKe@QRiQn2Zs&O=OtLWFG%TTI*!(KDTgYdOHqTW#DnfsXIu1-Vl*Dwc# z?ejmll7CNdUqu%?q37--j)^hODXO{^{*R^n|0p{Dl?d!8wXr;KV5>A=^rX$DJV;#1 zmMNDAI0Vim&wQedZpm?)F7;q6sfL>ga{;oaGUMx4dTPXI!fLA<8Ba~QiV`VE^J@8h zSc8}7%x^u(d(QXW{ZU4^Ok%?{h- z5&?l^ygz;2(tYWwqAgrI`c{M?z-~<%Dx0m#uCGlwfZ#OMoa^^AFAvKwy>i6_ zY$MqzdHRox{i(`s_vZ#%#X~d1)ALi`66c01K!X6PGM&26USBI@20lYm3p&cKxJ-d#G&Mn|xM_T&o9|i8KXM8%8*WWfFWLwosi`n2(YO(^nQUWxq5KJPR@d;;pfQv^^ zRW(&2*&jOQRQKWZD{_S^Vqcqcyg}!o5Se{BgvV14Iw}IG#$cAhITOR( zO&_(kIrHhbnkZ4$$Ms;>iiTblI5@bM{+_Z96`L92zi3+bIsFj5EfmF^(Gc$k&Nb;- zV>pqz5N~sq85pwaN?93F<+sRvQeU6E4$Byu{MBsu5by85@72ANXb@{~O~=^s_m6xf ze0x9Is1#ms`0NSWuwU1!+EhGzMiCS&S$jzF%$dW7$;6idlk@B2H?CRu79|Ab&T7MQ zTHX&UVkS~G;{!(nUD61}8&_}`uiycitqe%|kuCP}>WWj~Rjjj({``8~A(OajM_uBq zuL)I379>rYg&@`8Q^J~%0&rr&{zPe12rJ!1%*Tdq6EnKdqw7yyO~v_#R=F)|(briM zd5j>u)&8HITFWm>qYY3t@2?guA9FO%Kl`!gI@NN`pG4Pe)zE^htp(nu7`SVLoat9s z7%ubHo93KEzk(yngk4{wCE}T(wTLXOF-u6gi0$xJ7njLrg1n6_X5;-si~jBmy5|xc z%CC@dOH5ytm7lB9A=O`u@1y~^jnNcCM=40&z5m(MBp6j^cPeC zdj4MaXvoD;bbU;3tu=J(93zPKoW&3xu7yF%?AQJfQtW#wqov8&HmXegWE6JkuCVH8 z&F~1Vjts3)S1~ACJ=KC4$Q0gvs7fvmb#@`i>gf*L$`w~%<40cZKl06yG95fRn zNA&}%PaW%F_RZR{Pfap1ql)cMIVCB(+o7H*;}h;%${6REkKyxsZS|K!!=NiRa2Wrh zB<6OV(GfuXf1U5mlVxbNg4^t4w#H%lO%|9}C`M>H!vygne=s6O-dhnOadQ9-YMafH8;ZQ#{jzd1_XJ3R%~qK62qtt;t- z=RBJEQQyRrH^$vt;^Naa_&rM_%HvjleoD4zOM^q1$H>b$%6qEv z!P=pcJZg?GOsk5|B{qFvPfgDs?Lx-Jr+^WF3IR$Jg`c8&d*&FZONi!bF0&}lwQMpL)^&cN zQr>HT9yks28y?6RxXmZS{^C-PIA znmyn!A{PC^O%b&3tY+4AIBdLnR@b;jVrVH`>x7FotNxdg4+5Xtk>yP>X19u_I+x!$ z07@k7h%Wx-Kl#9ps>W4aY^x%>XcQQ-Ho$lmhu}-i&Ct$Q?Wh`8HqxaefXNY`m>CW8 zU@z*7=)Qi5+fe8LiQV+5{zx<`cT4)_Nj^)@p>mh_V^=25tY2MmOq2~exj)qGAg%s^ zZYL)C^dw)B_N}qTNd-k~~qetka zfG}c5XIh1f4;}_Ajg+^9hTu#Z8M#(j$0ww?X(l`%)IvKOeqzJc&%R5Ko6-LuLx_>P zj+TBH?ay(%$YH~#e*puH(7nK)ch0Xq;&ONWQv-%~QECFVvLS<=7~V0EIZ?b~^5He} z37_FJo38EgLi~!crA+i}1T<~0@Ton=lwOEYu*7Q4;13N}8fz7S+^%}nWG;JQP5F9q-k)P^1Qu@JTN^rUP@UHrRKo$!G(VAwoDKq1W zvWmQt){hAKaWCCwWch<bLvu{po>rCvRkB{Gc7 z9S0o0UOr8!p1N_?qo>XDc__OG5^||0n8hl)B56zZ#b|gF-;R>4&67~_RQi znIH2$^;-Uh-wIg${ms2_h^EOc!@J$gu??dYeZ(Zzb9c8?&=8H{B_QOCNky52KI_aL zcSX({HUSld-_O$hM=-1HQeG`nTEUbXXQAI(a9ih6OkD;C{1S1r2&~PzH^jjfHCS1k zWl-q*1qd>z7riu{V^ot5F-P0c%Sp`c9caK8h>LRu-;7P~^d7x{{q{|QbvBu}nHci2 zBfHv8SKHDL_?)=lH&i#RG!cpuQwSc$h&jFw)1$-IFOjZ23&%`x2G#Xde}d4eW@qcw z25duCYd(@t?xb%|%iLXZZ0lcuR`$1Hx;D zSu|H%8xKI0?q1FiSVuQtfoIjX`;| z9p3dBfp`o}AP^LgS*-NaXv=hy4{6I8VGOs_fkRXJ?LelGzIybW+nf|Swczm?oLL=x z!jlGTy`Oo~-Efw>QVzGqM^g=0v1U{;O3NfSGj%So5^|gCyfi+w9hZ){!rfa?VbObT zPR*?5K9KewCy*C{5XaMH$IKja`6$`G{S6fZDXW^i?U7fB6p%xzcbD3(8nksfweYn% z5QIjqmwzji7SY=)ltDmz%R?8l9sINxNLV}u?t+fKzXz>=!_DgqbRKN(=wn_@ zgx*6ZypqVicZwPOawzzvM z2G{M_id5Y4o~pPykJ*R1g?*0}do zQh@CXACR$m#jwtFq*f?cpcp4ug5^V#v+7Rg`!{;aOOi!30~(xRN*-T7Izy>I+!|>7 zl4lX=&HSinD$=D3v7OYVZ6|ffVyBKrs1BFrWw-~Hi82<_x6%<#Q&Wt7*?@V50TeS# zLm$^YXBoq~`#7amKq(PB~0RU57OFQunJAn6Y< zdM?A?1!|gh%yH9_@gc~R*BT=sOiyUHNSW=PQaM)L)7`BD??xFu z=}HEOq^;7xNbX3%mZ3f1diDX0^su{mW+2Yl{Va?=t7ptKx1nbeAj&Oa){$Y&*#lx; zCZ`sSaOytOZ@{AihYv(>i9E}jK;fGC(~vjD@X6UK)JZhut46l%7pYE(c)1tifOE6R*g02KMxd;3FyIiZ>_4HnBbSSoo}}Bo;OB! zEe893TfZM1KZFJj8OBhQYhsv(EN0M`_*&X3?p|d=jLqUJp0S%eHNde?bAL{m1IHv` zbgRJEerjQ9@&R*jex9*Wg{6w)X0m6CdrVaYjvjL-&<5AH7vRkNt>qc%a3)8Bkv=g~k70E-hPCe(irE(Em@^@LDfPeM zdHoyQq(oSJ5xIXm#=pQIplfP1!4I}*&i$19i}Z~*`T9(MglAb9hlL$csu_g3RI>h> z<*ql;i{g{Z^yM2uc%_$+oOSm}-D=yfa!o#nRJ;C|C;6HeVzudQM#YD*+1~Z9cap=N z@*d9-PY1d{<>RWQlO5~zbcxmt%ZpFD1d*aGYYSR0uA0=1KRyhq+CI{{M-VNl??my) z+=%Ulvowd?iUXoi&J1M<@toQ)rp+qijF!py3^C$}d7Ft&j8l2LcE(lQ_^wowGo5yZ z9yxg|U)~*ZBJkH?42|SWeLK8jzDVK77FXi5HX%KqXf|5vYK{t0c!`2;=|fX{uE7f} z9oH8yTLD%Pkii?V`GGevejFvT@$_VEa$$H)Ocv`8EPxtQ=`53H>vLtb`hr60LInYJ zVlx1vb=Vt~Z@W^0jO5Q>o`_!uE>TOKE6aK(6EGPMI!T@>3!{|4{Is<|7I1X>c#Q-g3Q|5S)|U+_>t8@IdtM3cMPW~S(G%C?(_3W^nL6N$j7 z*7P}^OujHjDxtZw3nbfiR_CoIR+=Ux+ujv(XV^dNmpXsP1VLmQNdofXp6us=eVK1% zI)Erkb=R`UvLQdIc(+<&X^vdgE{4)y>=wK&J_5Kp8-+#rta~9&9f#TIzD;ZGUmPtD zhH<1_5gKDmm5ro@dDwBvcOu3A7^F5*i)3;`dT8Uzh4@mKQ`$&zo`ZeaAr0^e=QvbU z*S@_{&2CO|aS32410L3nhxJ*`&NtkuS((Fi-6t&*z2{8|=YbgWX&??{p!=lIK{p}f z)VlRI64;4W&}a54Ua>~GG)k0xAh*BrvIY^KtiW4s$H{+sIt{wmQ% zIRFPJ*A!R5F9|~$RwuYiQbN9zuuaE1h7|}I>x_Zz`9$N@Ngb1}hmL-}Np?@TubtnK zcL8!|HC~L8=o>hN#4qRa*S3)M();bxk>RTlO~W{n_L|3LAS>Xdx?#ZHQMn3h%ksGPIQ?er&&FJEG&+45D)6)w&x>V=uQg<_kdLsohn1IU`K9pV( zUUr`ePDuc}JQIyiXp{k?sj99+0EmuS;{!#mt(p=uxlX6c1Arx0s6G9w^GE79?SJos z{EzLEJ7>B@DcB)X(-{BKyL}^#bf|zvVTd0 z8GePG`A~@oI`H*N=|6y72`s)*XH#=Eo9ndg!@dTHVj}lZt9wSf){|)f?hfF`rgMK8 z-g{8lF!ndV(e@zNC~4rjx08*QZ@8%_pIU=<`O-@=T3t@-A_v;@bw4@4m8OdU4M!OO}-@iUK|NHE?aA!9pEOBSWjGoDc(z~BiCv^gYZHCh^Mcnk!P}@$S`+P4@1xx-Yl|JQ8EyM0 z#Sdqkxx0&x>%0owe`-24lvy7u)3{^M=>7i zM*EyXrCFC_4n2nKGZHrXeruyo8`Wt5EvJ)y^?qACG!r z>Ky(RMxFGG+oQPZI6(R;P3@~!Gqya{a@9F5!DBL763^e7ePWq2FnswHzr%5A&82j= zl1f&GOP|$$m(5QMGPqMQwb6J9@;rT4z=8~KuI>piYfW3f)M89Wx7tfMxhrsYV6gvq z|7<;wQthSRtRWObLOZpM>wd~yR+P(57Sl^82E*%+wtXp2{Jm0 zLU(y+uI}FGeDOU6xpMk3FkX&M^HW||%l}K3{{OGn>kx#d2YQC4EH475FvG=P;jpyG z#D)jtnE%)1{Y|F-@dl+#_?jqVA5A*9`8+e@6mN2&@mP{q)IWbTybBOB{)<){$A$?H zd@tQs&X$9x3-lATLZSO9;BO=rG0GBd(T|yhcNb>=K3017#`n~r$b$8}6U8W~JA_!2?)tz>)y3ZIpRHA*TGF3;3=n zka8WO=xuwVzX|?-GuHo5fWL3uY>Q4=!hj0)Y*FVHDr`@ALFHUv$OgP3T0zy-N3Qw8 zw_R4b|F)pYgl%_W37qOA1D@5P3*s$K9lh+itE}Pgig7DLg6kl~OxGv^P~*!Qzs41D zr~ZUF#tc_Mz%vV5h-uMR(Qcu_3SP!t*{SIibr(TqMy09aOiRO3B3iv&i%#KK|5#MI zP|q&rdj?l~Z!qT$R910$O5=6HV+(om*wLS$r6R81d!{{);jj?7Inqgk^o0auz<^fS zJ)9sSd!TkK%K#{?a2=gfm&%a$(}+4sQFZR&S|1`-jR=5)3AI~m$b~czG<9zjx1joK zeb%Qn^rp=&?HUZpY;jOhTcHy}?tanC(Pc6pmh12_e*w5?R*||5p>jznCdjO|%UEpO z=sVj#>N$WdWIabnA^&4PpWLgHy-LAO3~OJm1HZb3F1c4cC3A0HUD@fFjk{GIkK{kKkqkO_2|K+M<$G5lEo^-`h!O;u=e#UFmgXPh{~%)c zn^K?C_}80iHGAH(*Nw}HRH2=j{)jq4Df1`kihazwg^H78EY#ALBU0}PAw`+0eq%$0 zYI6%8TO{Qb2Ti@XnRF3w?J&4-mH_^;{697axDt!V=ma8+&RgwNvvGRQha2LEgqK?W zkHHt)6D&{4nj@1Zgk=|zV6^o8u%-lASkuB zp0;E`z<_Iq@n>-J9d&5C&BTETuo@ip31zZS;h-ib^-o0$MADoz_yKMj@ty}FHJ!e# z#7c51M?Cvv{U`S_xR0DW2l+DSe&;3fGc)5Mm&iEFChy{QYV94i=}{Guih$BS8maX{ zu{DV)%0Z8L4)m?8`-RMUpeIICsZB;nU$r+Vx!;O`GO7o50W%CBk9*V9S9#VOrm(aU znkehG;u`_?DK~o$ybA<>p%(z1FTx$0O_1i-sji@3ozm@avZkRoBo^fz8E#@P*l?(t zi6D!Cr#$&*hKM})D2obXLn-DC?0OM(tz#T-}?#IS0fni&Tcj)%b zP#@qL^?PW#!FuZaAoNV0&9U~1$>D#VAKX#isY47LWAceRX|xUx;@D{a<&G2ieKz_- znT{}Cu<;@%&1BK`$TaHSgtXIMICr^AY;X2Db&ICN8W}?Q{5jR3aaG2;ybYrf(p23? z<;`<9ksy_o3myc3MeP3TksnW#r*)0s(k%MdZP7YnQ&Lxof#9T zm?`qh3q_+jx^u{jg0^lZUHR!5nv2iG|YCw#QK50~j=TPU0i6U7m{YN7C zzs9Ej;kU*xHh@MRtu3&I*+1Cc(+h?g<7*})H+@F}*C|EJ`^Z>}E>iRX+Ga0;E<59Z z+*GUS+BYhs;dlKP-mHuoJ3)h*c8h~a4;im|!t!3M8w(Tz|cHmW_tPBY}A45+{r{~|bL3Q1KC<0&rbiuQ-@?7-d zB@V6|UN(gtxwedL!%Edr;M5G7=v=oUw&1)IFgycQ;}4%ffh!FoXb|4L+4B#X0yw6s zf;zSqfIvxHIV*N_t=+tPbl!iM*gVfze1uTtlH&?uvM!xf12HsUv~w<1&V-NAT7E~^ z=nh{%tjlTVmb{nMg1@~0Nm2<|W(7`G`pgkt8NmhD0PEXhcme`8@nRr(G}DQc>agt| zF!#ze@l_XJ8L7v0{ZrorxP2c00mHnKjw_elKPWBPA_se^5c`4Qz@mPFg3M%Lym%$k zf(c{Hm4(r-tgbl;Ua!HNjy2S32-m%{7Si}IG2~WK7}<@sbQn}YWaB0yK1fyr-T@@B z$4G1Fi;DX>-yv&sEvTB}cs69B$&6Mms5m7#(I=heZq|tN5F?ancu)NXGN|{r^grTd zezyv>MY~-dl|`5N_>lBKOtk?4COG34O$s z0ef5sV@sZ<;v?s-*|;J9=CVZ3zJIJVTH9v_T&gw|?%PkxuhV4Rn#l~Uamfo>j7_1b z>y6wuyE*XE7p*R_X!No#Y)i(=6Y#xP)>h0;dFM*j;z;F~nxZY6>`|%=cSWp zD?QyyQxx6p=*GWvDnpyw=TZ!eh3&waF2sLSt5!-E0_0`jRbQ1WV~DeHtc=ss-+j&p z=@;XWmAQEc0xwU^!J@rHS%>Fw{w?egBI?0sUlxoqWNzgR8f&#Vhn!&wAkmiW(T1y6 zorwK~gW9L}VVV+!X3gzTuEYZo`|)__IivEJ9Ub6ZJi-Q;3_Fjgzm+W{_dEnR z*&?+$$GQdamwy78kYe{Z95(w--Zj)_h&B*82Dn!<_cUjmlu~UmgjW19h1T6ModJ?9 z_9YsMT`db}O;!vWJ~<5PH!{05M5!~gDVSZS%esFG#(N{j{o~#-gWy}1UFx!w z?PPg%Q|a#FVrpQ4u+rR9_aW_%9@s9cb0zMsNhcQNiS+Ph+Q<_6M13Mk3>>_a4>E<& zGQCs;xOO|!@YkWCvH%o-?d-6 zcY`13up60)4_i2``(%37NIDJ2zLE&`N>8w;xc|VaI-pcc0KhD+LK#~i?}6O;)7{KrgO4h^YYh7&q^?THt$YwhoAe%xWe$oggSKIW|H&RyC`ie^M0 z{HT1`lg+(nznX%!gAjetccCHVK#)hO7QrVr3v!w(CQg)5>2XX!Loc2T-oy@L8tZ~ zHMwOoC^53#{@zZpfi|*Va&tnU+rZa5^uayVW?9$8Ch#@#Q(b%LBw3?aw;wDwBaY}%RO1m-2+=J5r zwOg2d4a5*AD0_5LDJHD5mO)1>Y-c2>>J`%>ZRvdgz<>#DNM!BK3U356vZ^!!mr1SF z9AmvCugVZJ9o%fca0n(K*$n=8cE%IH$3tHZ&JRJ{1Ev>G!!$X%ikHT{W=ksQN&NB_ zao;n@L|c_^V9LJ^sZUtiKCkxTzBYvCK3@e8;Yc@zeU}RIs8^bX59Ll#x&)XW%T+3N zcmXks%?~*cGyc*Eywc0tB4{`fWH0dbjo8dC`SboVq-4yG!pIun3gbx?QMl)rJT-kO zjsfA(o++#^iPFlKpPL#AvdX%~!f1G!7h1?ESKaq+z0xbVkc1|VO83$}4a7TGI=QR* ziP^6k=|-y)oa>_taV8nB2VN;XwRuQQMjn(jxNP!Ib-X*6Z77IF3RnL8ytV;WsnaT53%{c^JC78qVYd_q58PG+Y9;Sd(4Cuu@p*PB~y)FtNSoZjIeiolVdfS?0q`?d1d<+QT$K zn%>X;_Hiw`#h$UTW2$`I)0_`B=Qev?!Csjj6~w2uEE>|3m^#o|UoA(Mwf#SZXUpwJ zu~-B*0o^fgv z2jXO&gMA5oqY7eWmTECNAF^HLa!aS(c4B7$RnV-0=Bu6BF4ZIolYJ+ zYaejh2O~H)B@y?7NYYMfKu(zX$x&&lk>h9V%0br*XJ$s*{^497pa|M>w@@&O`eB!x zx_^1uZ7cuyqfv#bxbk4E^6N5Sxu5M6g~+#Ae}>Q>{Xi&9?v?w6qIWT}^KM53-#>M^ z+n*plumi02Ue@DIrJuCkj0CpI(vs-=vU}jR1I?;`Z`^fWJ%9Uh-@1MZeP<}#?YkVFITtwybqNqS0gKj2 zg#$%2H|)fE%}8K~+XQAq~DPZI{jcVVMLhl)cgAlQmc0|Xpq=h zgTCH>L+Zgy&6KEcFy6C{dco(rXgusO|K5JJC z6gzbNfWv?Sa}$P6AVbz4`lu69W%U{P=*%*YLbm=Y{>xj4JK4$CF$qv|Xu(aVJsR zAkW^2Kibp1p4^Di`rM)C^&QIxbV6rmjN%1m1-=jkQWd)d*}9Z6mVckT;-+>*&}T~R zBOl?<(jWk@DH?ci1N>U9FUi?J^u)-&tW{@~`%XR}vjV8lShDvMz>#Ha%d8FLUp_JX zZ{4#y@3uuI|3dIcP8M8d(#S2v9I%uO0l@w5`}+7#FPf1nXiT#1&)ZuF5KSDZ3vG)Z zEP8Qv=f8Hu4|hBp`&RVL#xK6dXsU6rXjaURLdp#naeZL;O=(nWwQ2bXpv3RpLy1SS z-n&0pz0L8bZ@X+@4M#g@v%~5(;Dwl+*fn@!K{XCtLvLTahc*N zaG6!{EweXrFXm}qpOM4=m4YVYbP_g{d}pRmI<}G_fKp#ns`KS4i<(XdX_Ecn^j7V@ z%cf%>(qb($P^KvXRJgPU2pQXPW1W-vksT-3PVhhI8VdTY(}emhX0Nk_JX;a%&}*&+ z_nLAC(1%Amuk2UY-Xn%vU$~bUw|^ds-j6*}iUd};t;3%D>qprW&U9pR@;AL{^KJ9r74NA z?`Y52kv*_Ka%c%AWp;wYyo#}J7Yqddcx#uC^pIyqHg}Z(4b|O1Ub5LY5#^Y=^-1Q3 z4wNdzzSTCc5sEA=X{lTS6zGLLU_>5m_ZrWZ|An=k|Gq)LVq%>XjNNw>S5rrobqf%- zXy2dGVfs#TX-s0HNZha;c|F9ZH=w7k6bF~_^=uQLQ;iGxVY@rJ@}w_Z;d}-BUmC06 zHS}F&dkNg??~z#e-_#yDKzvNhVR;1l+XNCKnSBRbj|0l=DG=ghd%%x^FuHX`c+ys z6n>bHjs&o;h%C9N)r+XE+6e)cPG?{--dWPrc%Q#u7$WxH7ZZJ%XV^Qv>iPGL0*=uD z@L7g`N}~IF#oUg2QST84#O!^Bcb>?ER=e1-wv6b@hv&T0|Md01bM&a&%<@`F<49c`&ZIm3Os3Ql()u3?_KrvF zURnORLqG|(8JJq!WM%rjkQJam19J06%5`GLq|p8EX+@$kFX1~T?Pc$A#I(6#@laX$ zq>(lesP0Ta)@E_B@&{(Ij3*C=NtS}?nhuW7vtl}JUwGS|GwN?uEWM&Sfb;E@{2@ad z=@*CJXt5<@>(tPsdB}Rrx$K3D5LHh|9O{iM+k4lYxZ&fA(MnK5th$5--5Sg@O9@|# zBp$KlthjYl4)`uhp(I6r=jEgK7HAH`k?%7?0%dz2`I@lZ5+BuZ3rP<8ZHd4OTlFtk zpCCxO_H2v>vT0kH{)Y4-@U$7#lc3XvfjG zq@QhR`}JYzVT+vJ@t*vK_GxHez%e* zXhYu0tl1M^jS9>O+FL708osk)tu^eWAc!4xD64$EyGMNptLg)L+pSja*v$=1_a&C` zre=5FkB?4S8C&^QnNQYU96Ba~8o9DwB`qUnMgQ2uTDnfbUIAQl!27f#g^!zp0boWI z9N;OWTzmtRybCP4u?dNz_cyiAr79T~ta~@mpLtd#1M&UlnqE#SO+l~WLZ z8pY_{Bn`L;Z>F6by^kjB*PHWAJ;SwiWPO-aW-}9rUA-Fh(no~Y5n<8_O}&ec5sZ<` z8Cr@Df-bW#W+ye{gEVFID)4hVrjL)SJQ(=;3wdudmey3>`w;aPK{` zOtaSOEmHBFVWz-ME3K1x{y=rIF9xCMkOj_4-t6xmxG*M0t;~H%2uQ&N@7U#}Mz=3@ z`ZHq++3Z(?dd0l5X&Z3)JUEkY@W{XgxjFwYg3ZOWR;MksK>%cdNwUo?x~R1>RM7?d z9F&i0g-k5#1IdRl7&tHjX^?|M!H?+n`>IDqCcvR^^mSx^p*?6oru@SwMtM^&&=?5> zJYKWHTmr}9c^=Rd<6ur1dK<|hlornODCQ`26#J_0lz~GLGjqF|obugbkT@hRM2&ds zWs|>|4EIh}myG-EjO5iw{h$$_&EW-L@4_Ij@G2lSqzTv%1rZbhDIqpg zq=|Itq98RWJ(Penm8KxQ1f_%?X`vIOMS5?c2`G?2fB=CIlJM=I?>T4Od&f7%J>T#9 zhcS%V*?X_G=9H=soKSOfZf&Lp4d7-QRA1={?i)+{`3hWc#Q1PQD{X4nr}ST z3~-l(c#lWaJE|j0%~u4$W5VWSsglypc{aK&7(b)rHoRj|^XdiLLheSlP8{?D{rnRi zd~Mk|xbw=j$5jCZZbQ}z3wF)c+xN{TUQ<*~+ojh-yIM0v&#oluIu#^#I$!Z*gj)Of z?bX6bZ^FW5@ET+*MZ{*pyZYyPK8-Y4v?0hxuGk>Z{C3SxldpOW(AA7RE`avN=nkXg zb-vw64%?KiiTBrWGxMAc_sx%u%Z} z+qAB04{u*XC91<*>3o=s_>bdWylx$-HtNVBqKOaAKhqJ}U6Up~WT#jl=PmROhyv!A zfbxNKfvG<;9H0!$Frdg`WFLXNK#~uTb?uo1&HFI~dQQaIA*Y{R9x2BvfnFbB6Q4qD zsu-PGHh>mJWBV$o4bqZ_N=e$?P%ROjwmDJ1M}E`JO%Ks&rQIvI`_s@Upq*)~F^Xy= zxAQr%=t0kvUv>A4@0{?XxSGw9fFeLP=k)E3Xp9$d1omtNcbdJUM}H=t6E!Gw5)jfH zUa6QLskavtH*j*+L3FtRr&arQyEnP5r&1>hfYtY@F4_RXJ-5a+)~JG*&djE91ghLe zP0j#HznLS#JIa@1v{K0OJB2GQoMmV|dyW3Be zP3Jwb8BJ*)bn4PJqHyyH2r(OiwGQ1ld+=6UHbX6_%4ZDp^hx?vI!x*;g{_V2K?18$ zL2{X!rY~xX&abbxijg1lrot~(VWJ4^NggVfvGmMf`48oFrW7l+<*FlIBo?|so{}SEg zlIgVqv#={MhkngHLs#X}^msHtU9tqUGt`aeT%IXPE-m=eJ>_#Y{J^xT% zWQWVD{@$z_bzz%D7bd7Y8z+r7O)64|f}V5`@Qoip>7s$NgmLnuQyK$4P$}=Obb*u= z$;E(N{&(Xvj`1Q+wV{CwUqC2Da_-SAzm zRBc-g7&+1=e|6GDo2E-Ln&fIj!@j&I72?{~SCHen)J1kaBK6qxcW9dLy^=in+V96S zAn5eC&sVf?&K=nmva@Zwnew80B8y&ipH7-u*$cLKO^5^Q0n&?8c#q>tsr;x@(dU*`-UBt(QYYbkF4=Lz{>ZU$&6CWDVg_Yv?tqToa35Q=7+0jTFuu!r1s8$#Y!db(>bNyMhX0$iNvico)0*Gis_{c3 zj^mGO&gMrgV`){>sBHUWS;7)>4`h?HVxM#MqmoThRYUHS#nRy8p3h}K;c7+{cA_(( zs-Y+2*O2~$ZB<>-!*B`PowX@7x^K9hvLgF-;_9m%5ft@Z#Cf1f{SDj8AV-a3lzM|?^>85^5ex6qr_?)X)})@ABw$rWsABP(O-J2hRym* zOeSz(FwiC-%V|Am!Bj4wAY4xpnWVqJFKhq@?{=+f8TuKYw~?tV_B{+pNw-4lzx7v> zj1L#xl_NUvbs^{a0>uRkUgj(%r=E~N1IQ?M0}d!M!AOZ1@F2_m@kpT*hElL4^7c}1 za-Rc8c2Js1Fq zS>4qJ%Q??fC-4WVdR#}A*642JZ+ucFa z8YlFR767z0(e4+z-7xsJsJHaW5UtU-^NH3^W!_)!ew9B8HNkw<%{?23d7Q!5OFLa- zHqOojbUawZ*e2CE-*)3oHrW2;TF{bK(W2DzM6hev=p}g_RpDFr=r%LZjpd26AF0-1 zp?*}kW%%yqtnRol2wmoeKDDWwNo!j_tt;u%+ktSX+qnx0Eh9)}UI7>h{-NLT*Dry+ z{GO!b;lf3g2NYOA)WHG0e%8gTLPaWSbD{;6#iKRu)`|V4Tavo=)PB5|zNEr} z60C{;f}2s3FhGv|+!il%EPO+DsI$}xK&{aB?8NiqL1VsWle0gsXf2LK4{PUW#J3vk zjwKQ#o5;1t89@K!pd&EupQc}p_zJm!&gK3Iu5akBvH~p(bJ=RwzGRqDG}Dv>ClKrd zNZ0^qXO!jWXTc1br#?M}TpJkEEiu+C{KB}>&gRRT%yCAL(^cZhpF z1{wseX)TdgJe>mPfQAwx8&iO;WIAp*aXR7IPRvsDPHUvMluiAbCuwrnU=@dV=Y;_&j8)W|&G2nKKbE|RvDMX~y*LpNTc^|9t zOGmUN3&uO0Dnqnh3GS|OvcTPriroozI<8mhqu=vJKg2#K`Av1);MGc4qdY zWtMXt@=ISyIK$-{nfbotGKZ89JEfC!xjs^p(`+!O>kVZeoy54Iy|;H3q)sE!EFBcx zHJfl-AOf!+%*?!EuSwEnOMU`oDpwxqtyb%xvI~i5YP_$D@-;@JibnKsK{=3xP=Q*f z=OGZ=0foVrS8B&{**-pvpL%LN-dpmtB1F~+cOqCIcZ_~*x$^oO7gN}R0>hjaa@dE= z`J<5!PjEB~e1B??y7k?}9RAo;rEV<9O|kB_-U1kyi;WPsQ$woDXN#v=?c7dsI>H?$ zPIQc1T+*Gqnw^$iT|NYN6tt{+1nfG`n%r>8D10+s?_$9NBR~PlI{0vU*yBe7F@?sl zn$gT~&a=}3R-IO4!5u_ozITWG%r$W)W1 z(RC-)K)qw@Q1>qt=y8b55w)7>&Hmv5$x~mw@65;#yv{?6`hI;nD0$MsW;gFM{+=c1 zVFcL`-p}7er$ST+3GO$PUl_k-KISPj0TQM*IjyV)f$| z((&?rTjslhsH#Cvpa$>(fB9)kbg=;c+d?31kmDmKuJ5=ua>5ksDtT-BUZd;R)mRks zVed&mwoy1j$1GJB?h3{_VOC;CDURfcR!6#_s8%AbLU?MRcvl&ED8|%=os*FGiQs)R zDvkJ3xPVYtGywJbL>sQ96w<^7v-3~&bT6Nl3hxnT@bJkrhODpX#q;D)1Fmcn z+w9whjw0^tKK{eCUP4T9egfKBHJJ%ENy30_(wKuGD5{yq3Q&Vr2p-Ct8)JbdOlz0P z+rG{>6Sqf;g=v*)k^%|Tnc)`WZvnDMexFg5;t?;NUw_d70cSsHaZEAM<5&9t_lMkp z`3rN5o}!WrDgASkJ+-2B*dUjXQd;=#;vACdBNtUI%_nEwc@NC!D-rz)rNzO=HFk2)1hWUM{11mSoIBm-IX6`DPo+#pT4TOBK z>M*uSTMLMfckx-ytPN{*>%L z$dbkRNyh->y7>GFrq|bM$lNt;=zPhFis9tlB~F$-U2w5nv6xiv`~bT^S*^^XzG;t; zW++z->7rW~kFnMO=+JnGkf%LN7rwh$IBMsRi`P=-Vk5TreX`CV7eDNyZvvFo!R1AC* z9XfeKtjY`lf`s{|akJ8KzlNcS<=xZE@@iC+JAtmmhnU*ah4*2wxr`>iJqSu{oW?lf z3h4vT)#?$5t$d*{FWE>?PjU!V4YYIoBp9LV{uSw|@h3&L6XM!)+wmrW-|;R8-N(*G zNDJ-H>6g8QjjJ2fW_EdsNINtpJ9OSc1{E`}N(gZvNDg zUx(|E(ISjOE6{Fu(9B?Ipzg$wghSo%eSHjMJHxeZ*!^%hF0ZAuw9KWEyjgpK;oXmy z{h{#||Do~N0U8fzP_8Rd-Sb&y}^+uu}|A1@g zoPk66Qh?G9#sMbmf$J1f%{kGv9_aEw$q1J%aeSnNAJK5fREhh{%cYUEVxzFh5KiNm zsI9K6U-|9EDe6hClrCks55=u}eWseMS??qZVDm@F&RCs=#m*O-^P|)XL|)2(@THnp zximFz|2$jD`7dX}g2VJL!loL*GN*nT*#0mdHLaOS{}SR%$;e9nO4;Ducw8ghBRzgK z)ux1zLVN_*>U5iQIVt<4IP^)D(#dMolLBNOV`T&bz8K`&yqipZ+#$(EIMmv@f>aL?d$QnsdKn$m(~+wn+3Fo6bK)FIWhbZ ztggsPvU|A;gfNP1mQ4<($R>dx=&0=yS5G0S9?(7W)Mx2J&XtlbGd+e_j9~asV|}mWj>7 zlT8F--K%vTTa~A7FMfOOHgTi%_cCSuZbp(NB}$biL)bVx zuT`t2x>-V|!XwX~`OiPPh`~@U24|fDy+yAN?RoYr?c?AVzhkWQ+#;LZ9LlFuE<*Ck zu`viIfo=diJ&)}3D)U$XfTOIo55u4CzL`d?K*p3qB+YwWu17@e2hb(Wr}eD9yK!g@ zIX5df4?bkomHXNt?Z$86s5&?fk8I+{fH#(fQ_`k!*rJ`n`k1ZSuI}5p3r3bd;P`VW z6VGWepR=fJaYLup&Vz0BAt2^4T56eZ`xCCqqHD-emtLudXVGR}Yd_xotOe5MW0rKj zXP-a*iJTK%Ohbn{CV)O>G?)VrMDLzB_0T**_SQ`K7QpoXQsU`z;qfYLb?=`rMtbML za{P|gQ?&_6WhUfW(b}GCFYs4jeuJMs&X-q5)aj0+1e93@P_9ublDq%}TN-(=F<*f@ z9G^d5Dm^Y4LiE}Xl!ec%t(rrY#MmtVWc!#tL*+h*tZCKZ$68D~aHFRr6lg!1vU6_} z0LJ|hP-8rKLm=w#y6GS%VYQuq?WuWmIy>&aW?i2I6a4 zoD0kIR+WMWLl(KB2%=4`<%x_O>|iBMU5JoSR(k&dv`ME^z6Y(E`Ql1WzHU@O@h=zN zlv7(|gJu3_zv_y#XIP{UMh9bFY#3Rkl{J7#YtJ_FUR55LSl*^^8oLbh36K$TAMFGI z{kXi%j`QBKH}+Z(XT1Y#J@jxT&`OeB_}45+J0#`8%Y&41~0r*k~?QagG$*Gis)KZnF70IUXr%K6r@| zS;xlaT%e>Aj~km82Rh5Tjr#=Sb!8=BInbmbK^ch&40UxT(88WK+pF>Z0@S|%8teFg ztFMW4=|e*{E(@BZpA%5sI_~4HRP46x?Ck>{kXi_Gc@sbZlGH%nxtiXOOxySblxE2X z70u~K^>rj&1l^o8x99!~_3RgPZ~VX>tC?bRZeOKo%tk1%V!H#)X1TjFgaX4-h3js$ z&r8Zc3IV?d%8**q0$m!fw;i9MJrIC{b)yuFRt&O06(uAr=_{CeLwVBg5s()uj^K9y}+{$#E%|@pdBILF&{8GKyU37P+>4~F%WBhqW zj>E(5Gpb+GhkIn>mY!e~7&{+9BMups+s!ktoXmm?b0taZ{hgx@jCc84c6-gYyO2k8|C3Pv zJn(fO+n2AdyyLPSs`>5IOtNVn&>@#LP>(CT1YJsw-H{y>=2v75iNHzkgV1XO3AJOd zaMoNMuF{troULrr*V)mls8m*Ie|7rcsScZpqv5Jo*( zAB-fIu~}p5P1y^sX*v9WPmygq;>PEu8H*0JJ>p!W^H7OL3fYtS?QHVnZNdS69+M1|O znr;whPBT=;&wopxP^F`&ka-wo04*}o$|H-OMabuq<|-UK|`71+9dlY+Wt)0ywBUCovgABa9lO0s+}ZS_+Mgi%QI%Zx6Q5j(YSo-m z2+1%M?+lH#zHT8oT(*P?(UT<`#I}_3filT*y+LXH)KwtM zO7`vRctUz|d8p4}Z<(%%Req5h8brD`i7wvMKNIYzHTMi27_yxVG%8zfQo7cBNBU2& z2%HB6IM*BH@A_ardOu)!#$+Dtkz@+_9sSFMCd2xw8H}l)06;j+R+twOf-qLoO$$=P zyYMX)$$&DiMjNdp{YB-_m4mi)z&j-Q&8;P;LJtkAy<7IN%Sm7bl6eE%W^?&F%bk%C zmOf_t$)kh+C}DTP;|^N=BRolm$~^`STsW|}dGVR=$#jHOo!>*X?iTzA8kZJc0UnqZ z+)L)lNp=Q;i&dbGcXN(3dDRoB?^*7;U0n zXrCB&tb#I4O7mM5exWY&+cU*yt@M4|z&5GHIzpEmDjh+8qpoW~@`i4B`}51*1d0nn z;1HnO@~+!rjfDB7HA$bZc3j<2!h3C!NN_lcDm+cQ$%hm`L;W3Yvq1kAnj)K^sC;N2 z&nrHV)A{>SnTVL8pWnJw^VbMEYH`Z3I?AXipJYj^LS|>W#WJTuGfY10*%w;P=#vM6z8LqP z=lc(^jBy6J!JoJdeXX~4JEFKSqZXiRo(Gh7Rw7&Qw$L;7q-#SJj>399`gM;;PBNp1 zS@z7Z)dM{|%9(&Et#OuEn(9b8mpDf7DPA)Qot!IXT|)UL_uy$0#%(J@-6D6p1zGWb z)OQrk4rqtHJFF7`#@@HxQ%&ovtzLtG99s<|eJZ24tg5oD?_gv{e` z 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 z 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 PZ|4;X!uBohR3p7r zH*A*%xY_T0bSVcR=-;oMR)i6=!-nHAI9%g~5A927Fg&1LIt5K*^A1WcBuPe<53qpZe3da#(g5*R5?q6RQV7=C+zTl-EsiLxQu#DBsC4 zF%3E@-j9t{C+0Wu`jE7gDOm81nqOrD#vQ|}G5)Y8zi+7IlOKEdno1? z8aSRRx|PYkgWP^P%9J4+@XFLpoua^r;wV#H>*~QN8A->;UEz|?sutry-AI^4jlFH1 zt=)EvH%u+6?Yd>xFbJtl5L$0`7b*6Zr3i$`F_}Uk;>VPNA!N2^LeN>{_u`69$c3D zx)8E;fd{6umNi@aSc-8W32qkls?E%iDaMT#17mX}X9(_iD&KZG_Y6(op2AGW8Fs8w z>;=w>yyaI^-u+Tf-wK@Hn`iVB6LbF8nCR2-A21Qtbni*%dxEwbHv;>N*~1FL>58VI zqM0D-j4&wKxFD{Gn0u6%i3hZ@M)a?bFA-azscN6o%2E&=y4(ryBF9`Cj4U60W!_)? zDL?f}Jm>KBQV)CewE9~4xu+WJ9}A1(m1t$=2lOeIw{o7No*AUpSANz?(^2OMKV!%%CYZ-*>Pc#Yb|V|&_n`z;P&Su7E}u>C<(=(& zMl&=MW^kmXWFYRokbG-5NcBxiyS9z8&mvQ(0U&qR-i|Ao=WDUu8dbkq_zwB#sY(ng z;RsMhPk?atk#cSjb32W^8G%B8aDW_2ih+ms_Qh3Maz(0Ha7Q6lxZ??a=Tzs2H;81f zo*&R!g83g_*2lvJM`99n-$x^&bp^xt7#=g)Ic>FSb1s@bpt#<_<)f)iw>}xjy|vX! zMk04xk0Q#^T$XK*XP3zA7Ec(|y-yy8wQS*lM)2vN&%?Gv^EL{Q<^n4rd6b3E&=8Jt zY62_l3k`Vrd(6yqmLHu^8-A)NnVZNb_!%*0)c@No`kx|Zm)l_01s7UL*{R-W|0U4m z^4nI6;@F%R3aXvM@P6Jf=+Z3nYBzltbPGpkjj65%KUm<5m$WwV&JP>&;?G*v->$BI zBI>DkdR$|1|7m64r=i({G($Z`hoXX5ZV)a_c6KrI_K4XGM0Y_LazE#}Xi1@}p{_1t z1ZQDfgUdVTlTbF@d5U;k&Ot+&berHtSdz7swQUqHr-u2{)eBZ>mGXKPvrIGu9^HUl znNL+r1R~56tM(;Z0MkEw3P>1+`FD=g7^6Ou(b~f3#^ty#h%=N4Hx1Si%)dPy$^iK? zwq#gX@${9s`mBV~wXo>hhSK*;-4<`_O*0QB#c|Yqzx_diIv&}59;{r!wK9VF(jcc& z6J>xv>oIjD1V$ghkc|adqo~sI394r~7?*u{s5KV3n5lVu76Jy_bVHVO!VQkb1S-2) z0o*iLl1U{Tff#=WQ0nnkYdrZ6E}y^@N2NtN7EiKwRm$U;yAgyI-!i|X4<0EH6ZEAV zJ{LHPx(8roZWs*MIj%M`he2DyhnKJ!j6euMAPH;VoMe@@wsS|O){CQ5Q^U}cp9#V% zeyboII}Wvzrd9tcksuPeSU={HzJvUeTU zHoUHw3(O3h$J2Jz>Inp>Fd&|^Bt4zHgybZ0J`Q^Bg_#oba*_*^D6Q5z=q-F_H+

x; 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%0A4>gn#i4}w?^krC)|ET?^L+g8 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 Date: Wed, 21 Feb 2024 07:22:57 +0200 Subject: [PATCH 18/87] Dropped the 'v' in version tags Signed-off-by: Itay Grudev --- .github/workflows/release-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 856d8a743..c652b433f 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -6,7 +6,7 @@ name: release-pr on: push: branches: - - release/*-v* + - release/* permissions: pull-requests: write From c80c17be8531208475e1087620ad2712e65271f8 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 21 Feb 2024 07:27:55 +0200 Subject: [PATCH 19/87] Bug Fix: Typo in CI PR assignee Signed-off-by: Itay Grudev --- .github/workflows/release-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index c652b433f..28fe4796a 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -27,5 +27,5 @@ jobs: BODY="Automated PR. Will trigger the ${TAG} release when approved." LABEL=release ASSIGNEE=${{ github.actor }} - gh pr create --title "${TITLE}" --body "${BODY}" --label "${LABEL}" --assignee "@${ASSIGNEE}" || + gh pr create --title "${TITLE}" --body "${BODY}" --label "${LABEL}" --assignee "${ASSIGNEE}" || gh pr edit --title "${TITLE}" --body "${BODY}" --add-label "${LABEL}" From 9bf11cf0a3a42525dad0c0cd785292f65acf3b7c Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 21 Feb 2024 21:55:29 +0200 Subject: [PATCH 20/87] Bug Fix: New release templates were not backwards compatible Signed-off-by: Itay Grudev --- .github/workflows/release-pr.yml | 2 +- .github/workflows/release-publish.yml | 1 + RELEASE.md | 21 +++++++++++---------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 28fe4796a..dbc0fda12 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -6,7 +6,7 @@ name: release-pr on: push: branches: - - release/* + - release/*-v* permissions: pull-requests: write diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 891d78207..aad2dc592 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -37,6 +37,7 @@ jobs: env: CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" CR_GENERATE_RELEASE_NOTES: true + CR_RELEASE_NAME_TEMPLATE: "{{ .Name }}-v{{ .Version }}" - name: Login to GitHub Container Registry uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 diff --git a/RELEASE.md b/RELEASE.md index 18111f24f..8baad7bcc 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -22,9 +22,9 @@ In order to create a new release of the `cloudnative-pg` chart, follow these ste ```bash NEW_VERSION="X.Y.Z" ``` -3. Create a branch named `release/cloudnative-pg-X.Y.Z` and switch to it: +3. Create a branch named `release/cloudnative-pg-vX.Y.Z` and switch to it: ```bash - git checkout -b release/cluster-$NEW_VERSION + git checkout -b release/cloudnative-pg-v$NEW_VERSION ``` 4. Update the `.version` in the [Chart.yaml](./charts/cloudnative-pg/Chart.yaml) file to `"X.Y.Z"` ```bash @@ -73,12 +73,13 @@ In order to create a new release of the `cloudnative-pg` chart, follow these ste ```bash git push --set-upstream origin release/cloudnative-pg-v$NEW_VERSION ``` -9. A PR named `Release cloudnative-pg-X.Y.Z` should be automatically created +9. A PR named `Release cloudnative-pg-vX.Y.Z` should be automatically created 10. Wait for all the checks to pass 11. Two approvals are required in order to merge the PR, if you are a maintainer approve the PR yourself and ask for another approval, otherwise ask for two approvals directly. -12. Merge the PR squashing all commits and **taking care to keep the commit message to be `Release cloudnative-pg-X.Y.Z`** -13. A release `cloudnative-pg-X.Y.Z` should be automatically created by an action, which will then trigger the release +12. Merge the PR squashing all commits and **taking care to keep the commit message to be + `Release cloudnative-pg-vX.Y.Z`** +13. A release `cloudnative-pg-vX.Y.Z` should be automatically created by an action, which will then trigger the release action. Verify they both are successful. 14. Once done you should be able to run: ```bash @@ -98,9 +99,9 @@ In order to create a new release of the `cluster` chart, follow these steps: ```bash NEW_VERSION="X.Y.Z" ``` -3. Create a branch: named `release/cluster-X.Y.Z` and switch to it +3. Create a branch: named `release/cluster-vX.Y.Z` and switch to it ```bash - git checkout -b release/cluster-$NEW_VERSION + git checkout -b release/cluster-v$NEW_VERSION ``` 4. Update the `.version` in the [Chart.yaml](./charts/cluster/Chart.yaml) file to `"X.Y.Z"` ```bash @@ -116,7 +117,7 @@ In order to create a new release of the `cluster` chart, follow these steps: ``` 7. Push the new branch ```bash - git push --set-upstream origin release/cloudnative-pg-v$NEW_VERSION + git push --set-upstream origin release/cluster-v$NEW_VERSION ``` 8. A PR should be automatically created 9. Wait for all the checks to pass @@ -124,8 +125,8 @@ In order to create a new release of the `cluster` chart, follow these steps: maintainer approve the PR yourself and ask for another approval, otherwise ask for two approvals directly. 11. Merge the PR squashing all commits and **taking care to keep the commit - message to be `Release cluster-X.Y.Z`** -12. A release `cluster-X.Y.Z` should be automatically created by an action, which will ten trigger the release action. + message to be `Release cluster-vX.Y.Z`** +12. A release `cluster-vX.Y.Z` should be automatically created by an action, which will ten trigger the release action. Verify they both are successful. 13. Once done you should be able to run: ```bash From 22e6fabf01bdc47bd1061c7c4050488e16df0c99 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 21 Feb 2024 22:04:50 +0200 Subject: [PATCH 21/87] Added a name to the install cosign action, so it stands out properly Signed-off-by: Itay Grudev --- .github/workflows/release-publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index aad2dc592..b6bd92a2f 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -46,7 +46,8 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0 + - name: Install sigstore/cosign + uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0 - name: Push charts to GHCR env: From fbdde936be5eff97e7808318fce756d6438e73b4 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 21 Feb 2024 22:08:33 +0200 Subject: [PATCH 22/87] Replaced ubuntu-latest ubuntu-22.04 Signed-off-by: Itay Grudev --- .github/workflows/release-pr.yml | 2 +- .github/workflows/release-publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index dbc0fda12..53608c6f7 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -13,7 +13,7 @@ permissions: jobs: create-pull-request: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index b6bd92a2f..aadd35bff 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -12,7 +12,7 @@ permissions: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 From 282a61478e433f07bee7474443d21037c7824d82 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 21 Feb 2024 23:16:39 +0200 Subject: [PATCH 23/87] Bug Fix: Release CD not explicitly configured to skip release on existing tags Signed-off-by: Itay Grudev --- .github/workflows/release-publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index aadd35bff..0645ed7e1 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -36,6 +36,7 @@ jobs: uses: helm/chart-releaser-action@a917fd15b20e8b64b94d9158ad54cd6345335584 # v1.6.0 env: CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + CR_SKIP_EXISTING: true CR_GENERATE_RELEASE_NOTES: true CR_RELEASE_NAME_TEMPLATE: "{{ .Name }}-v{{ .Version }}" From 36ffc8975d362e9c3e8e66097bdf5c2cecb925a2 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 21 Feb 2024 23:29:03 +0200 Subject: [PATCH 24/87] Bug Fix: cosign step not correctly extracting tag version Signed-off-by: Itay Grudev --- .github/workflows/release-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 0645ed7e1..d2451f81e 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -66,6 +66,6 @@ jobs: file=${pkg##*/} name=${file%-*} version=${file%.*} - version=${version#*-} + version=${version##*-} cosign sign ghcr.io/"${GITHUB_REPOSITORY_OWNER}"/charts/"${name}":"${version}" done From fb67447ccfc6247e0a69fa98efcb7d714a93f0fc Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Thu, 22 Feb 2024 04:19:19 +0200 Subject: [PATCH 25/87] Added PGP Key for Helm provenance Signed-off-by: Itay Grudev --- .github/workflows/release-publish.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index d2451f81e..5077a05e5 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -27,6 +27,11 @@ jobs: git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + - name: Import PGP Private Key + run: | + echo "${{ secrets.PGP_PRIVATE_KEY }}" | gpg --dearmor --output keyring.gpg + echo "${{ secrets.PGP_KEY_PASSPHRASE }}" > passphrase-file.txt + - name: Set up Helm uses: azure/setup-helm@29960d0f5f19214b88e1d9ba750a9914ab0f1a2f # v4.0.0 with: @@ -36,6 +41,10 @@ jobs: uses: helm/chart-releaser-action@a917fd15b20e8b64b94d9158ad54cd6345335584 # v1.6.0 env: CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + CR_KEY: helm-charts+no-reply@cloudnative-pg.io + CR_KEYRING: keyring.gpg + CR_PASSPHRASE_FILE: passphrase-file.txt + CR_SIGN: true CR_SKIP_EXISTING: true CR_GENERATE_RELEASE_NOTES: true CR_RELEASE_NAME_TEMPLATE: "{{ .Name }}-v{{ .Version }}" From 957c1febbab474af4a9fb043cb2c9d6b67a159e2 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Thu, 22 Feb 2024 12:06:32 +0200 Subject: [PATCH 26/87] Added Public Key to the repository Signed-off-by: Itay Grudev --- provenance.gpg | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 provenance.gpg diff --git a/provenance.gpg b/provenance.gpg new file mode 100644 index 000000000..944a40b82 --- /dev/null +++ b/provenance.gpg @@ -0,0 +1,83 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=ZWMG +-----END PGP PUBLIC KEY BLOCK----- From 001d78758b864f8ffd42799f6f44760c0ca5a671 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Thu, 22 Feb 2024 12:25:27 +0200 Subject: [PATCH 27/87] Improved PGP and passphrase security by storing them on /tmp and wiping them afterwards Signed-off-by: Itay Grudev --- .github/workflows/release-publish.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 5077a05e5..e1ac81667 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -29,8 +29,8 @@ jobs: - name: Import PGP Private Key run: | - echo "${{ secrets.PGP_PRIVATE_KEY }}" | gpg --dearmor --output keyring.gpg - echo "${{ secrets.PGP_KEY_PASSPHRASE }}" > passphrase-file.txt + echo "${{ secrets.PGP_PRIVATE_KEY }}" | gpg --dearmor --output /tmp/keyring.gpg + echo "${{ secrets.PGP_KEY_PASSPHRASE }}" > /tmp/passphrase-file.txt - name: Set up Helm uses: azure/setup-helm@29960d0f5f19214b88e1d9ba750a9914ab0f1a2f # v4.0.0 @@ -42,13 +42,17 @@ jobs: env: CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" CR_KEY: helm-charts+no-reply@cloudnative-pg.io - CR_KEYRING: keyring.gpg - CR_PASSPHRASE_FILE: passphrase-file.txt + CR_KEYRING: /tmp/keyring.gpg + CR_PASSPHRASE_FILE: /tmp/passphrase-file.txt CR_SIGN: true CR_SKIP_EXISTING: true CR_GENERATE_RELEASE_NOTES: true CR_RELEASE_NAME_TEMPLATE: "{{ .Name }}-v{{ .Version }}" + - name: Securely delete the PGP key and passphrase + if: always() + run: shred --remove=wipesync /tmp/keyring.gpg /tmp/passphrase-file.txt + - name: Login to GitHub Container Registry uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 with: From b2088c4513063885fb06c82683686e3bb13d443b Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Fri, 1 Mar 2024 12:02:29 +0200 Subject: [PATCH 28/87] Implemented Prometheus Rule for automated alerts (#193) feat(cluster): Prometheus Rule for automated alerts + runbooks for a basic set of alerts * Renamed: `cluster.monitoring.enablePodMonitor` to `cluster.monitoring.podMonitor.enabled` * New configuration option: `cluster.monitoring.prometheusRule.enabled` defaults to `true` Signed-off-by: Itay Grudev Signed-off-by: Gabriele Bartolini Co-authored-by: Gabriele Bartolini --- Makefile | 17 +- charts/cluster/README.md | 12 +- .../docs/runbooks/CNPGClusterHACritical.md | 49 +++ .../docs/runbooks/CNPGClusterHAWarning.md | 51 +++ .../CNPGClusterHighConnectionsCritical.md | 24 ++ .../CNPGClusterHighConnectionsWarning.md | 24 ++ .../runbooks/CNPGClusterHighReplicationLag.md | 31 ++ .../CNPGClusterInstancesOnSameNode.md | 28 ++ .../CNPGClusterLowDiskSpaceCritical.md | 31 ++ .../CNPGClusterLowDiskSpaceWarning.md | 31 ++ .../docs/runbooks/CNPGClusterOffline.md | 43 +++ .../runbooks/CNPGClusterZoneSpreadWarning.md | 37 ++ charts/cluster/examples/custom-queries.yaml | 3 +- charts/cluster/templates/NOTES.txt | 29 +- charts/cluster/templates/_helpers.tpl | 1 + charts/cluster/templates/cluster.yaml | 2 +- charts/cluster/templates/prometheus-rule.yaml | 177 +++++++++ charts/cluster/values.schema.json | 342 ++++++++++++++++++ charts/cluster/values.yaml | 9 +- 19 files changed, 908 insertions(+), 33 deletions(-) create mode 100644 charts/cluster/docs/runbooks/CNPGClusterHACritical.md create mode 100644 charts/cluster/docs/runbooks/CNPGClusterHAWarning.md create mode 100644 charts/cluster/docs/runbooks/CNPGClusterHighConnectionsCritical.md create mode 100644 charts/cluster/docs/runbooks/CNPGClusterHighConnectionsWarning.md create mode 100644 charts/cluster/docs/runbooks/CNPGClusterHighReplicationLag.md create mode 100644 charts/cluster/docs/runbooks/CNPGClusterInstancesOnSameNode.md create mode 100644 charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceCritical.md create mode 100644 charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceWarning.md create mode 100644 charts/cluster/docs/runbooks/CNPGClusterOffline.md create mode 100644 charts/cluster/docs/runbooks/CNPGClusterZoneSpreadWarning.md create mode 100644 charts/cluster/templates/prometheus-rule.yaml create mode 100644 charts/cluster/values.schema.json diff --git a/Makefile b/Makefile index 29b256f2e..ac2030a88 100644 --- a/Makefile +++ b/Makefile @@ -12,15 +12,12 @@ docs: ## Generate charts' docs using helm-docs (echo "Please, install https://github.com/norwoodj/helm-docs first" && exit 1) .PHONY: schema -schema: ## Generate charts' schema usign helm schema-gen plugin - @helm schema-gen charts/cloudnative-pg/values.yaml > charts/cloudnative-pg/values.schema.json || \ - (echo "Please, run: helm plugin install https://github.com/karuppiah7890/helm-schema-gen.git" && exit 1) +schema: cloudnative-pg-schema cluster-schema ## Generate charts' schema using helm-schema-gen -.PHONY: pgbench-deploy -pgbench-deploy: ## Installs pgbench chart - helm dependency update charts/pgbench - helm upgrade --install pgbench --atomic charts/pgbench +cloudnative-pg-schema: + @helm schema-gen charts/cloudnative-pg/values.yaml | cat > charts/cloudnative-pg/values.schema.json || \ + (echo "Please, run: helm plugin install https://github.com/karuppiah7890/helm-schema-gen.git" && exit 1) -.PHONY: pgbench-uninstall -pgbench-uninstall: ## Uninstalls cnpg-pgbench chart if present - @helm uninstall pgbench +cluster-schema: + @helm schema-gen charts/cluster/values.yaml | cat > charts/cluster/values.schema.json || \ + (echo "Please, run: helm plugin install https://github.com/karuppiah7890/helm-schema-gen.git" && exit 1) diff --git a/charts/cluster/README.md b/charts/cluster/README.md index d334c4389..812685364 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -88,9 +88,9 @@ Additionally you can specify the following parameters: ```yaml backups: scheduledBackups: - - name: daily-backup - schedule: "0 0 0 * * *" # Daily at midnight - backupOwnerReference: self + - name: daily-backup + schedule: "0 0 0 * * *" # Daily at midnight + backupOwnerReference: self ``` Each backup adapter takes it's own set of parameters, listed in the [Configuration options](#Configuration-options) section @@ -149,8 +149,10 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | cluster.instances | int | `3` | Number of instances | | cluster.logLevel | string | `"info"` | The instances' log level, one of the following values: error, warning, info (default), debug, trace | | cluster.monitoring.customQueries | list | `[]` | | -| cluster.monitoring.enablePodMonitor | bool | `false` | | -| cluster.postgresql | string | `nil` | Configuration of the PostgreSQL server See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration | +| cluster.monitoring.enabled | bool | `false` | | +| cluster.monitoring.podMonitor.enabled | bool | `true` | | +| cluster.monitoring.prometheusRule.enabled | bool | `true` | | +| cluster.postgresql | object | `{}` | Configuration of the PostgreSQL server See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration | | cluster.primaryUpdateMethod | string | `"switchover"` | Method to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated. It can be switchover (default) or in-place (restart). | | cluster.primaryUpdateStrategy | string | `"unsupervised"` | Strategy to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated: it can be automated (unsupervised - default) or manual (supervised) | | cluster.priorityClassName | string | `""` | | diff --git a/charts/cluster/docs/runbooks/CNPGClusterHACritical.md b/charts/cluster/docs/runbooks/CNPGClusterHACritical.md new file mode 100644 index 000000000..8be576c32 --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterHACritical.md @@ -0,0 +1,49 @@ +CNPGClusterHACritical +===================== + +Meaning +------- + +The `CNPGClusterHACritical` alert is triggered when the CloudNativePG cluster has no ready standby replicas. + +This can happen during either a normal failover or automated minor version upgrades in a cluster with 2 or less +instances. The replaced instance may need some time to catch-up with the cluster primary instance. + +This alarm will be always triggered if your cluster is configured to run with only 1 instance. In this case you +may want to silence it. + +Impact +------ + +Having no available replicas puts your cluster at a severe risk if the primary instance fails. The primary instance is +still online and able to serve queries, although connections to the `-ro` endpoint will fail. + +Diagnosis +--------- + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +Get the status of the CloudNativePG cluster instances: + +```bash +kubectl get pods -A -l "cnpg.io/podRole=instance" -o wide +``` + +Check the logs of the affected CloudNativePG instances: + +```bash +kubectl logs --namespace pod/ +``` + +Check the CloudNativePG operator logs: + +```bash +kubectl logs --namespace cnpg-system -l "app.kubernetes.io/name=cloudnative-pg" +``` + +Mitigation +---------- + +Refer to the [CloudNativePG Failure Modes](https://cloudnative-pg.io/documentation/current/failure_modes/) +and [CloudNativePG Troubleshooting](https://cloudnative-pg.io/documentation/current/troubleshooting/) documentation for +more information on how to troubleshoot and mitigate this issue. diff --git a/charts/cluster/docs/runbooks/CNPGClusterHAWarning.md b/charts/cluster/docs/runbooks/CNPGClusterHAWarning.md new file mode 100644 index 000000000..80acfad96 --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterHAWarning.md @@ -0,0 +1,51 @@ +CNPGClusterHAWarning +==================== + +Meaning +------- + +The `CNPGClusterHAWarning` alert is triggered when the CloudNativePG cluster ready standby replicas are less than `2`. + +This alarm will be always triggered if your cluster is configured to run with less than `3` instances. In this case you +may want to silence it. + +Impact +------ + +Having less than two available replicas puts your cluster at risk if another instance fails. The cluster is still able +to operate normally, although the `-ro` and `-r` endpoints operate at reduced capacity. + +This can happen during a normal failover or automated minor version upgrades. The replaced instance may need some time +to catch-up with the cluster primary instance which will trigger the alert if the operation takes more than 5 minutes. + +At `0` available ready replicas, a `CNPGClusterHACritical` alert will be triggered. + +Diagnosis +--------- + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +Get the status of the CloudNativePG cluster instances: + +```bash +kubectl get pods -A -l "cnpg.io/podRole=instance" -o wide +``` + +Check the logs of the affected CloudNativePG instances: + +```bash +kubectl logs --namespace pod/ +``` + +Check the CloudNativePG operator logs: + +```bash +kubectl logs --namespace cnpg-system -l "app.kubernetes.io/name=cloudnative-pg" +``` + +Mitigation +---------- + +Refer to the [CloudNativePG Failure Modes](https://cloudnative-pg.io/documentation/current/failure_modes/) +and [CloudNativePG Troubleshooting](https://cloudnative-pg.io/documentation/current/troubleshooting/) documentation for +more information on how to troubleshoot and mitigate this issue. diff --git a/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsCritical.md b/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsCritical.md new file mode 100644 index 000000000..2003421b9 --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsCritical.md @@ -0,0 +1,24 @@ +CNPGClusterHighConnectionsCritical +================================== + +Meaning +------- + +This alert is triggered when the number of connections to the CloudNativePG cluster instance exceeds 95% of its capacity. + +Impact +------ + +At 100% capacity, the CloudNativePG cluster instance will not be able to accept new connections. This will result in a service +disruption. + +Diagnosis +--------- + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +Mitigation +---------- + +* Increase the maximum number of connections by increasing the `max_connections` PostgreSQL parameter. +* Use connection pooling by enabling PgBouncer to reduce the number of connections to the database. diff --git a/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsWarning.md b/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsWarning.md new file mode 100644 index 000000000..636579f75 --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsWarning.md @@ -0,0 +1,24 @@ +CNPGClusterHighConnectionsWarning +================================= + +Meaning +------- + +This alert is triggered when the number of connections to the CloudNativePG cluster instance exceeds 85% of its capacity. + +Impact +------ + +At 100% capacity, the CloudNativePG cluster instance will not be able to accept new connections. This will result in a service +disruption. + +Diagnosis +--------- + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +Mitigation +---------- + +* Increase the maximum number of connections by increasing the `max_connections` PostgreSQL parameter. +* Use connection pooling by enabling PgBouncer to reduce the number of connections to the database. diff --git a/charts/cluster/docs/runbooks/CNPGClusterHighReplicationLag.md b/charts/cluster/docs/runbooks/CNPGClusterHighReplicationLag.md new file mode 100644 index 000000000..78963ce09 --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterHighReplicationLag.md @@ -0,0 +1,31 @@ +CNPGClusterHighReplicationLag +============================= + +Meaning +------- + +This alert is triggered when the replication lag of the CloudNativePG cluster exceed `1s`. + +Impact +------ + +High replication lag can cause the cluster replicas become out of sync. Queries to the `-r` and `-ro` endpoints may return stale data. +In the event of a failover, there may be data loss for the time period of the lag. + +Diagnosis +--------- + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +High replication lag can be caused by a number of factors, including: +* Network issues +* High load on the primary or replicas +* Long running queries +* Suboptimal PostgreSQL configuration, in particular small numbers of `max_wal_senders`. + +```yaml +kubectl exec --namespace --stdin --tty services/-rw -- psql -c "SELECT * from pg_stat_replication;" +``` + +Mitigation +---------- diff --git a/charts/cluster/docs/runbooks/CNPGClusterInstancesOnSameNode.md b/charts/cluster/docs/runbooks/CNPGClusterInstancesOnSameNode.md new file mode 100644 index 000000000..df309ffa9 --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterInstancesOnSameNode.md @@ -0,0 +1,28 @@ +CNPGClusterInstancesOnSameNode +============================ + +Meaning +------- + +The `CNPGClusterInstancesOnSameNode` alert is raised when two or more database pods are scheduled on the same node. + +Impact +------ + +A failure or scheduled downtime of a single node will lead to a potential service disruption and/or data loss. + +Diagnosis +--------- + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +```bash +kubectl get pods -A -l "cnpg.io/podRole=instance" -o wide +``` + +Mitigation +---------- + +1. Verify you have more than a single node with no taints, preventing pods to be scheduled there. +2. Verify your [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/) configuration. +3. For more information, please refer to the ["Scheduling"](https://cloudnative-pg.io/documentation/current/scheduling/) section in the documentation diff --git a/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceCritical.md b/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceCritical.md new file mode 100644 index 000000000..5b7355275 --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceCritical.md @@ -0,0 +1,31 @@ +CNPGClusterLowDiskSpaceCritical +=============================== + +Meaning +------- + +This alert is triggered when the disk space on the CloudNativePG cluster exceeds 90%. It can be triggered by either: + +* the PVC hosting the `PGDATA` (`storage` section) +* the PVC hosting WAL files (`walStorage` section), where applicable +* any PVC hosting a tablespace (`tablespaces` section) + +Impact +------ + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +Excessive disk space usage can lead fragmentation negatively impacting performance. Reaching 100% disk usage will result +in downtime and data loss. + +Diagnosis +--------- + +Mitigation +---------- + +If you experience issues with the WAL (Write-Ahead Logging) volume and have +set up continuous archiving, ensure that WAL archiving is functioning +correctly. This is crucial to avoid a buildup of WAL files in the `pg_wal` +folder. Monitor the `cnpg_collector_pg_wal_archive_status` metric, specifically +ensuring that the number of `ready` files does not increase linearly. diff --git a/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceWarning.md b/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceWarning.md new file mode 100644 index 000000000..36e56acf1 --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceWarning.md @@ -0,0 +1,31 @@ +CNPGClusterLowDiskSpaceWarning +============================== + +Meaning +------- + +This alert is triggered when the disk space on the CloudNativePG cluster exceeds 90%. It can be triggered by either: + +* the PVC hosting the `PGDATA` (`storage` section) +* the PVC hosting WAL files (`walStorage` section), where applicable +* any PVC hosting a tablespace (`tablespaces` section) + +Impact +------ + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +Excessive disk space usage can lead fragmentation negatively impacting performance. Reaching 100% disk usage will result +in downtime and data loss. + +Diagnosis +--------- + +Mitigation +---------- + +If you experience issues with the WAL (Write-Ahead Logging) volume and have +set up continuous archiving, ensure that WAL archiving is functioning +correctly. This is crucial to avoid a buildup of WAL files in the `pg_wal` +folder. Monitor the `cnpg_collector_pg_wal_archive_status` metric, specifically +ensuring that the number of `ready` files does not increase linearly. diff --git a/charts/cluster/docs/runbooks/CNPGClusterOffline.md b/charts/cluster/docs/runbooks/CNPGClusterOffline.md new file mode 100644 index 000000000..0e69db15b --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterOffline.md @@ -0,0 +1,43 @@ +CNPGClusterOffline +================== + +Meaning +------- + +The `CNPGClusterOffline` alert is triggered when there are no ready CloudNativePG instances. + +Impact +------ + +Having an offline cluster means your applications will not be able to access the database, leading to potential service +disruption. + +Diagnosis +--------- + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +Get the status of the CloudNativePG cluster instances: + +```bash +kubectl get pods -A -l "cnpg.io/podRole=instance" -o wide +``` + +Check the logs of the affected CloudNativePG instances: + +```bash +kubectl logs --namespace pod/ +``` + +Check the CloudNativePG operator logs: + +```bash +kubectl logs --namespace cnpg-system -l "app.kubernetes.io/name=cloudnative-pg" +``` + +Mitigation +---------- + +Refer to the [CloudNativePG Failure Modes](https://cloudnative-pg.io/documentation/current/failure_modes/) +and [CloudNativePG Troubleshooting](https://cloudnative-pg.io/documentation/current/troubleshooting/) documentation for +more information on how to troubleshoot and mitigate this issue. diff --git a/charts/cluster/docs/runbooks/CNPGClusterZoneSpreadWarning.md b/charts/cluster/docs/runbooks/CNPGClusterZoneSpreadWarning.md new file mode 100644 index 000000000..c5bdb05da --- /dev/null +++ b/charts/cluster/docs/runbooks/CNPGClusterZoneSpreadWarning.md @@ -0,0 +1,37 @@ +CNPGClusterZoneSpreadWarning +============================ + +Meaning +------- + +The `CNPGClusterZoneSpreadWarning` alert is raised when pods are not evenly distributed across availability zones. To be +more accurate, the alert is raised when the number of `pods > zones < 3`. + +Impact +------ + +The uneven distribution of pods across availability zones can lead to a single point of failure if a zone goes down. + +Diagnosis +--------- + +Use the [CloudNativePG Grafana Dashboard](https://grafana.com/grafana/dashboards/20417-cloudnativepg/). + +Get the status of the CloudNativePG cluster instances: + +```bash +kubectl get pods -A -l "cnpg.io/podRole=instance" -o wide +``` + +Get the nodes and their respective zones: + +```bash +kubectl get nodes --label-columns topology.kubernetes.io/zone +``` + +Mitigation +---------- + +1. Verify you have more than a single node with no taints, preventing pods to be scheduled in each availability zone. +2. Verify your [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/) configuration. +3. Delete the pods and their respective PVC that are not in the desired availability zone and allow the operator to repair the cluster. diff --git a/charts/cluster/examples/custom-queries.yaml b/charts/cluster/examples/custom-queries.yaml index 1e6ef16f6..7995202d8 100644 --- a/charts/cluster/examples/custom-queries.yaml +++ b/charts/cluster/examples/custom-queries.yaml @@ -4,6 +4,7 @@ mode: standalone cluster: instances: 1 monitoring: + enabled: true customQueries: - name: "pg_cache_hit" query: | @@ -20,4 +21,4 @@ cluster: description: "Cache hit ratio" backups: - enabled: false \ No newline at end of file + enabled: false diff --git a/charts/cluster/templates/NOTES.txt b/charts/cluster/templates/NOTES.txt index 28c0e6172..dd5142ecc 100644 --- a/charts/cluster/templates/NOTES.txt +++ b/charts/cluster/templates/NOTES.txt @@ -42,20 +42,21 @@ Configuration {{ $scheduledBackups = printf "%s, %s" $scheduledBackups .name }} {{- end -}} -╭───────────────────┬────────────────────────────────────────────╮ -│ Configuration │ Value │ -┝━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥ -│ Cluster mode │ {{ (printf "%-42s" .Values.mode) }} │ -│ Type │ {{ (printf "%-42s" .Values.type) }} │ -│ Image │ {{ include "cluster.color-info" (printf "%-42s" (include "cluster.imageName" .)) }} │ -│ Instances │ {{ include (printf "%s%s" "cluster.color-" $redundancyColor) (printf "%-42s" (toString .Values.cluster.instances)) }} │ -│ Backups │ {{ include (printf "%s%s" "cluster.color-" (ternary "ok" "error" .Values.backups.enabled)) (printf "%-42s" (ternary "Enabled" "Disabled" .Values.backups.enabled)) }} │ -│ Backup Provider │ {{ (printf "%-42s" (title .Values.backups.provider)) }} │ -│ Scheduled Backups │ {{ (printf "%-42s" $scheduledBackups) }} │ -│ Storage │ {{ (printf "%-42s" .Values.cluster.storage.size) }} │ -│ Storage Class │ {{ (printf "%-42s" (default "Default" .Values.cluster.storage.storageClass)) }} │ -│ PGBouncer │ {{ (printf "%-42s" (ternary "Enabled" "Disabled" .Values.pooler.enabled)) }} │ -╰───────────────────┴────────────────────────────────────────────╯ +╭───────────────────┬────────────────────────────────────────────────────────╮ +│ Configuration │ Value │ +┝━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥ +│ Cluster mode │ {{ (printf "%-54s" .Values.mode) }} │ +│ Type │ {{ (printf "%-54s" .Values.type) }} │ +│ Image │ {{ include "cluster.color-info" (printf "%-54s" (include "cluster.imageName" .)) }} │ +│ Instances │ {{ include (printf "%s%s" "cluster.color-" $redundancyColor) (printf "%-54s" (toString .Values.cluster.instances)) }} │ +│ Backups │ {{ include (printf "%s%s" "cluster.color-" (ternary "ok" "error" .Values.backups.enabled)) (printf "%-54s" (ternary "Enabled" "Disabled" .Values.backups.enabled)) }} │ +│ Backup Provider │ {{ (printf "%-54s" (title .Values.backups.provider)) }} │ +│ Scheduled Backups │ {{ (printf "%-54s" $scheduledBackups) }} │ +│ Storage │ {{ (printf "%-54s" .Values.cluster.storage.size) }} │ +│ Storage Class │ {{ (printf "%-54s" (default "Default" .Values.cluster.storage.storageClass)) }} │ +│ PGBouncer │ {{ (printf "%-54s" (ternary "Enabled" "Disabled" .Values.pooler.enabled)) }} │ +│ Monitoring │ {{ include (printf "%s%s" "cluster.color-" (ternary "ok" "error" .Values.cluster.monitoring.enabled)) (printf "%-54s" (ternary "Enabled" "Disabled" .Values.cluster.monitoring.enabled)) }} │ +╰───────────────────┴────────────────────────────────────────────────────────╯ {{ if not .Values.backups.enabled }} {{- include "cluster.color-error" "Warning! Backups not enabled. Recovery will not be possible! Do not use this configuration in production.\n" }} diff --git a/charts/cluster/templates/_helpers.tpl b/charts/cluster/templates/_helpers.tpl index b00846d60..db3c253e5 100644 --- a/charts/cluster/templates/_helpers.tpl +++ b/charts/cluster/templates/_helpers.tpl @@ -48,6 +48,7 @@ Selector labels {{- define "cluster.selectorLabels" -}} app.kubernetes.io/name: {{ include "cluster.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/part-of: cloudnative-pg {{- end }} {{/* diff --git a/charts/cluster/templates/cluster.yaml b/charts/cluster/templates/cluster.yaml index 4ec251698..1410ded56 100644 --- a/charts/cluster/templates/cluster.yaml +++ b/charts/cluster/templates/cluster.yaml @@ -54,7 +54,7 @@ spec: {{ end }} monitoring: - enablePodMonitor: {{ .Values.cluster.monitoring.enablePodMonitor }} + enablePodMonitor: {{ and .Values.cluster.monitoring.enabled .Values.cluster.monitoring.podMonitor.enabled }} {{- if not (empty .Values.cluster.monitoring.customQueries) }} customQueriesConfigMap: - name: {{ include "cluster.fullname" . }}-monitoring diff --git a/charts/cluster/templates/prometheus-rule.yaml b/charts/cluster/templates/prometheus-rule.yaml new file mode 100644 index 000000000..3da33025b --- /dev/null +++ b/charts/cluster/templates/prometheus-rule.yaml @@ -0,0 +1,177 @@ +{{- if and .Values.cluster.monitoring.enabled .Values.cluster.monitoring.prometheusRule.enabled -}} +{{- $value := "{{ $value }}" -}} +{{- $namespace := .Release.Namespace -}} +{{- $cluster := printf "%s/%s" $namespace (include "cluster.fullname" .)}} +{{- $labels := dict "job" "{{ $labels.job }}" "node" "{{ $labels.node }}" "pod" "{{ $labels.pod }}" -}} +{{- $podSelector := printf "%s-([1-9][0-9]*)$" (include "cluster.fullname" .) -}} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + labels: + {{- include "cluster.labels" . | nindent 4 }} + {{- with .Values.cluster.additionalLabels }} + {{ toYaml . | nindent 4 }} + {{- end }} + name: {{ include "cluster.fullname" . }}-alert-rules +spec: + groups: + - name: cloudnative-pg/{{ include "cluster.fullname" . }} + rules: + - alert: CNPGClusterHAWarning + annotations: + summary: CNPG Cluster less than 2 standby replicas. + description: |- + CloudNativePG Cluster "{{ $labels.job }}" has only {{ $value }} standby replicas, putting + your cluster at risk if another instance fails. The cluster is still able to operate normally, although + the `-ro` and `-r` endpoints operate at reduced capacity. + + This can happen during a normal fail-over or automated minor version upgrades. The replaced instance may + need some time to catch-up with the cluster primary instance. + + This alarm will be constantly triggered if your cluster is configured to run with less than 3 instances. + In this case you may want to silence it. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHAWarning.md + expr: | + max by (job) (cnpg_pg_replication_streaming_replicas{namespace="{{ $namespace }}"} - cnpg_pg_replication_is_wal_receiver_up{namespace="{{ $namespace }}"}) < 2 + for: 5m + labels: + severity: warning + - alert: CNPGClusterHACritical + annotations: + summary: CNPG Cluster has no standby replicas! + description: |- + CloudNativePG Cluster "{{ $labels.job }}" has no ready standby replicas. Your cluster at a severe + risk of data loss and downtime if the primary instance fails. + + The primary instance is still online and able to serve queries, although connections to the `-ro` endpoint + will fail. The `-r` endpoint os operating at reduced capacity and all traffic is being served by the main. + + This can happen during a normal fail-over or automated minor version upgrades in a cluster with 2 or less + instances. The replaced instance may need some time to catch-up with the cluster primary instance. + + This alarm will be always trigger if your cluster is configured to run with only 1 instance. In this + case you may want to silence it. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHACritical.md + expr: | + max by (job) (cnpg_pg_replication_streaming_replicas{namespace="{{ $namespace }}"} - cnpg_pg_replication_is_wal_receiver_up{namespace="{{ $namespace }}"}) < 1 + for: 5m + labels: + severity: critical + - alert: CNPGClusterOffline + annotations: + summary: CNPG Cluster has no running instances! + description: |- + CloudNativePG Cluster "{{ $labels.job }}" has no ready instances. + + Having an offline cluster means your applications will not be able to access the database, leading to + potential service disruption and/or data loss. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterOffline.md + expr: | + ({{ .Values.cluster.instances }} - count(cnpg_collector_up{namespace=~"{{ $namespace }}",pod=~"{{ $podSelector }}"}) OR vector(0)) > 0 + for: 5m + labels: + severity: critical + - alert: CNPGClusterZoneSpreadWarning + annotations: + summary: CNPG Cluster instances in the same zone. + description: |- + CloudNativePG Cluster "{{ $cluster }}" has instances in the same availability zone. + + A disaster in one availability zone will lead to a potential service disruption and/or data loss. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterZoneSpreadWarning.md + expr: | + {{ .Values.cluster.instances }} > count(count by (label_topology_kubernetes_io_zone) (kube_pod_info{namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"} * on(node,instance) group_left(label_topology_kubernetes_io_zone) kube_node_labels)) < 3 + for: 5m + labels: + severity: warning + - alert: CNPGClusterInstancesOnSameNode + annotations: + summary: CNPG Cluster instances are located on the same node. + description: |- + CloudNativePG Cluster "{{ $cluster }}" has {{ $value }} + instances on the same node {{ $labels.node }}. + + A failure or scheduled downtime of a single node will lead to a potential service disruption and/or data loss. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterInstancesOnSameNode.md + expr: | + count by (node) (kube_pod_info{namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) > 1 + for: 5m + labels: + severity: warning + - alert: CNPGClusterHighReplicationLag + annotations: + summary: CNPG Cluster high replication lag + description: |- + CloudNativePG Cluster "{{ $cluster }}" is experiencing a high replication lag of + {{ "{{ $value }}" }}ms. + + High replication lag indicates network issues, busy instances, slow queries or suboptimal configuration. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighReplicationLag.md + expr: | + max(cnpg_pg_replication_lag{namespace=~"{{ $namespace }}",pod=~"{{ $podSelector }}"}) * 1000 > 1000 + for: 5m + labels: + severity: warning + - alert: CNPGClusterHighConnectionsWarning + annotations: + summary: CNPG Instance is approaching the maximum number of connections. + description: |- + CloudNativePG Cluster "{{ $cluster }}" instance {{ $labels.pod }} is using {{ "{{ $value }}" }}% of + the maximum number of connections. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsWarning.md + expr: | + sum by (pod) (cnpg_backends_total{namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) * 100 > 80 + for: 5m + labels: + severity: warning + - alert: CNPGClusterHighConnectionsCritical + annotations: + summary: CNPG Instance maximum number of connections critical! + description: |- + CloudNativePG Cluster "{{ $cluster }}" instance {{ $labels.pod }} is using {{ "{{ $value }}" }}% of + the maximum number of connections. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsCritical.md + expr: | + sum by (pod) (cnpg_backends_total{namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) * 100 > 95 + for: 5m + labels: + severity: critical + - alert: CNPGClusterLowDiskSpaceWarning + annotations: + summary: CNPG Instance is running out of disk space. + description: |- + CloudNativePG Cluster "{{ $cluster }}" is running low on disk space. Check attached PVCs. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceWarning.md + expr: | + max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}"} / kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}"})) > 0.7 OR + max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-wal"} / kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-wal"})) > 0.7 OR + max(sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-tbs.*"}) + / + sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-tbs.*"}) + * + on(namespace, persistentvolumeclaim) group_left(volume) + kube_pod_spec_volumes_persistentvolumeclaims_info{pod=~"{{ $podSelector }}"} + ) > 0.7 + for: 5m + labels: + severity: warning + - alert: CNPGClusterLowDiskSpaceCritical + annotations: + summary: CNPG Instance is running out of disk space! + description: |- + CloudNativePG Cluster "{{ $cluster }}" is running extremely low on disk space. Check attached PVCs! + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceCritical.md + expr: | + max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}"} / kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}"})) > 0.9 OR + max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-wal"} / kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-wal"})) > 0.9 OR + max(sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-tbs.*"}) + / + sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-tbs.*"}) + * + on(namespace, persistentvolumeclaim) group_left(volume) + kube_pod_spec_volumes_persistentvolumeclaims_info{pod=~"{{ $podSelector }}"} + ) > 0.9 + for: 5m + labels: + severity: warning +{{ end }} diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json new file mode 100644 index 000000000..49550780b --- /dev/null +++ b/charts/cluster/values.schema.json @@ -0,0 +1,342 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "backups": { + "type": "object", + "properties": { + "azure": { + "type": "object", + "properties": { + "connectionString": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "inheritFromAzureAD": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "storageAccount": { + "type": "string" + }, + "storageKey": { + "type": "string" + }, + "storageSasToken": { + "type": "string" + } + } + }, + "destinationPath": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "endpointURL": { + "type": "string" + }, + "google": { + "type": "object", + "properties": { + "applicationCredentials": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "gkeEnvironment": { + "type": "boolean" + }, + "path": { + "type": "string" + } + } + }, + "provider": { + "type": "string" + }, + "retentionPolicy": { + "type": "string" + }, + "s3": { + "type": "object", + "properties": { + "accessKey": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "path": { + "type": "string" + }, + "region": { + "type": "string" + }, + "secretKey": { + "type": "string" + } + } + }, + "scheduledBackups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backupOwnerReference": { + "type": "string" + }, + "name": { + "type": "string" + }, + "schedule": { + "type": "string" + } + } + } + } + } + }, + "cluster": { + "type": "object", + "properties": { + "additionalLabels": { + "type": "object" + }, + "affinity": { + "type": "object", + "properties": { + "topologyKey": { + "type": "string" + } + } + }, + "annotations": { + "type": "object" + }, + "certificates": { + "type": "null" + }, + "enableSuperuserAccess": { + "type": "boolean" + }, + "imageName": { + "type": "string" + }, + "imagePullPolicy": { + "type": "string" + }, + "imagePullSecrets": { + "type": "array" + }, + "initdb": { + "type": "object" + }, + "instances": { + "type": "integer" + }, + "logLevel": { + "type": "string" + }, + "monitoring": { + "type": "object", + "properties": { + "customQueries": { + "type": "array" + }, + "enabled": { + "type": "boolean" + }, + "podMonitor": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "prometheusRule": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + } + } + }, + "postgresql": { + "type": "object" + }, + "primaryUpdateMethod": { + "type": "string" + }, + "primaryUpdateStrategy": { + "type": "string" + }, + "priorityClassName": { + "type": "string" + }, + "resources": { + "type": "null" + }, + "storage": { + "type": "object", + "properties": { + "size": { + "type": "string" + }, + "storageClass": { + "type": "string" + } + } + }, + "superuserSecret": { + "type": "string" + } + } + }, + "fullnameOverride": { + "type": "string" + }, + "mode": { + "type": "string" + }, + "nameOverride": { + "type": "string" + }, + "pooler": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "instances": { + "type": "integer" + }, + "parameters": { + "type": "object", + "properties": { + "default_pool_size": { + "type": "string" + }, + "max_client_conn": { + "type": "string" + } + } + }, + "poolMode": { + "type": "string" + } + } + }, + "recovery": { + "type": "object", + "properties": { + "azure": { + "type": "object", + "properties": { + "connectionString": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "inheritFromAzureAD": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "storageAccount": { + "type": "string" + }, + "storageKey": { + "type": "string" + }, + "storageSasToken": { + "type": "string" + } + } + }, + "backupName": { + "type": "string" + }, + "clusterName": { + "type": "string" + }, + "destinationPath": { + "type": "string" + }, + "endpointURL": { + "type": "string" + }, + "google": { + "type": "object", + "properties": { + "applicationCredentials": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "gkeEnvironment": { + "type": "boolean" + }, + "path": { + "type": "string" + } + } + }, + "method": { + "type": "string" + }, + "pitrTarget": { + "type": "object", + "properties": { + "time": { + "type": "string" + } + } + }, + "provider": { + "type": "string" + }, + "s3": { + "type": "object", + "properties": { + "accessKey": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "path": { + "type": "string" + }, + "region": { + "type": "string" + }, + "secretKey": { + "type": "string" + } + } + } + } + }, + "type": { + "type": "string" + } + } +} diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index dec1fc9b4..02f967133 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -132,7 +132,11 @@ cluster: superuserSecret: "" monitoring: - enablePodMonitor: false + enabled: false + podMonitor: + enabled: true + prometheusRule: + enabled: true customQueries: [] # - name: "pg_cache_hit_ratio" # query: "SELECT current_database() as datname, sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratio FROM pg_statio_user_tables;" @@ -146,7 +150,8 @@ cluster: # -- Configuration of the PostgreSQL server # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration - postgresql: + postgresql: {} + # max_connections: 300 # -- BootstrapInitDB is the configuration of the bootstrap process when initdb is used # See: https://cloudnative-pg.io/documentation/current/bootstrap/ From 962184aef7be05dc4aa7f55c3ac815c44c9ff037 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:24:41 +0200 Subject: [PATCH 29/87] Release cluster-v0.0.2 (#200) * Implemented Prometheus Rule for automated alerts (#193) * Renamed: `cluster.monitoring.enablePodMonitor` to `cluster.monitoring.podMonitor.enabled` * New configuration option: `cluster.monitoring.prometheusRule.enabled` defaults to `true` Signed-off-by: Itay Grudev Co-authored-by: Itay Grudev --- RELEASE.md | 12 ++++++++++-- charts/cluster/Chart.yaml | 2 +- charts/cluster/README.md | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 8baad7bcc..41fa2b859 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -17,6 +17,9 @@ release (e.g. 1.17.1) In order to create a new release of the `cloudnative-pg` chart, follow these steps: 1. Take note of the current value of the release: see `.version` in `charts/cloudnative-pg/Chart.yaml` + ```bash + yq -r '.version' charts/cloudnative-pg/Chart.yaml + ``` 2. Decide which version to create, depending on the kind of jump of the CloudNativePG release, following semver semantics. For this document, let's call it `X.Y.Z` ```bash @@ -28,7 +31,7 @@ In order to create a new release of the `cloudnative-pg` chart, follow these ste ``` 4. Update the `.version` in the [Chart.yaml](./charts/cloudnative-pg/Chart.yaml) file to `"X.Y.Z"` ```bash - sed -i -E "s/^version: ([0-9]+.?)+/version: $APP_VERSION/" charts/cloudnative-pg/Chart.yaml + sed -i -E "s/^version: ([0-9]+.?)+/version: $NEW_VERSION/" charts/cloudnative-pg/Chart.yaml ``` 5. Update everything else as required, e.g. if releasing due to a new `cloudnative-pg` version being released, you might want to update the following: @@ -67,6 +70,7 @@ In order to create a new release of the `cloudnative-pg` chart, follow these ste ``` 7. Commit and add the relevant information you wish in the commit message. ```bash + git add . git commit -S -s -m "Release cloudnative-pg-v$NEW_VERSION" --edit ``` 8. Push the new branch @@ -94,6 +98,9 @@ In order to create a new release of the `cloudnative-pg` chart, follow these ste In order to create a new release of the `cluster` chart, follow these steps: 1. Take note of the current value of the release: see `.version` in `charts/cluster/Chart.yaml` + ```bash + yq -r '.version' charts/cluster/Chart.yaml + ``` 2. Decide which version to create, depending on the kind of changes and backwards compatibility, following semver semantics. For this document, let's call it `X.Y.Z` ```bash @@ -105,7 +112,7 @@ In order to create a new release of the `cluster` chart, follow these steps: ``` 4. Update the `.version` in the [Chart.yaml](./charts/cluster/Chart.yaml) file to `"X.Y.Z"` ```bash - sed -i -E "s/^version: ([0-9]+.?)+/version: $APP_VERSION/" charts/cluster/Chart.yaml + sed -i -E "s/^version: ([0-9]+.?)+/version: $NEW_VERSION/" charts/cluster/Chart.yaml ``` 5. Run `make docs schema` to regenerate the docs and the values schema in case it is needed ```bash @@ -113,6 +120,7 @@ In order to create a new release of the `cluster` chart, follow these steps: ``` 6. Commit and add the relevant information you wish in the commit message. ```bash + git add . git commit -S -s -m "Release cluster-v$NEW_VERSION" --edit ``` 7. Push the new branch diff --git a/charts/cluster/Chart.yaml b/charts/cluster/Chart.yaml index 9cac5cbc9..dcae9cb8c 100644 --- a/charts/cluster/Chart.yaml +++ b/charts/cluster/Chart.yaml @@ -18,7 +18,7 @@ name: cluster description: Deploys and manages a CloudNativePG cluster and its associated resources. icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: 0.0.1 +version: 0.0.2 sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 812685364..3c1f33995 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -1,6 +1,6 @@ # cluster -![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![Version: 0.0.2](https://img.shields.io/badge/Version-0.0.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) > **Warning** > ### This chart is under active development. From 3d88ae3dc1ad22ff873533e35d1c3c5c5c09a3c0 Mon Sep 17 00:00:00 2001 From: NigelVanHattum Date: Tue, 12 Mar 2024 08:30:45 +0100 Subject: [PATCH 30/87] feat(cluster): Added support for specifying user UID & GID (#207) Closes #206 --- charts/cluster/README.md | 2 ++ charts/cluster/templates/cluster.yaml | 2 ++ charts/cluster/values.schema.json | 6 ++++++ charts/cluster/values.yaml | 6 ++++++ 4 files changed, 16 insertions(+) diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 3c1f33995..0b882f2f8 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -152,6 +152,8 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | cluster.monitoring.enabled | bool | `false` | | | cluster.monitoring.podMonitor.enabled | bool | `true` | | | cluster.monitoring.prometheusRule.enabled | bool | `true` | | +| cluster.postgresGID | int | `26` | The GID of the postgres user inside the image, defaults to 26 | +| cluster.postgresUID | int | `26` | The UID of the postgres user inside the image, defaults to 26 | | cluster.postgresql | object | `{}` | Configuration of the PostgreSQL server See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration | | cluster.primaryUpdateMethod | string | `"switchover"` | Method to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated. It can be switchover (default) or in-place (restart). | | cluster.primaryUpdateStrategy | string | `"unsupervised"` | Strategy to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated: it can be automated (unsupervised - default) or manual (supervised) | diff --git a/charts/cluster/templates/cluster.yaml b/charts/cluster/templates/cluster.yaml index 1410ded56..4c40a12d0 100644 --- a/charts/cluster/templates/cluster.yaml +++ b/charts/cluster/templates/cluster.yaml @@ -19,6 +19,8 @@ spec: imagePullSecrets: {{- . | toYaml | nindent 4 }} {{- end }} + postgresUID: {{ .Values.cluster.postgresUID }} + postgresGID: {{ .Values.cluster.postgresGID }} storage: size: {{ .Values.cluster.storage.size }} storageClass: {{ .Values.cluster.storage.storageClass }} diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 49550780b..75a9551b1 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -176,6 +176,12 @@ "postgresql": { "type": "object" }, + "postgresUID": { + "type": "integer" + }, + "postgresGID": { + "type": "integer" + }, "primaryUpdateMethod": { "type": "string" }, diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index 02f967133..ff3c79a19 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -90,6 +90,12 @@ cluster: size: 8Gi storageClass: "" + # -- The UID of the postgres user inside the image, defaults to 26 + postgresUID: 26 + + # -- The GID of the postgres user inside the image, defaults to 26 + postgresGID: 26 + # -- Resources requirements of every generated Pod. # Please refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ for more information. # We strongly advise you use the same setting for limits and requests so that your cluster pods are given a Guaranteed QoS. From aab8c3ad48b402cff43235972ad797204378ae39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 07:31:07 +0000 Subject: [PATCH 31/87] chore(deps): update azure/setup-helm action to v4.1.0 --- .github/workflows/release-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index e1ac81667..39d28a91c 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -33,7 +33,7 @@ jobs: echo "${{ secrets.PGP_KEY_PASSPHRASE }}" > /tmp/passphrase-file.txt - name: Set up Helm - uses: azure/setup-helm@29960d0f5f19214b88e1d9ba750a9914ab0f1a2f # v4.0.0 + uses: azure/setup-helm@b7246b12e77f7134dc2d460a3d5bad15bbe29390 # v4.1.0 with: version: v3.14.1 From 3ed35ae437d5dbc536cb66a20692afe43c17e751 Mon Sep 17 00:00:00 2001 From: Dmitry Pchelintsev Date: Wed, 13 Mar 2024 14:14:29 +0300 Subject: [PATCH 32/87] Bug Fix: Correct ref to superuserSecret (#209) Signed-off-by: Dmitry Pchelintsev --- charts/cluster/templates/cluster.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/charts/cluster/templates/cluster.yaml b/charts/cluster/templates/cluster.yaml index 4c40a12d0..9634dc8f0 100644 --- a/charts/cluster/templates/cluster.yaml +++ b/charts/cluster/templates/cluster.yaml @@ -43,8 +43,10 @@ spec: {{- toYaml . | nindent 4 }} {{ end }} enableSuperuserAccess: {{ .Values.cluster.enableSuperuserAccess }} - superuserSecret: {{ .Values.cluster.superuserSecret }} - + {{- with .Values.cluster.superuserSecret }} + superuserSecret: + name: {{ . }} + {{ end }} postgresql: shared_preload_libraries: {{- if eq .Values.type "timescaledb" }} From b86396eb8bb5d38cf7009e10001c262bee6596b1 Mon Sep 17 00:00:00 2001 From: Mykyta Orlov Date: Wed, 13 Mar 2024 16:10:10 +0200 Subject: [PATCH 33/87] Grafana dashboard label to "1" (#203) According to most used monitoring stack label value should be "1" to be automatically loaded. While it's not very hard to find it took me some time to figure out why dashboard wasn't loaded. Signed-off-by: Mykyta Orlov --- charts/cloudnative-pg/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/cloudnative-pg/values.yaml b/charts/cloudnative-pg/values.yaml index ce32e97d1..8304224cf 100644 --- a/charts/cloudnative-pg/values.yaml +++ b/charts/cloudnative-pg/values.yaml @@ -148,7 +148,7 @@ monitoring: # -- Label that ConfigMaps should have to be loaded as dashboards. sidecarLabel: "grafana_dashboard" # -- Label value that ConfigMaps should have to be loaded as dashboards. - sidecarLabelValue: "" + sidecarLabelValue: "1" # Default monitoring queries monitoringQueriesConfigMap: From 7a9f4ec35f17024ac408a436319dfa2c2791f89f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:29:27 +0200 Subject: [PATCH 34/87] chore(deps): update docker/login-action action to v3.1.0 (#212) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 39d28a91c..fb5905691 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -54,7 +54,7 @@ jobs: run: shred --remove=wipesync /tmp/keyring.gpg /tmp/passphrase-file.txt - name: Login to GitHub Container Registry - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 with: registry: ghcr.io username: ${{ github.actor }} From 9bf91a9159d4813be7de52be4cfd2e8c2739bc86 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:32:07 +0200 Subject: [PATCH 35/87] Release cloudnative-pg-v0.20.2 (#214) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Niccolò Fei Co-authored-by: Niccolò Fei --- charts/cloudnative-pg/Chart.yaml | 4 +- charts/cloudnative-pg/README.md | 4 +- .../cloudnative-pg/templates/crds/crds.yaml | 11358 ++++++++-------- 3 files changed, 5871 insertions(+), 5495 deletions(-) diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index 6068c94f6..b5b7763b2 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,12 +18,12 @@ name: cloudnative-pg description: CloudNativePG Operator Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.20.1" +version: "0.20.2" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.22.1" +appVersion: "1.22.2" sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 6ce2782ad..c0ecd7f4c 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.20.1](https://img.shields.io/badge/Version-0.20.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.22.1](https://img.shields.io/badge/AppVersion-1.22.1-informational?style=flat-square) +![Version: 0.20.2](https://img.shields.io/badge/Version-0.20.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.22.2](https://img.shields.io/badge/AppVersion-1.22.2-informational?style=flat-square) CloudNativePG Operator Helm Chart @@ -39,7 +39,7 @@ CloudNativePG Operator Helm Chart | monitoring.grafanaDashboard.create | bool | `false` | | | monitoring.grafanaDashboard.namespace | string | `""` | Allows overriding the namespace where the ConfigMap will be created, defaulting to the same one as the Release. | | monitoring.grafanaDashboard.sidecarLabel | string | `"grafana_dashboard"` | Label that ConfigMaps should have to be loaded as dashboards. | -| monitoring.grafanaDashboard.sidecarLabelValue | string | `""` | Label value that ConfigMaps should have to be loaded as dashboards. | +| monitoring.grafanaDashboard.sidecarLabelValue | string | `"1"` | Label value that ConfigMaps should have to be loaded as dashboards. | | monitoring.podMonitorEnabled | bool | `false` | Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. | | monitoringQueriesConfigMap.name | string | `"cnpg-default-monitoring"` | The name of the default monitoring configmap. | | monitoringQueriesConfigMap.queries | string | `"backends:\n query: |\n SELECT sa.datname\n , sa.usename\n , sa.application_name\n , states.state\n , COALESCE(sa.count, 0) AS total\n , COALESCE(sa.max_tx_secs, 0) AS max_tx_duration_seconds\n FROM ( VALUES ('active')\n , ('idle')\n , ('idle in transaction')\n , ('idle in transaction (aborted)')\n , ('fastpath function call')\n , ('disabled')\n ) AS states(state)\n LEFT JOIN (\n SELECT datname\n , state\n , usename\n , COALESCE(application_name, '') AS application_name\n , COUNT(*)\n , COALESCE(EXTRACT (EPOCH FROM (max(now() - xact_start))), 0) AS max_tx_secs\n FROM pg_catalog.pg_stat_activity\n GROUP BY datname, state, usename, application_name\n ) sa ON states.state = sa.state\n WHERE sa.usename IS NOT NULL\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - usename:\n usage: \"LABEL\"\n description: \"Name of the user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - state:\n usage: \"LABEL\"\n description: \"State of the backend\"\n - total:\n usage: \"GAUGE\"\n description: \"Number of backends\"\n - max_tx_duration_seconds:\n usage: \"GAUGE\"\n description: \"Maximum duration of a transaction in seconds\"\n\nbackends_waiting:\n query: |\n SELECT count(*) AS total\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks\n ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n WHERE NOT blocked_locks.granted\n metrics:\n - total:\n usage: \"GAUGE\"\n description: \"Total number of backends that are currently waiting on other queries\"\n\npg_database:\n query: |\n SELECT datname\n , pg_catalog.pg_database_size(datname) AS size_bytes\n , pg_catalog.age(datfrozenxid) AS xid_age\n , pg_catalog.mxid_age(datminmxid) AS mxid_age\n FROM pg_catalog.pg_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - size_bytes:\n usage: \"GAUGE\"\n description: \"Disk space used by the database\"\n - xid_age:\n usage: \"GAUGE\"\n description: \"Number of transactions from the frozen XID to the current one\"\n - mxid_age:\n usage: \"GAUGE\"\n description: \"Number of multiple transactions (Multixact) from the frozen XID to the current one\"\n\npg_postmaster:\n query: |\n SELECT EXTRACT(EPOCH FROM pg_postmaster_start_time) AS start_time\n FROM pg_catalog.pg_postmaster_start_time()\n metrics:\n - start_time:\n usage: \"GAUGE\"\n description: \"Time at which postgres started (based on epoch)\"\n\npg_replication:\n query: \"SELECT CASE WHEN (\n NOT pg_catalog.pg_is_in_recovery()\n OR pg_catalog.pg_last_wal_receive_lsn() = pg_catalog.pg_last_wal_replay_lsn())\n THEN 0\n ELSE GREATEST (0,\n EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp())))\n END AS lag,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up,\n (SELECT count(*) FROM pg_catalog.pg_stat_replication) AS streaming_replicas\"\n metrics:\n - lag:\n usage: \"GAUGE\"\n description: \"Replication lag behind primary in seconds\"\n - in_recovery:\n usage: \"GAUGE\"\n description: \"Whether the instance is in recovery\"\n - is_wal_receiver_up:\n usage: \"GAUGE\"\n description: \"Whether the instance wal_receiver is up\"\n - streaming_replicas:\n usage: \"GAUGE\"\n description: \"Number of streaming replicas connected to the instance\"\n\npg_replication_slots:\n query: |\n SELECT slot_name,\n slot_type,\n database,\n active,\n (CASE pg_catalog.pg_is_in_recovery()\n WHEN TRUE THEN pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_last_wal_receive_lsn(), restart_lsn)\n ELSE pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn)\n END) as pg_wal_lsn_diff\n FROM pg_catalog.pg_replication_slots\n WHERE NOT temporary\n metrics:\n - slot_name:\n usage: \"LABEL\"\n description: \"Name of the replication slot\"\n - slot_type:\n usage: \"LABEL\"\n description: \"Type of the replication slot\"\n - database:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - active:\n usage: \"GAUGE\"\n description: \"Flag indicating whether the slot is active\"\n - pg_wal_lsn_diff:\n usage: \"GAUGE\"\n description: \"Replication lag in bytes\"\n\npg_stat_archiver:\n query: |\n SELECT archived_count\n , failed_count\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_archived_time)), -1) AS seconds_since_last_archival\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_failed_time)), -1) AS seconds_since_last_failure\n , COALESCE(EXTRACT(EPOCH FROM last_archived_time), -1) AS last_archived_time\n , COALESCE(EXTRACT(EPOCH FROM last_failed_time), -1) AS last_failed_time\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_archived_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_archived_wal_start_lsn\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_failed_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_failed_wal_start_lsn\n , EXTRACT(EPOCH FROM stats_reset) AS stats_reset_time\n FROM pg_catalog.pg_stat_archiver\n metrics:\n - archived_count:\n usage: \"COUNTER\"\n description: \"Number of WAL files that have been successfully archived\"\n - failed_count:\n usage: \"COUNTER\"\n description: \"Number of failed attempts for archiving WAL files\"\n - seconds_since_last_archival:\n usage: \"GAUGE\"\n description: \"Seconds since the last successful archival operation\"\n - seconds_since_last_failure:\n usage: \"GAUGE\"\n description: \"Seconds since the last failed archival operation\"\n - last_archived_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving succeeded\"\n - last_failed_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving failed\"\n - last_archived_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Archived WAL start LSN\"\n - last_failed_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Last failed WAL LSN\"\n - stats_reset_time:\n usage: \"GAUGE\"\n description: \"Time at which these statistics were last reset\"\n\npg_stat_bgwriter:\n query: |\n SELECT checkpoints_timed\n , checkpoints_req\n , checkpoint_write_time\n , checkpoint_sync_time\n , buffers_checkpoint\n , buffers_clean\n , maxwritten_clean\n , buffers_backend\n , buffers_backend_fsync\n , buffers_alloc\n FROM pg_catalog.pg_stat_bgwriter\n metrics:\n - checkpoints_timed:\n usage: \"COUNTER\"\n description: \"Number of scheduled checkpoints that have been performed\"\n - checkpoints_req:\n usage: \"COUNTER\"\n description: \"Number of requested checkpoints that have been performed\"\n - checkpoint_write_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds\"\n - checkpoint_sync_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds\"\n - buffers_checkpoint:\n usage: \"COUNTER\"\n description: \"Number of buffers written during checkpoints\"\n - buffers_clean:\n usage: \"COUNTER\"\n description: \"Number of buffers written by the background writer\"\n - maxwritten_clean:\n usage: \"COUNTER\"\n description: \"Number of times the background writer stopped a cleaning scan because it had written too many buffers\"\n - buffers_backend:\n usage: \"COUNTER\"\n description: \"Number of buffers written directly by a backend\"\n - buffers_backend_fsync:\n usage: \"COUNTER\"\n description: \"Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)\"\n - buffers_alloc:\n usage: \"COUNTER\"\n description: \"Number of buffers allocated\"\n\npg_stat_database:\n query: |\n SELECT datname\n , xact_commit\n , xact_rollback\n , blks_read\n , blks_hit\n , tup_returned\n , tup_fetched\n , tup_inserted\n , tup_updated\n , tup_deleted\n , conflicts\n , temp_files\n , temp_bytes\n , deadlocks\n , blk_read_time\n , blk_write_time\n FROM pg_catalog.pg_stat_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of this database\"\n - xact_commit:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been committed\"\n - xact_rollback:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been rolled back\"\n - blks_read:\n usage: \"COUNTER\"\n description: \"Number of disk blocks read in this database\"\n - blks_hit:\n usage: \"COUNTER\"\n description: \"Number of times disk blocks were found already in the buffer cache, so that a read was not necessary (this only includes hits in the PostgreSQL buffer cache, not the operating system's file system cache)\"\n - tup_returned:\n usage: \"COUNTER\"\n description: \"Number of rows returned by queries in this database\"\n - tup_fetched:\n usage: \"COUNTER\"\n description: \"Number of rows fetched by queries in this database\"\n - tup_inserted:\n usage: \"COUNTER\"\n description: \"Number of rows inserted by queries in this database\"\n - tup_updated:\n usage: \"COUNTER\"\n description: \"Number of rows updated by queries in this database\"\n - tup_deleted:\n usage: \"COUNTER\"\n description: \"Number of rows deleted by queries in this database\"\n - conflicts:\n usage: \"COUNTER\"\n description: \"Number of queries canceled due to conflicts with recovery in this database\"\n - temp_files:\n usage: \"COUNTER\"\n description: \"Number of temporary files created by queries in this database\"\n - temp_bytes:\n usage: \"COUNTER\"\n description: \"Total amount of data written to temporary files by queries in this database\"\n - deadlocks:\n usage: \"COUNTER\"\n description: \"Number of deadlocks detected in this database\"\n - blk_read_time:\n usage: \"COUNTER\"\n description: \"Time spent reading data file blocks by backends in this database, in milliseconds\"\n - blk_write_time:\n usage: \"COUNTER\"\n description: \"Time spent writing data file blocks by backends in this database, in milliseconds\"\n\npg_stat_replication:\n primary: true\n query: |\n SELECT usename\n , COALESCE(application_name, '') AS application_name\n , COALESCE(client_addr::text, '') AS client_addr\n , COALESCE(client_port::text, '') AS client_port\n , EXTRACT(EPOCH FROM backend_start) AS backend_start\n , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), write_lsn) AS write_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), flush_lsn) AS flush_diff_bytes\n , COALESCE(pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), replay_lsn),0) AS replay_diff_bytes\n , COALESCE((EXTRACT(EPOCH FROM write_lag)),0)::float AS write_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM flush_lag)),0)::float AS flush_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM replay_lag)),0)::float AS replay_lag_seconds\n FROM pg_catalog.pg_stat_replication\n metrics:\n - usename:\n usage: \"LABEL\"\n description: \"Name of the replication user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - client_addr:\n usage: \"LABEL\"\n description: \"Client IP address\"\n - client_port:\n usage: \"LABEL\"\n description: \"Client TCP port\"\n - backend_start:\n usage: \"COUNTER\"\n description: \"Time when this process was started\"\n - backend_xmin_age:\n usage: \"COUNTER\"\n description: \"The age of this standby's xmin horizon\"\n - sent_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location sent on this connection\"\n - write_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location written to disk by this standby server\"\n - flush_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location flushed to disk by this standby server\"\n - replay_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location replayed into the database on this standby server\"\n - write_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written it\"\n - flush_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it\"\n - replay_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written, flushed and applied it\"\n\npg_settings:\n query: |\n SELECT name,\n CASE setting WHEN 'on' THEN '1' WHEN 'off' THEN '0' ELSE setting END AS setting\n FROM pg_catalog.pg_settings\n WHERE vartype IN ('integer', 'real', 'bool')\n ORDER BY 1\n metrics:\n - name:\n usage: \"LABEL\"\n description: \"Name of the setting\"\n - setting:\n usage: \"GAUGE\"\n description: \"Setting value\"\n"` | A string representation of a YAML defining monitoring queries. | diff --git a/charts/cloudnative-pg/templates/crds/crds.yaml b/charts/cloudnative-pg/templates/crds/crds.yaml index f040a3805..9607beb4e 100644 --- a/charts/cloudnative-pg/templates/crds/crds.yaml +++ b/charts/cloudnative-pg/templates/crds/crds.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 helm.sh/resource-policy: keep name: backups.postgresql.cnpg.io spec: @@ -37,20 +37,26 @@ spec: description: Backup is the Schema for the backups API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: - description: 'Specification of the desired behavior of the backup. More - info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + description: |- + Specification of the desired behavior of the backup. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status properties: cluster: description: The cluster to backup @@ -63,50 +69,54 @@ spec: type: object method: default: barmanObjectStore - description: 'The backup method to be used, possible options are `barmanObjectStore` - and `volumeSnapshot`. Defaults to: `barmanObjectStore`.' + description: |- + The backup method to be used, possible options are `barmanObjectStore` + and `volumeSnapshot`. Defaults to: `barmanObjectStore`. enum: - barmanObjectStore - volumeSnapshot type: string online: - description: Whether the default type of backup with volume snapshots - is online/hot (`true`, default) or offline/cold (`false`) Overrides - the default setting specified in the cluster field '.spec.backup.volumeSnapshot.online' + description: |- + Whether the default type of backup with volume snapshots is + online/hot (`true`, default) or offline/cold (`false`) + Overrides the default setting specified in the cluster field '.spec.backup.volumeSnapshot.online' type: boolean onlineConfiguration: - description: Configuration parameters to control the online/hot backup - with volume snapshots Overrides the default settings specified in - the cluster '.backup.volumeSnapshot.onlineConfiguration' stanza + description: |- + Configuration parameters to control the online/hot backup with volume snapshots + Overrides the default settings specified in the cluster '.backup.volumeSnapshot.onlineConfiguration' stanza properties: immediateCheckpoint: - description: Control whether the I/O workload for the backup initial - checkpoint will be limited, according to the `checkpoint_completion_target` - setting on the PostgreSQL server. If set to true, an immediate - checkpoint will be used, meaning PostgreSQL will complete the - checkpoint as soon as possible. `false` by default. + description: |- + Control whether the I/O workload for the backup initial checkpoint will + be limited, according to the `checkpoint_completion_target` setting on + the PostgreSQL server. If set to true, an immediate checkpoint will be + used, meaning PostgreSQL will complete the checkpoint as soon as + possible. `false` by default. type: boolean waitForArchive: default: true - description: If false, the function will return immediately after - the backup is completed, without waiting for WAL to be archived. - This behavior is only useful with backup software that independently - monitors WAL archiving. Otherwise, WAL required to make the - backup consistent might be missing and make the backup useless. - By default, or when this parameter is true, pg_backup_stop will - wait for WAL to be archived when archiving is enabled. On a - standby, this means that it will wait only when archive_mode - = always. If write activity on the primary is low, it may be - useful to run pg_switch_wal on the primary in order to trigger + description: |- + If false, the function will return immediately after the backup is completed, + without waiting for WAL to be archived. + This behavior is only useful with backup software that independently monitors WAL archiving. + Otherwise, WAL required to make the backup consistent might be missing and make the backup useless. + By default, or when this parameter is true, pg_backup_stop will wait for WAL to be archived when archiving is + enabled. + On a standby, this means that it will wait only when archive_mode = always. + If write activity on the primary is low, it may be useful to run pg_switch_wal on the primary in order to trigger an immediate segment switch. type: boolean type: object target: - description: The policy to decide which instance should perform this - backup. If empty, it defaults to `cluster.spec.backup.target`. Available - options are empty string, `primary` and `prefer-standby`. `primary` - to have backups run always on primary instances, `prefer-standby` - to have backups run preferably on the most updated standby, if available. + description: |- + The policy to decide which instance should perform this backup. If empty, + it defaults to `cluster.spec.backup.target`. + Available options are empty string, `primary` and `prefer-standby`. + `primary` to have backups run always on primary instances, + `prefer-standby` to have backups run preferably on the most updated + standby, if available. enum: - primary - prefer-standby @@ -115,8 +125,10 @@ spec: - cluster type: object status: - description: 'Most recently observed status of the backup. This data may - not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + description: |- + Most recently observed status of the backup. This data may not be up to + date. Populated by the system. Read-only. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status properties: azureCredentials: description: The credentials to use to upload data to Azure Blob Storage @@ -152,7 +164,8 @@ spec: - name type: object storageKey: - description: The storage account key to be used in conjunction + description: |- + The storage account key to be used in conjunction with the storage account name properties: key: @@ -166,8 +179,9 @@ spec: - name type: object storageSasToken: - description: A shared-access-signature to be used in conjunction - with the storage account name + description: |- + A shared-access-signature to be used in conjunction with + the storage account name properties: key: description: The key to select @@ -204,9 +218,10 @@ spec: description: Unused. Retained for compatibility with old versions. type: string destinationPath: - description: The path where to store the backup (i.e. s3://bucket/path/to/folder) - this path, with different destination folders, will be used for - WALs and for data. This may not be populated in case of errors. + description: |- + The path where to store the backup (i.e. s3://bucket/path/to/folder) + this path, with different destination folders, will be used for WALs + and for data. This may not be populated in case of errors. type: string encryption: description: Encryption method required to S3 API @@ -218,9 +233,10 @@ spec: description: The ending WAL type: string endpointCA: - description: EndpointCA store the CA bundle of the barman endpoint. - Useful when using self-signed certificates to avoid errors with - certificate issuer and barman-cloud-wal-archive. + description: |- + EndpointCA store the CA bundle of the barman endpoint. + Useful when using self-signed certificates to avoid + errors with certificate issuer and barman-cloud-wal-archive. properties: key: description: The key to select @@ -233,8 +249,9 @@ spec: - name type: object endpointURL: - description: Endpoint to be used to upload data to the cloud, overriding - the automatic endpoint discovery + description: |- + Endpoint to be used to upload data to the cloud, + overriding the automatic endpoint discovery type: string error: description: The detected error @@ -258,8 +275,9 @@ spec: - name type: object gkeEnvironment: - description: If set to true, will presume that it's running inside - a GKE environment, default to false. + description: |- + If set to true, will presume that it's running inside a GKE environment, + default to false. type: boolean type: object instanceID: @@ -345,7 +363,8 @@ spec: type: object type: object serverName: - description: The server name on S3, the cluster name is used if this + description: |- + The server name on S3, the cluster name is used if this parameter is omitted type: string snapshotBackupStatus: @@ -362,8 +381,9 @@ spec: description: Name is the snapshot resource name type: string tablespaceName: - description: TablespaceName is the name of the snapshotted - tablespace. Only set when type is PG_TABLESPACE + description: |- + TablespaceName is the name of the snapshotted tablespace. Only set + when type is PG_TABLESPACE type: string type: description: Type is tho role of the snapshot in the cluster, @@ -402,7 +422,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 helm.sh/resource-policy: keep name: clusters.postgresql.cnpg.io spec: @@ -440,20 +460,26 @@ spec: description: Cluster is the Schema for the PostgreSQL API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: - description: 'Specification of the desired behavior of the cluster. More - info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + description: |- + Specification of the desired behavior of the cluster. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status properties: affinity: description: Affinity/Anti-affinity rules for Pods @@ -463,16 +489,15 @@ spec: terms to be passed to all the cluster's pods. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods to - nodes that satisfy the affinity expressions specified by - this field, but it may choose a node that violates one or - more of the expressions. The node that is most preferred - is the one with the greatest sum of weights, i.e. for each - node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements of - this field and adding "weight" to the sum if the node has - pods which matches the corresponding podAffinityTerm; the + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm @@ -483,36 +508,33 @@ spec: with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -525,51 +547,74 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. + Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -582,40 +627,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching the corresponding - podAffinityTerm, in the range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -624,52 +666,51 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by this - field are not met at scheduling time, the pod will not be - scheduled onto the node. If the affinity requirements specified - by this field cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may or may - not try to eventually evict the pod from its node. When - there are multiple elements, the lists of nodes corresponding - to each podAffinityTerm are intersected, i.e. all terms - must be satisfied. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not co-located - (anti-affinity) with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which a pod of the set of - pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -681,47 +722,74 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. + Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied to the - union of the namespaces selected by this field and - the ones listed in the namespaces field. null selector - and null or empty namespaces list means "this pod's - namespace". An empty selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -733,32 +801,28 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list of namespace - names that the term applies to. The term is applied - to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. null or - empty namespaces list and null namespaceSelector means - "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where - co-located is defined as running on a node whose value - of the label with key topologyKey matches that of - any node on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: @@ -767,22 +831,20 @@ spec: type: array type: object additionalPodAntiAffinity: - description: AdditionalPodAntiAffinity allows to specify pod anti-affinity - terms to be added to the ones generated by the operator if EnablePodAntiAffinity - is set to true (default) or to be used exclusively if set to - false. + description: |- + AdditionalPodAntiAffinity allows to specify pod anti-affinity terms to be added to the ones generated + by the operator if EnablePodAntiAffinity is set to true (default) or to be used exclusively if set to false. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods to - nodes that satisfy the anti-affinity expressions specified - by this field, but it may choose a node that violates one - or more of the expressions. The node that is most preferred - is the one with the greatest sum of weights, i.e. for each - node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, - etc.), compute a sum by iterating through the elements of - this field and adding "weight" to the sum if the node has - pods which matches the corresponding podAffinityTerm; the + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm @@ -793,36 +855,33 @@ spec: with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -835,51 +894,74 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. + Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -892,40 +974,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching the corresponding - podAffinityTerm, in the range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -934,52 +1013,51 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the anti-affinity requirements - specified by this field cease to be met at some point during - pod execution (e.g. due to a pod label update), the system - may or may not try to eventually evict the pod from its - node. When there are multiple elements, the lists of nodes - corresponding to each podAffinityTerm are intersected, i.e. - all terms must be satisfied. + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not co-located - (anti-affinity) with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which a pod of the set of - pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -991,47 +1069,74 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. + Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied to the - union of the namespaces selected by this field and - the ones listed in the namespaces field. null selector - and null or empty namespaces list means "this pod's - namespace". An empty selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -1043,32 +1148,28 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list of namespace - names that the term applies to. The term is applied - to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. null or - empty namespaces list and null namespaceSelector means - "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where - co-located is defined as running on a node whose value - of the label with key topologyKey matches that of - any node on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: @@ -1077,31 +1178,30 @@ spec: type: array type: object enablePodAntiAffinity: - description: Activates anti-affinity for the pods. The operator - will define pods anti-affinity unless this field is explicitly - set to false + description: |- + Activates anti-affinity for the pods. The operator will define pods + anti-affinity unless this field is explicitly set to false type: boolean nodeAffinity: - description: 'NodeAffinity describes node affinity scheduling - rules for the pod. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity' + description: |- + NodeAffinity describes node affinity scheduling rules for the pod. + More info: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods to - nodes that satisfy the affinity expressions specified by - this field, but it may choose a node that violates one or - more of the expressions. The node that is most preferred - is the one with the greatest sum of weights, i.e. for each - node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements of - this field and adding "weight" to the sum if the node matches - the corresponding matchExpressions; the node(s) with the - highest sum are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. items: - description: An empty preferred scheduling term matches - all objects with implicit weight 0 (i.e. it's a no-op). - A null preferred scheduling term matches no objects (i.e. - is also a no-op). + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: description: A node selector term, associated with the @@ -1111,30 +1211,26 @@ spec: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -1147,30 +1243,26 @@ spec: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -1192,50 +1284,46 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by this - field are not met at scheduling time, the pod will not be - scheduled onto the node. If the affinity requirements specified - by this field cease to be met at some point during pod execution - (e.g. due to an update), the system may or may not try to - eventually evict the pod from its node. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: description: Required. A list of node selector terms. The terms are ORed. items: - description: A null or empty node selector term matches - no objects. The requirements of them are ANDed. The - TopologySelectorTerm type implements a subset of the - NodeSelectorTerm. + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -1248,30 +1336,26 @@ spec: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -1291,64 +1375,66 @@ spec: nodeSelector: additionalProperties: type: string - description: 'NodeSelector is map of key-value pairs used to define - the nodes on which the pods can run. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + description: |- + NodeSelector is map of key-value pairs used to define the nodes on which + the pods can run. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ type: object podAntiAffinityType: - description: 'PodAntiAffinityType allows the user to decide whether - pod anti-affinity between cluster instance has to be considered - a strong requirement during scheduling or not. Allowed values - are: "preferred" (default if empty) or "required". Setting it - to "required", could lead to instances remaining pending until - new kubernetes nodes are added if all the existing nodes don''t - match the required pod anti-affinity rule. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity' + description: |- + PodAntiAffinityType allows the user to decide whether pod anti-affinity between cluster instance has to be + considered a strong requirement during scheduling or not. Allowed values are: "preferred" (default if empty) or + "required". Setting it to "required", could lead to instances remaining pending until new kubernetes nodes are + added if all the existing nodes don't match the required pod anti-affinity rule. + More info: + https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity type: string tolerations: - description: 'Tolerations is a list of Tolerations that should - be set for all the pods, in order to allow them to run on tainted - nodes. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' + description: |- + Tolerations is a list of Tolerations that should be set for all the pods, in order to allow them to run + on tainted nodes. + More info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . properties: effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, allowed - values are NoSchedule, PreferNoSchedule and NoExecute. + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. type: string key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match - all values and all keys. + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. type: string operator: - description: Operator represents a key's relationship to - the value. Valid operators are Exists and Equal. Defaults - to Equal. Exists is equivalent to wildcard for value, - so that a pod can tolerate all taints of a particular - category. + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. type: string tolerationSeconds: - description: TolerationSeconds represents the period of - time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the taint - forever (do not evict). Zero and negative values will - be treated as 0 (evict immediately) by the system. + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. format: int64 type: integer value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. type: string type: object type: array topologyKey: - description: TopologyKey to use for anti-affinity configuration. - See k8s documentation for more info on that + description: |- + TopologyKey to use for anti-affinity configuration. See k8s documentation + for more info on that type: string type: object backup: @@ -1392,7 +1478,8 @@ spec: - name type: object storageKey: - description: The storage account key to be used in conjunction + description: |- + The storage account key to be used in conjunction with the storage account name properties: key: @@ -1406,8 +1493,9 @@ spec: - name type: object storageSasToken: - description: A shared-access-signature to be used in conjunction - with the storage account name + description: |- + A shared-access-signature to be used in conjunction with + the storage account name properties: key: description: The key to select @@ -1421,55 +1509,81 @@ spec: type: object type: object data: - description: The configuration to be used to backup the data - files When not defined, base backups files will be stored - uncompressed and may be unencrypted in the object store, - according to the bucket default policy. + description: |- + The configuration to be used to backup the data files + When not defined, base backups files will be stored uncompressed and may + be unencrypted in the object store, according to the bucket default + policy. properties: + additionalCommandArgs: + description: |- + AdditionalCommandArgs represents additional arguments that can be appended + to the 'barman-cloud-backup' command-line invocation. These arguments + provide flexibility to customize the backup process further according to + specific requirements or configurations. + + + Example: + In a scenario where specialized backup options are required, such as setting + a specific timeout or defining custom behavior, users can use this field + to specify additional command arguments. + + + Note: + It's essential to ensure that the provided arguments are valid and supported + by the 'barman-cloud-backup' command, to avoid potential errors or unintended + behavior during execution. + items: + type: string + type: array compression: - description: Compress a backup file (a tar file per tablespace) - while streaming it to the object store. Available options - are empty string (no compression, default), `gzip`, - `bzip2` or `snappy`. + description: |- + Compress a backup file (a tar file per tablespace) while streaming it + to the object store. Available options are empty string (no + compression, default), `gzip`, `bzip2` or `snappy`. enum: - gzip - bzip2 - snappy type: string encryption: - description: Whenever to force the encryption of files - (if the bucket is not already configured for that). - Allowed options are empty string (use the bucket policy, - default), `AES256` and `aws:kms` + description: |- + Whenever to force the encryption of files (if the bucket is + not already configured for that). + Allowed options are empty string (use the bucket policy, default), + `AES256` and `aws:kms` enum: - AES256 - aws:kms type: string immediateCheckpoint: - description: Control whether the I/O workload for the - backup initial checkpoint will be limited, according - to the `checkpoint_completion_target` setting on the - PostgreSQL server. If set to true, an immediate checkpoint - will be used, meaning PostgreSQL will complete the checkpoint - as soon as possible. `false` by default. + description: |- + Control whether the I/O workload for the backup initial checkpoint will + be limited, according to the `checkpoint_completion_target` setting on + the PostgreSQL server. If set to true, an immediate checkpoint will be + used, meaning PostgreSQL will complete the checkpoint as soon as + possible. `false` by default. type: boolean jobs: - description: The number of parallel jobs to be used to - upload the backup, defaults to 2 + description: |- + The number of parallel jobs to be used to upload the backup, defaults + to 2 format: int32 minimum: 1 type: integer type: object destinationPath: - description: The path where to store the backup (i.e. s3://bucket/path/to/folder) - this path, with different destination folders, will be used - for WALs and for data + description: |- + The path where to store the backup (i.e. s3://bucket/path/to/folder) + this path, with different destination folders, will be used for WALs + and for data minLength: 1 type: string endpointCA: - description: EndpointCA store the CA bundle of the barman - endpoint. Useful when using self-signed certificates to - avoid errors with certificate issuer and barman-cloud-wal-archive + description: |- + EndpointCA store the CA bundle of the barman endpoint. + Useful when using self-signed certificates to avoid + errors with certificate issuer and barman-cloud-wal-archive properties: key: description: The key to select @@ -1482,7 +1596,8 @@ spec: - name type: object endpointURL: - description: Endpoint to be used to upload data to the cloud, + description: |- + Endpoint to be used to upload data to the cloud, overriding the automatic endpoint discovery type: string googleCredentials: @@ -1504,15 +1619,17 @@ spec: - name type: object gkeEnvironment: - description: If set to true, will presume that it's running - inside a GKE environment, default to false. + description: |- + If set to true, will presume that it's running inside a GKE environment, + default to false. type: boolean type: object historyTags: additionalProperties: type: string - description: HistoryTags is a list of key value pairs that - will be passed to the Barman --history-tags option. + description: |- + HistoryTags is a list of key value pairs that will be passed to the + Barman --history-tags option. type: object s3Credentials: description: The credentials to use to upload data to S3 @@ -1576,47 +1693,50 @@ spec: type: object type: object serverName: - description: The server name on S3, the cluster name is used - if this parameter is omitted + description: |- + The server name on S3, the cluster name is used if this + parameter is omitted type: string tags: additionalProperties: type: string - description: Tags is a list of key value pairs that will be - passed to the Barman --tags option. + description: |- + Tags is a list of key value pairs that will be passed to the + Barman --tags option. type: object wal: - description: The configuration for the backup of the WAL stream. - When not defined, WAL files will be stored uncompressed - and may be unencrypted in the object store, according to - the bucket default policy. + description: |- + The configuration for the backup of the WAL stream. + When not defined, WAL files will be stored uncompressed and may be + unencrypted in the object store, according to the bucket default policy. properties: compression: - description: Compress a WAL file before sending it to - the object store. Available options are empty string - (no compression, default), `gzip`, `bzip2` or `snappy`. + description: |- + Compress a WAL file before sending it to the object store. Available + options are empty string (no compression, default), `gzip`, `bzip2` or `snappy`. enum: - gzip - bzip2 - snappy type: string encryption: - description: Whenever to force the encryption of files - (if the bucket is not already configured for that). - Allowed options are empty string (use the bucket policy, - default), `AES256` and `aws:kms` + description: |- + Whenever to force the encryption of files (if the bucket is + not already configured for that). + Allowed options are empty string (use the bucket policy, default), + `AES256` and `aws:kms` enum: - AES256 - aws:kms type: string maxParallel: - description: Number of WAL files to be either archived - in parallel (when the PostgreSQL instance is archiving - to a backup object store) or restored in parallel (when - a PostgreSQL standby is fetching WAL files from a recovery - object store). If not specified, WAL files will be processed - one at a time. It accepts a positive integer as a value - - with 1 being the minimum accepted value. + description: |- + Number of WAL files to be either archived in parallel (when the + PostgreSQL instance is archiving to a backup object store) or + restored in parallel (when a PostgreSQL standby is fetching WAL + files from a recovery object store). If not specified, WAL files + will be processed one at a time. It accepts a positive integer as a + value - with 1 being the minimum accepted value. minimum: 1 type: integer type: object @@ -1624,20 +1744,21 @@ spec: - destinationPath type: object retentionPolicy: - description: RetentionPolicy is the retention policy to be used - for backups and WALs (i.e. '60d'). The retention policy is expressed - in the form of `XXu` where `XX` is a positive integer and `u` - is in `[dwm]` - days, weeks, months. It's currently only applicable - when using the BarmanObjectStore method. + description: |- + RetentionPolicy is the retention policy to be used for backups + and WALs (i.e. '60d'). The retention policy is expressed in the form + of `XXu` where `XX` is a positive integer and `u` is in `[dwm]` - + days, weeks, months. + It's currently only applicable when using the BarmanObjectStore method. pattern: ^[1-9][0-9]*[dwm]$ type: string target: default: prefer-standby - description: The policy to decide which instance should perform - backups. Available options are empty string, which will default - to `prefer-standby` policy, `primary` to have backups run always - on primary instances, `prefer-standby` to have backups run preferably - on the most updated standby, if available. + description: |- + The policy to decide which instance should perform backups. Available + options are empty string, which will default to `prefer-standby` policy, + `primary` to have backups run always on primary instances, `prefer-standby` + to have backups run preferably on the most updated standby, if available. enum: - primary - prefer-standby @@ -1653,9 +1774,9 @@ spec: to .metadata.annotations snapshot resources. type: object className: - description: ClassName specifies the Snapshot Class to be - used for PG_DATA PersistentVolumeClaim. It is the default - class for the other types if no specific class is present + description: |- + ClassName specifies the Snapshot Class to be used for PG_DATA PersistentVolumeClaim. + It is the default class for the other types if no specific class is present type: string labels: additionalProperties: @@ -1665,9 +1786,9 @@ spec: type: object online: default: true - description: Whether the default type of backup with volume - snapshots is online/hot (`true`, default) or offline/cold - (`false`) + description: |- + Whether the default type of backup with volume snapshots is + online/hot (`true`, default) or offline/cold (`false`) type: boolean onlineConfiguration: default: @@ -1677,27 +1798,25 @@ spec: backup with volume snapshots properties: immediateCheckpoint: - description: Control whether the I/O workload for the - backup initial checkpoint will be limited, according - to the `checkpoint_completion_target` setting on the - PostgreSQL server. If set to true, an immediate checkpoint - will be used, meaning PostgreSQL will complete the checkpoint - as soon as possible. `false` by default. + description: |- + Control whether the I/O workload for the backup initial checkpoint will + be limited, according to the `checkpoint_completion_target` setting on + the PostgreSQL server. If set to true, an immediate checkpoint will be + used, meaning PostgreSQL will complete the checkpoint as soon as + possible. `false` by default. type: boolean waitForArchive: default: true - description: If false, the function will return immediately - after the backup is completed, without waiting for WAL - to be archived. This behavior is only useful with backup - software that independently monitors WAL archiving. - Otherwise, WAL required to make the backup consistent - might be missing and make the backup useless. By default, - or when this parameter is true, pg_backup_stop will - wait for WAL to be archived when archiving is enabled. - On a standby, this means that it will wait only when - archive_mode = always. If write activity on the primary - is low, it may be useful to run pg_switch_wal on the - primary in order to trigger an immediate segment switch. + description: |- + If false, the function will return immediately after the backup is completed, + without waiting for WAL to be archived. + This behavior is only useful with backup software that independently monitors WAL archiving. + Otherwise, WAL required to make the backup consistent might be missing and make the backup useless. + By default, or when this parameter is true, pg_backup_stop will wait for WAL to be archived when archiving is + enabled. + On a standby, this means that it will wait only when archive_mode = always. + If write activity on the primary is low, it may be useful to run pg_switch_wal on the primary in order to trigger + an immediate segment switch. type: boolean type: object snapshotOwnerReference: @@ -1712,9 +1831,9 @@ spec: tablespaceClassName: additionalProperties: type: string - description: TablespaceClassName specifies the Snapshot Class - to be used for the tablespaces. defaults to the PGDATA Snapshot - Class, if set + description: |- + TablespaceClassName specifies the Snapshot Class to be used for the tablespaces. + defaults to the PGDATA Snapshot Class, if set type: object walClassName: description: WalClassName specifies the Snapshot Class to @@ -1729,8 +1848,9 @@ spec: description: Bootstrap the cluster via initdb properties: dataChecksums: - description: 'Whether the `-k` option should be passed to - initdb, enabling checksums on data pages (default: `false`)' + description: |- + Whether the `-k` option should be passed to initdb, + enabling checksums on data pages (default: `false`) type: boolean database: description: 'Name of the database used by the application. @@ -1741,9 +1861,9 @@ spec: for initdb (default:`UTF8`) type: string import: - description: Bootstraps the new cluster by importing data - from an existing PostgreSQL instance using logical backup - (`pg_dump` and `pg_restore`) + description: |- + Bootstraps the new cluster by importing data from an existing PostgreSQL + instance using logical backup (`pg_dump` and `pg_restore`) properties: databases: description: The databases to import @@ -1751,10 +1871,10 @@ spec: type: string type: array postImportApplicationSQL: - description: List of SQL queries to be executed as a superuser - in the application database right after is imported - - to be used with extreme care (by default empty). Only - available in microservice type. + description: |- + List of SQL queries to be executed as a superuser in the application + database right after is imported - to be used with extreme care + (by default empty). Only available in microservice type. items: type: string type: array @@ -1764,9 +1884,9 @@ spec: type: string type: array schemaOnly: - description: 'When set to true, only the `pre-data` and - `post-data` sections of `pg_restore` are invoked, avoiding - data import. Default: `false`.' + description: |- + When set to true, only the `pre-data` and `post-data` sections of + `pg_restore` are invoked, avoiding data import. Default: `false`. type: boolean source: description: The source of the import @@ -1799,40 +1919,42 @@ spec: for initdb (default:`C`) type: string options: - description: 'The list of options that must be passed to initdb - when creating the cluster. Deprecated: This could lead to - inconsistent configurations, please use the explicit provided - parameters instead. If defined, explicit values will be - ignored.' + description: |- + The list of options that must be passed to initdb when creating the cluster. + Deprecated: This could lead to inconsistent configurations, + please use the explicit provided parameters instead. + If defined, explicit values will be ignored. items: type: string type: array owner: - description: Name of the owner of the database in the instance - to be used by applications. Defaults to the value of the - `database` key. + description: |- + Name of the owner of the database in the instance to be used + by applications. Defaults to the value of the `database` key. type: string postInitApplicationSQL: - description: List of SQL queries to be executed as a superuser - in the application database right after is created - to - be used with extreme care (by default empty) + description: |- + List of SQL queries to be executed as a superuser in the application + database right after is created - to be used with extreme care + (by default empty) items: type: string type: array postInitApplicationSQLRefs: - description: PostInitApplicationSQLRefs points references - to ConfigMaps or Secrets which contain SQL files, the general - implementation order to these references is from all Secrets - to all ConfigMaps, and inside Secrets or ConfigMaps, the - implementation order is same as the order of each array + description: |- + PostInitApplicationSQLRefs points references to ConfigMaps or Secrets which + contain SQL files, the general implementation order to these references is + from all Secrets to all ConfigMaps, and inside Secrets or ConfigMaps, + the implementation order is same as the order of each array (by default empty) properties: configMapRefs: description: ConfigMapRefs holds a list of references to ConfigMaps items: - description: ConfigMapKeySelector contains enough information - to let you locate the key of a ConfigMap + description: |- + ConfigMapKeySelector contains enough information to let you locate + the key of a ConfigMap properties: key: description: The key to select @@ -1849,8 +1971,9 @@ spec: description: SecretRefs holds a list of references to Secrets items: - description: SecretKeySelector contains enough information - to let you locate the key of a Secret + description: |- + SecretKeySelector contains enough information to let you locate + the key of a Secret properties: key: description: The key to select @@ -1865,23 +1988,26 @@ spec: type: array type: object postInitSQL: - description: List of SQL queries to be executed as a superuser - immediately after the cluster has been created - to be used - with extreme care (by default empty) + description: |- + List of SQL queries to be executed as a superuser immediately + after the cluster has been created - to be used with extreme care + (by default empty) items: type: string type: array postInitTemplateSQL: - description: List of SQL queries to be executed as a superuser - in the `template1` after the cluster has been created - - to be used with extreme care (by default empty) + description: |- + List of SQL queries to be executed as a superuser in the `template1` + after the cluster has been created - to be used with extreme care + (by default empty) items: type: string type: array secret: - description: Name of the secret containing the initial credentials - for the owner of the user database. If empty a new secret - will be created from scratch + description: |- + Name of the secret containing the initial credentials for the + owner of the user database. If empty a new secret will be + created from scratch properties: name: description: Name of the referent. @@ -1890,30 +2016,32 @@ spec: - name type: object walSegmentSize: - description: 'The value in megabytes (1 to 1024) to be passed - to the `--wal-segsize` option for initdb (default: empty, - resulting in PostgreSQL default: 16MB)' + description: |- + The value in megabytes (1 to 1024) to be passed to the `--wal-segsize` + option for initdb (default: empty, resulting in PostgreSQL default: 16MB) maximum: 1024 minimum: 1 type: integer type: object pg_basebackup: - description: Bootstrap the cluster taking a physical backup of - another compatible PostgreSQL instance + description: |- + Bootstrap the cluster taking a physical backup of another compatible + PostgreSQL instance properties: database: description: 'Name of the database used by the application. Default: `app`.' type: string owner: - description: Name of the owner of the database in the instance - to be used by applications. Defaults to the value of the - `database` key. + description: |- + Name of the owner of the database in the instance to be used + by applications. Defaults to the value of the `database` key. type: string secret: - description: Name of the secret containing the initial credentials - for the owner of the user database. If empty a new secret - will be created from scratch + description: |- + Name of the secret containing the initial credentials for the + owner of the user database. If empty a new secret will be + created from scratch properties: name: description: Name of the referent. @@ -1933,14 +2061,16 @@ spec: description: Bootstrap the cluster from a backup properties: backup: - description: The backup object containing the physical base - backup from which to initiate the recovery procedure. Mutually - exclusive with `source` and `volumeSnapshots`. + description: |- + The backup object containing the physical base backup from which to + initiate the recovery procedure. + Mutually exclusive with `source` and `volumeSnapshots`. properties: endpointCA: - description: EndpointCA store the CA bundle of the barman - endpoint. Useful when using self-signed certificates - to avoid errors with certificate issuer and barman-cloud-wal-archive. + description: |- + EndpointCA store the CA bundle of the barman endpoint. + Useful when using self-signed certificates to avoid + errors with certificate issuer and barman-cloud-wal-archive. properties: key: description: The key to select @@ -1963,30 +2093,30 @@ spec: Default: `app`.' type: string owner: - description: Name of the owner of the database in the instance - to be used by applications. Defaults to the value of the - `database` key. + description: |- + Name of the owner of the database in the instance to be used + by applications. Defaults to the value of the `database` key. type: string recoveryTarget: - description: 'By default, the recovery process applies all - the available WAL files in the archive (full recovery). - However, you can also end the recovery as soon as a consistent - state is reached or recover to a point-in-time (PITR) by - specifying a `RecoveryTarget` object, as expected by PostgreSQL - (i.e., timestamp, transaction Id, LSN, ...). More info: - https://www.postgresql.org/docs/current/runtime-config-wal.html#RUNTIME-CONFIG-WAL-RECOVERY-TARGET' + description: |- + By default, the recovery process applies all the available + WAL files in the archive (full recovery). However, you can also + end the recovery as soon as a consistent state is reached or + recover to a point-in-time (PITR) by specifying a `RecoveryTarget` object, + as expected by PostgreSQL (i.e., timestamp, transaction Id, LSN, ...). + More info: https://www.postgresql.org/docs/current/runtime-config-wal.html#RUNTIME-CONFIG-WAL-RECOVERY-TARGET properties: backupID: - description: The ID of the backup from which to start - the recovery process. If empty (default) the operator - will automatically detect the backup based on targetTime - or targetLSN if specified. Otherwise use the latest - available backup in chronological order. + description: |- + The ID of the backup from which to start the recovery process. + If empty (default) the operator will automatically detect the backup + based on targetTime or targetLSN if specified. Otherwise use the + latest available backup in chronological order. type: string exclusive: - description: Set the target to be exclusive. If omitted, - defaults to false, so that in Postgres, `recovery_target_inclusive` - will be true + description: |- + Set the target to be exclusive. If omitted, defaults to false, so that + in Postgres, `recovery_target_inclusive` will be true type: boolean targetImmediate: description: End recovery as soon as a consistent state @@ -1996,7 +2126,8 @@ spec: description: The target LSN (Log Sequence Number) type: string targetName: - description: The target name (to be previously created + description: |- + The target name (to be previously created with `pg_create_restore_point`) type: string targetTLI: @@ -2012,9 +2143,10 @@ spec: type: string type: object secret: - description: Name of the secret containing the initial credentials - for the owner of the user database. If empty a new secret - will be created from scratch + description: |- + Name of the secret containing the initial credentials for the + owner of the user database. If empty a new secret will be + created from scratch properties: name: description: Name of the referent. @@ -2023,27 +2155,30 @@ spec: - name type: object source: - description: The external cluster whose backup we will restore. - This is also used as the name of the folder under which - the backup is stored, so it must be set to the name of the - source cluster Mutually exclusive with `backup`. + description: |- + The external cluster whose backup we will restore. This is also + used as the name of the folder under which the backup is stored, + so it must be set to the name of the source cluster + Mutually exclusive with `backup`. type: string volumeSnapshots: - description: The static PVC data source(s) from which to initiate - the recovery procedure. Currently supporting `VolumeSnapshot` + description: |- + The static PVC data source(s) from which to initiate the + recovery procedure. Currently supporting `VolumeSnapshot` and `PersistentVolumeClaim` resources that map an existing PVC group, compatible with CloudNativePG, and taken with a cold backup copy on a fenced Postgres instance (limitation - which will be removed in the future when online backup will - be implemented). Mutually exclusive with `backup`. + which will be removed in the future when online backup + will be implemented). + Mutually exclusive with `backup`. properties: storage: description: Configuration of the storage of the instances properties: apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. type: string kind: @@ -2059,14 +2194,14 @@ spec: x-kubernetes-map-type: atomic tablespaceStorage: additionalProperties: - description: TypedLocalObjectReference contains enough - information to let you locate the typed referenced - object inside the same namespace. + description: |- + TypedLocalObjectReference contains enough information to let you locate the + typed referenced object inside the same namespace. properties: apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. type: string kind: @@ -2090,9 +2225,9 @@ spec: WAL (Write-Ahead Log) properties: apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. type: string kind: @@ -2115,20 +2250,23 @@ spec: description: The configuration for the CA and related certificates properties: clientCASecret: - description: 'The secret containing the Client CA certificate. - If not defined, a new secret will be created with a self-signed - CA and will be used to generate all the client certificates.

    Contains:

    - `ca.crt`: CA that should - be used to validate the client certificates, used as `ssl_ca_file` - of all the instances.
    - `ca.key`: key used to generate - client certificates, if ReplicationTLSSecret is provided, this - can be omitted.
    ' + description: |- + The secret containing the Client CA certificate. If not defined, a new secret will be created + with a self-signed CA and will be used to generate all the client certificates.
    +
    + Contains:
    +
    + - `ca.crt`: CA that should be used to validate the client certificates, + used as `ssl_ca_file` of all the instances.
    + - `ca.key`: key used to generate client certificates, if ReplicationTLSSecret is provided, + this can be omitted.
    type: string replicationTLSSecret: - description: The secret of type kubernetes.io/tls containing the - client certificate to authenticate as the `streaming_replica` - user. If not defined, ClientCASecret must provide also `ca.key`, - and a new secret will be created using the provided CA. + description: |- + The secret of type kubernetes.io/tls containing the client certificate to authenticate as + the `streaming_replica` user. + If not defined, ClientCASecret must provide also `ca.key`, and a new secret will be + created using the provided CA. type: string serverAltDNSNames: description: The list of the server alternative DNS names to be @@ -2137,21 +2275,23 @@ spec: type: string type: array serverCASecret: - description: 'The secret containing the Server CA certificate. - If not defined, a new secret will be created with a self-signed - CA and will be used to generate the TLS certificate ServerTLSSecret.

    Contains:

    - `ca.crt`: CA that should - be used to validate the server certificate, used as `sslrootcert` - in client connection strings.
    - `ca.key`: key used to - generate Server SSL certs, if ServerTLSSecret is provided, this - can be omitted.
    ' + description: |- + The secret containing the Server CA certificate. If not defined, a new secret will be created + with a self-signed CA and will be used to generate the TLS certificate ServerTLSSecret.
    +
    + Contains:
    +
    + - `ca.crt`: CA that should be used to validate the server certificate, + used as `sslrootcert` in client connection strings.
    + - `ca.key`: key used to generate Server SSL certs, if ServerTLSSecret is provided, + this can be omitted.
    type: string serverTLSSecret: - description: The secret of type kubernetes.io/tls containing the - server TLS certificate and key that will be set as `ssl_cert_file` - and `ssl_key_file` so that clients can connect to postgres securely. - If not defined, ServerCASecret must provide also `ca.key` and - a new secret will be created using the provided CA. + description: |- + The secret of type kubernetes.io/tls containing the server TLS certificate and key that will be set as + `ssl_cert_file` and `ssl_key_file` so that clients can connect to postgres securely. + If not defined, ServerCASecret must provide also `ca.key` and a new secret will be + created using the provided CA. type: string type: object description: @@ -2159,16 +2299,17 @@ spec: type: string enableSuperuserAccess: default: false - description: When this option is enabled, the operator will use the - `SuperuserSecret` to update the `postgres` user password (if the - secret is not present, the operator will automatically create one). - When this option is disabled, the operator will ignore the `SuperuserSecret` - content, delete it when automatically created, and then blank the - password of the `postgres` user by setting it to `NULL`. Disabled - by default. + description: |- + When this option is enabled, the operator will use the `SuperuserSecret` + to update the `postgres` user password (if the secret is + not present, the operator will automatically create one). When this + option is disabled, the operator will ignore the `SuperuserSecret` content, delete + it when automatically created, and then blank the password of the `postgres` + user by setting it to `NULL`. Disabled by default. type: boolean env: - description: Env follows the Env format to pass environment variables + description: |- + Env follows the Env format to pass environment variables to the pods created in the cluster items: description: EnvVar represents an environment variable present in @@ -2178,15 +2319,16 @@ spec: description: Name of the environment variable. Must be a C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's value. Cannot @@ -2199,8 +2341,10 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap or its key @@ -2211,10 +2355,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the FieldPath is @@ -2229,10 +2372,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required for volumes, @@ -2261,8 +2403,10 @@ spec: be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or its key must @@ -2278,8 +2422,9 @@ spec: type: object type: array envFrom: - description: EnvFrom follows the EnvFrom format to pass environment - variables sources to the pods to be used by Env + description: |- + EnvFrom follows the EnvFrom format to pass environment variables + sources to the pods to be used by Env items: description: EnvFromSource represents the source of a set of ConfigMaps properties: @@ -2287,8 +2432,10 @@ spec: description: The ConfigMap to select from properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap must be defined @@ -2303,8 +2450,10 @@ spec: description: The Secret to select from properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret must be defined @@ -2318,58 +2467,67 @@ spec: source of ephemeral volumes. properties: volumeClaimTemplate: - description: "Will be used to create a stand-alone PVC to provision - the volume. The pod in which this EphemeralVolumeSource is embedded - will be the owner of the PVC, i.e. the PVC will be deleted together - with the pod. The name of the PVC will be `-` where `` is the name from the `PodSpec.Volumes` - array entry. Pod validation will reject the pod if the concatenated - name is not valid for a PVC (for example, too long). \n An existing - PVC with that name that is not owned by the pod will *not* be - used for the pod to avoid using an unrelated volume by mistake. - Starting the pod is then blocked until the unrelated PVC is - removed. If such a pre-created PVC is meant to be used by the - pod, the PVC has to updated with an owner reference to the pod - once the pod exists. Normally this should not be necessary, - but it may be useful when manually reconstructing a broken cluster. - \n This field is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. \n Required, must not - be nil." + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). + + + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. + + + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. + + + Required, must not be nil. properties: metadata: - description: May contain labels and annotations that will - be copied into the PVC when creating it. No other fields - are allowed and will be rejected during validation. + description: |- + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. type: object spec: - description: The specification for the PersistentVolumeClaim. - The entire content is copied unchanged into the PVC that - gets created from this template. The same fields as in a - PersistentVolumeClaim are also valid here. + description: |- + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim + are also valid here. properties: accessModes: - description: 'accessModes contains the desired access - modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 items: type: string type: array dataSource: - description: 'dataSource field can be used to specify - either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the provisioner - or an external controller can support the specified - data source, it will create a new volume based on the - contents of the specified data source. When the AnyVolumeDataSource - feature gate is enabled, dataSource contents will be - copied to dataSourceRef, and dataSourceRef contents - will be copied to dataSource when dataSourceRef.namespace - is not specified. If the namespace is specified, then - dataSourceRef will not be copied to dataSource.' + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. properties: apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. type: string kind: @@ -2384,39 +2542,35 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object from - which to populate the volume with data, if a non-empty - volume is desired. This may be any object from a non-empty - API group (non core object) or a PersistentVolumeClaim - object. When this field is specified, volume binding - will only succeed if the type of the specified object - matches some installed volume populator or dynamic provisioner. - This field will replace the functionality of the dataSource - field and as such if both fields are non-empty, they - must have the same value. For backwards compatibility, - when namespace isn''t specified in dataSourceRef, both - fields (dataSource and dataSourceRef) will be set to - the same value automatically if one of them is empty - and the other is non-empty. When namespace is specified - in dataSourceRef, dataSource isn''t set to the same - value and must be empty. There are three important differences - between dataSource and dataSourceRef: * While dataSource - only allows two specific types of objects, dataSourceRef - allows any non-core object, as well as PersistentVolumeClaim - objects. * While dataSource ignores disallowed values - (dropping them), dataSourceRef preserves all values, - and generates an error if a disallowed value is specified. - * While dataSource only allows local objects, dataSourceRef - allows objects in any namespaces. (Beta) Using this - field requires the AnyVolumeDataSource feature gate - to be enabled. (Alpha) Using the namespace field of - dataSourceRef requires the CrossNamespaceVolumeDataSource - feature gate to be enabled.' + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. properties: apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. type: string kind: @@ -2426,51 +2580,23 @@ spec: description: Name is the name of resource being referenced type: string namespace: - description: Namespace is the namespace of resource - being referenced Note that when a namespace is specified, - a gateway.networking.k8s.io/ReferenceGrant object - is required in the referent namespace to allow that - namespace's owner to accept the reference. See the - ReferenceGrant documentation for details. (Alpha) - This field requires the CrossNamespaceVolumeDataSource - feature gate to be enabled. + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. type: string required: - kind - name type: object resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify resource - requirements that are lower than previous value but - must still be higher than capacity recorded in the status - field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources properties: - claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used by - this container. \n This is an alpha field and requires - enabling the DynamicResourceAllocation feature gate. - \n This field is immutable. It can only be set for - containers." - items: - description: ResourceClaim references one entry - in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one - entry in pod.spec.resourceClaims of the Pod - where this field is used. It makes that resource - available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -2478,8 +2604,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -2488,12 +2615,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is omitted - for a container, it defaults to Limits if that is - explicitly specified, otherwise to an implementation-defined - value. Requests cannot exceed Limits. More info: - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object selector: @@ -2504,8 +2630,8 @@ spec: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: @@ -2513,17 +2639,16 @@ spec: applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string values. - If the operator is In or NotIn, the values - array must be non-empty. If the operator is - Exists or DoesNotExist, the values array must - be empty. This array is replaced during a - strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -2535,22 +2660,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic storageClassName: - description: 'storageClassName is the name of the StorageClass - required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: - description: volumeMode defines what type of volume is - required by the claim. Value of Filesystem is implied - when not included in claim spec. + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. type: string volumeName: description: volumeName is the binding reference to the @@ -2562,8 +2702,9 @@ spec: type: object type: object ephemeralVolumesSizeLimit: - description: EphemeralVolumesSizeLimit allows the user to set the - limits for the ephemeral volumes + description: |- + EphemeralVolumesSizeLimit allows the user to set the limits for the ephemeral + volumes properties: shm: anyOf: @@ -2584,9 +2725,9 @@ spec: externalClusters: description: The list of external clusters which are used in the configuration items: - description: ExternalCluster represents the connection parameters - to an external cluster which is used in the other sections of - the configuration + description: |- + ExternalCluster represents the connection parameters to an + external cluster which is used in the other sections of the configuration properties: barmanObjectStore: description: The configuration for the barman-cloud tool suite @@ -2626,7 +2767,8 @@ spec: - name type: object storageKey: - description: The storage account key to be used in conjunction + description: |- + The storage account key to be used in conjunction with the storage account name properties: key: @@ -2640,8 +2782,9 @@ spec: - name type: object storageSasToken: - description: A shared-access-signature to be used in - conjunction with the storage account name + description: |- + A shared-access-signature to be used in conjunction with + the storage account name properties: key: description: The key to select @@ -2655,55 +2798,81 @@ spec: type: object type: object data: - description: The configuration to be used to backup the - data files When not defined, base backups files will be - stored uncompressed and may be unencrypted in the object - store, according to the bucket default policy. + description: |- + The configuration to be used to backup the data files + When not defined, base backups files will be stored uncompressed and may + be unencrypted in the object store, according to the bucket default + policy. properties: + additionalCommandArgs: + description: |- + AdditionalCommandArgs represents additional arguments that can be appended + to the 'barman-cloud-backup' command-line invocation. These arguments + provide flexibility to customize the backup process further according to + specific requirements or configurations. + + + Example: + In a scenario where specialized backup options are required, such as setting + a specific timeout or defining custom behavior, users can use this field + to specify additional command arguments. + + + Note: + It's essential to ensure that the provided arguments are valid and supported + by the 'barman-cloud-backup' command, to avoid potential errors or unintended + behavior during execution. + items: + type: string + type: array compression: - description: Compress a backup file (a tar file per - tablespace) while streaming it to the object store. - Available options are empty string (no compression, - default), `gzip`, `bzip2` or `snappy`. + description: |- + Compress a backup file (a tar file per tablespace) while streaming it + to the object store. Available options are empty string (no + compression, default), `gzip`, `bzip2` or `snappy`. enum: - gzip - bzip2 - snappy type: string encryption: - description: Whenever to force the encryption of files - (if the bucket is not already configured for that). - Allowed options are empty string (use the bucket policy, - default), `AES256` and `aws:kms` + description: |- + Whenever to force the encryption of files (if the bucket is + not already configured for that). + Allowed options are empty string (use the bucket policy, default), + `AES256` and `aws:kms` enum: - AES256 - aws:kms type: string immediateCheckpoint: - description: Control whether the I/O workload for the - backup initial checkpoint will be limited, according - to the `checkpoint_completion_target` setting on the - PostgreSQL server. If set to true, an immediate checkpoint - will be used, meaning PostgreSQL will complete the - checkpoint as soon as possible. `false` by default. + description: |- + Control whether the I/O workload for the backup initial checkpoint will + be limited, according to the `checkpoint_completion_target` setting on + the PostgreSQL server. If set to true, an immediate checkpoint will be + used, meaning PostgreSQL will complete the checkpoint as soon as + possible. `false` by default. type: boolean jobs: - description: The number of parallel jobs to be used - to upload the backup, defaults to 2 + description: |- + The number of parallel jobs to be used to upload the backup, defaults + to 2 format: int32 minimum: 1 type: integer type: object destinationPath: - description: The path where to store the backup (i.e. s3://bucket/path/to/folder) - this path, with different destination folders, will be - used for WALs and for data + description: |- + The path where to store the backup (i.e. s3://bucket/path/to/folder) + this path, with different destination folders, will be used for WALs + and for data minLength: 1 type: string endpointCA: - description: EndpointCA store the CA bundle of the barman - endpoint. Useful when using self-signed certificates to - avoid errors with certificate issuer and barman-cloud-wal-archive + description: |- + EndpointCA store the CA bundle of the barman endpoint. + Useful when using self-signed certificates to avoid + errors with certificate issuer and barman-cloud-wal-archive properties: key: description: The key to select @@ -2716,7 +2885,8 @@ spec: - name type: object endpointURL: - description: Endpoint to be used to upload data to the cloud, + description: |- + Endpoint to be used to upload data to the cloud, overriding the automatic endpoint discovery type: string googleCredentials: @@ -2738,15 +2908,17 @@ spec: - name type: object gkeEnvironment: - description: If set to true, will presume that it's - running inside a GKE environment, default to false. + description: |- + If set to true, will presume that it's running inside a GKE environment, + default to false. type: boolean type: object historyTags: additionalProperties: type: string - description: HistoryTags is a list of key value pairs that - will be passed to the Barman --history-tags option. + description: |- + HistoryTags is a list of key value pairs that will be passed to the + Barman --history-tags option. type: object s3Credentials: description: The credentials to use to upload data to S3 @@ -2810,48 +2982,50 @@ spec: type: object type: object serverName: - description: The server name on S3, the cluster name is - used if this parameter is omitted + description: |- + The server name on S3, the cluster name is used if this + parameter is omitted type: string tags: additionalProperties: type: string - description: Tags is a list of key value pairs that will - be passed to the Barman --tags option. + description: |- + Tags is a list of key value pairs that will be passed to the + Barman --tags option. type: object wal: - description: The configuration for the backup of the WAL - stream. When not defined, WAL files will be stored uncompressed - and may be unencrypted in the object store, according - to the bucket default policy. + description: |- + The configuration for the backup of the WAL stream. + When not defined, WAL files will be stored uncompressed and may be + unencrypted in the object store, according to the bucket default policy. properties: compression: - description: Compress a WAL file before sending it to - the object store. Available options are empty string - (no compression, default), `gzip`, `bzip2` or `snappy`. + description: |- + Compress a WAL file before sending it to the object store. Available + options are empty string (no compression, default), `gzip`, `bzip2` or `snappy`. enum: - gzip - bzip2 - snappy type: string encryption: - description: Whenever to force the encryption of files - (if the bucket is not already configured for that). - Allowed options are empty string (use the bucket policy, - default), `AES256` and `aws:kms` + description: |- + Whenever to force the encryption of files (if the bucket is + not already configured for that). + Allowed options are empty string (use the bucket policy, default), + `AES256` and `aws:kms` enum: - AES256 - aws:kms type: string maxParallel: - description: Number of WAL files to be either archived - in parallel (when the PostgreSQL instance is archiving - to a backup object store) or restored in parallel - (when a PostgreSQL standby is fetching WAL files from - a recovery object store). If not specified, WAL files - will be processed one at a time. It accepts a positive - integer as a value - with 1 being the minimum accepted - value. + description: |- + Number of WAL files to be either archived in parallel (when the + PostgreSQL instance is archiving to a backup object store) or + restored in parallel (when a PostgreSQL standby is fetching WAL + files from a recovery object store). If not specified, WAL files + will be processed one at a time. It accepts a positive integer as a + value - with 1 being the minimum accepted value. minimum: 1 type: integer type: object @@ -2868,22 +3042,24 @@ spec: description: The server name, required type: string password: - description: The reference to the password to be used to connect - to the server. If a password is provided, CloudNativePG creates - a PostgreSQL passfile at `/controller/external/NAME/pass` - (where "NAME" is the cluster's name). This passfile is automatically - referenced in the connection string when establishing a connection - to the remote PostgreSQL server from the current PostgreSQL - `Cluster`. This ensures secure and efficient password management - for external clusters. + description: |- + The reference to the password to be used to connect to the server. + If a password is provided, CloudNativePG creates a PostgreSQL + passfile at `/controller/external/NAME/pass` (where "NAME" is the + cluster's name). This passfile is automatically referenced in the + connection string when establishing a connection to the remote + PostgreSQL server from the current PostgreSQL `Cluster`. This ensures + secure and efficient password management for external clusters. properties: key: description: The key of the secret to select from. Must be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or its key must @@ -2894,16 +3070,19 @@ spec: type: object x-kubernetes-map-type: atomic sslCert: - description: The reference to an SSL certificate to be used - to connect to this instance + description: |- + The reference to an SSL certificate to be used to connect to this + instance properties: key: description: The key of the secret to select from. Must be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or its key must @@ -2914,16 +3093,19 @@ spec: type: object x-kubernetes-map-type: atomic sslKey: - description: The reference to an SSL private key to be used - to connect to this instance + description: |- + The reference to an SSL private key to be used to connect to this + instance properties: key: description: The key of the secret to select from. Must be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or its key must @@ -2934,16 +3116,19 @@ spec: type: object x-kubernetes-map-type: atomic sslRootCert: - description: The reference to an SSL CA public key to be used - to connect to this instance + description: |- + The reference to an SSL CA public key to be used to connect to this + instance properties: key: description: The key of the secret to select from. Must be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or its key must @@ -2959,26 +3144,32 @@ spec: type: array failoverDelay: default: 0 - description: The amount of time (in seconds) to wait before triggering - a failover after the primary PostgreSQL instance in the cluster - was detected to be unhealthy + description: |- + The amount of time (in seconds) to wait before triggering a failover + after the primary PostgreSQL instance in the cluster was detected + to be unhealthy format: int32 type: integer imageName: - description: Name of the container image, supporting both tags (`:`) - and digests for deterministic and repeatable deployments (`:@sha256:`) + description: |- + Name of the container image, supporting both tags (`:`) + and digests for deterministic and repeatable deployments + (`:@sha256:`) type: string imagePullPolicy: - description: 'Image pull policy. One of `Always`, `Never` or `IfNotPresent`. - If not defined, it defaults to `IfNotPresent`. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + description: |- + Image pull policy. + One of `Always`, `Never` or `IfNotPresent`. + If not defined, it defaults to `IfNotPresent`. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images type: string imagePullSecrets: description: The list of pull secrets to be used to pull the images items: - description: LocalObjectReference contains enough information to - let you locate a local object with a known type inside the same - namespace + description: |- + LocalObjectReference contains enough information to let you locate a + local object with a known type inside the same namespace properties: name: description: Name of the referent. @@ -3023,36 +3214,41 @@ spec: roles: description: Database roles managed by the `Cluster` items: - description: "RoleConfiguration is the representation, in Kubernetes, - of a PostgreSQL role with the additional field Ensure specifying - whether to ensure the presence or absence of the role in the - database \n The defaults of the CREATE ROLE command are applied - Reference: https://www.postgresql.org/docs/current/sql-createrole.html" + description: |- + RoleConfiguration is the representation, in Kubernetes, of a PostgreSQL role + with the additional field Ensure specifying whether to ensure the presence or + absence of the role in the database + + + The defaults of the CREATE ROLE command are applied + Reference: https://www.postgresql.org/docs/current/sql-createrole.html properties: bypassrls: - description: Whether a role bypasses every row-level security - (RLS) policy. Default is `false`. + description: |- + Whether a role bypasses every row-level security (RLS) policy. + Default is `false`. type: boolean comment: description: Description of the role type: string connectionLimit: default: -1 - description: If the role can log in, this specifies how - many concurrent connections the role can make. `-1` (the - default) means no limit. + description: |- + If the role can log in, this specifies how many concurrent + connections the role can make. `-1` (the default) means no limit. format: int64 type: integer createdb: - description: When set to `true`, the role being defined - will be allowed to create new databases. Specifying `false` - (default) will deny a role the ability to create databases. + description: |- + When set to `true`, the role being defined will be allowed to create + new databases. Specifying `false` (default) will deny a role the + ability to create databases. type: boolean createrole: - description: Whether the role will be permitted to create, - alter, drop, comment on, change the security label for, - and grant or revoke membership in other roles. Default - is `false`. + description: |- + Whether the role will be permitted to create, alter, drop, comment + on, change the security label for, and grant or revoke membership in + other roles. Default is `false`. type: boolean disablePassword: description: DisablePassword indicates that a role's password @@ -3067,31 +3263,32 @@ spec: - absent type: string inRoles: - description: List of one or more existing roles to which - this role will be immediately added as a new member. Default - empty. + description: |- + List of one or more existing roles to which this role will be + immediately added as a new member. Default empty. items: type: string type: array inherit: default: true - description: Whether a role "inherits" the privileges of - roles it is a member of. Defaults is `true`. + description: |- + Whether a role "inherits" the privileges of roles it is a member of. + Defaults is `true`. type: boolean login: - description: Whether the role is allowed to log in. A role - having the `login` attribute can be thought of as a user. - Roles without this attribute are useful for managing database - privileges, but are not users in the usual sense of the - word. Default is `false`. + description: |- + Whether the role is allowed to log in. A role having the `login` + attribute can be thought of as a user. Roles without this attribute + are useful for managing database privileges, but are not users in + the usual sense of the word. Default is `false`. type: boolean name: description: Name of the role type: string passwordSecret: - description: Secret containing the password of the role - (if present) If null, the password will be ignored unless - DisablePassword is set + description: |- + Secret containing the password of the role (if present) + If null, the password will be ignored unless DisablePassword is set properties: name: description: Name of the referent. @@ -3100,26 +3297,26 @@ spec: - name type: object replication: - description: Whether a role is a replication role. A role - must have this attribute (or be a superuser) in order - to be able to connect to the server in replication mode - (physical or logical replication) and in order to be able - to create or drop replication slots. A role having the - `replication` attribute is a very highly privileged role, - and should only be used on roles actually used for replication. - Default is `false`. + description: |- + Whether a role is a replication role. A role must have this + attribute (or be a superuser) in order to be able to connect to the + server in replication mode (physical or logical replication) and in + order to be able to create or drop replication slots. A role having + the `replication` attribute is a very highly privileged role, and + should only be used on roles actually used for replication. Default + is `false`. type: boolean superuser: - description: Whether the role is a `superuser` who can override - all access restrictions within the database - superuser - status is dangerous and should be used only when really - needed. You must yourself be a superuser to create a new - superuser. Defaults is `false`. + description: |- + Whether the role is a `superuser` who can override all access + restrictions within the database - superuser status is dangerous and + should be used only when really needed. You must yourself be a + superuser to create a new superuser. Defaults is `false`. type: boolean validUntil: - description: Date and time after which the role's password - is no longer valid. When omitted, the password will never - expire (default). + description: |- + Date and time after which the role's password is no longer valid. + When omitted, the password will never expire (default). format: date-time type: string required: @@ -3129,16 +3326,18 @@ spec: type: object maxSyncReplicas: default: 0 - description: The target value for the synchronous replication quorum, - that can be decreased if the number of ready standbys is lower than - this. Undefined or 0 disable synchronous replication. + description: |- + The target value for the synchronous replication quorum, that can be + decreased if the number of ready standbys is lower than this. + Undefined or 0 disable synchronous replication. minimum: 0 type: integer minSyncReplicas: default: 0 - description: Minimum number of instances required in synchronous replication - with the primary. Undefined or 0 allow writes to complete when no - standby is available. + description: |- + Minimum number of instances required in synchronous replication with the + primary. Undefined or 0 allow writes to complete when no standby is + available. minimum: 0 type: integer monitoring: @@ -3148,8 +3347,9 @@ spec: customQueriesConfigMap: description: The list of config maps containing the custom queries items: - description: ConfigMapKeySelector contains enough information - to let you locate the key of a ConfigMap + description: |- + ConfigMapKeySelector contains enough information to let you locate + the key of a ConfigMap properties: key: description: The key to select @@ -3165,8 +3365,9 @@ spec: customQueriesSecret: description: The list of secrets containing the custom queries items: - description: SecretKeySelector contains enough information to - let you locate the key of a Secret + description: |- + SecretKeySelector contains enough information to let you locate + the key of a Secret properties: key: description: The key to select @@ -3181,9 +3382,10 @@ spec: type: array disableDefaultQueries: default: false - description: 'Whether the default queries should be injected. - Set it to `true` if you don''t want to inject default queries - into the cluster. Default: false.' + description: |- + Whether the default queries should be injected. + Set it to `true` if you don't want to inject default queries into the cluster. + Default: false. type: boolean enablePodMonitor: default: false @@ -3193,16 +3395,24 @@ spec: description: The list of metric relabelings for the `PodMonitor`. Applied to samples before ingestion. items: - description: "RelabelConfig allows dynamic rewriting of the - label set for targets, alerts, scraped samples and remote - write samples. \n More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config" + description: |- + RelabelConfig allows dynamic rewriting of the label set for targets, alerts, + scraped samples and remote write samples. + + + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config properties: action: default: replace - description: "Action to perform based on the regex matching. - \n `Uppercase` and `Lowercase` actions require Prometheus - >= v2.36.0. `DropEqual` and `KeepEqual` actions require - Prometheus >= v2.41.0. \n Default: \"Replace\"" + description: |- + Action to perform based on the regex matching. + + + `Uppercase` and `Lowercase` actions require Prometheus >= v2.36.0. + `DropEqual` and `KeepEqual` actions require Prometheus >= v2.41.0. + + + Default: "Replace" enum: - replace - Replace @@ -3228,8 +3438,11 @@ spec: - DropEqual type: string modulus: - description: "Modulus to take of the hash of the source - label values. \n Only applicable when the action is `HashMod`." + description: |- + Modulus to take of the hash of the source label values. + + + Only applicable when the action is `HashMod`. format: int64 type: integer regex: @@ -3237,30 +3450,39 @@ spec: value is matched. type: string replacement: - description: "Replacement value against which a Replace - action is performed if the regular expression matches. - \n Regex capture groups are available." + description: |- + Replacement value against which a Replace action is performed if the + regular expression matches. + + + Regex capture groups are available. type: string separator: description: Separator is the string between concatenated SourceLabels. type: string sourceLabels: - description: The source labels select values from existing - labels. Their content is concatenated using the configured - Separator and matched against the configured regular expression. + description: |- + The source labels select values from existing labels. Their content is + concatenated using the configured Separator and matched against the + configured regular expression. items: - description: LabelName is a valid Prometheus label name - which may only contain ASCII letters, numbers, as well - as underscores. + description: |- + LabelName is a valid Prometheus label name which may only contain ASCII + letters, numbers, as well as underscores. pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ type: string type: array targetLabel: - description: "Label to which the resulting string is written - in a replacement. \n It is mandatory for `Replace`, `HashMod`, - `Lowercase`, `Uppercase`, `KeepEqual` and `DropEqual` - actions. \n Regex capture groups are available." + description: |- + Label to which the resulting string is written in a replacement. + + + It is mandatory for `Replace`, `HashMod`, `Lowercase`, `Uppercase`, + `KeepEqual` and `DropEqual` actions. + + + Regex capture groups are available. type: string type: object type: array @@ -3268,16 +3490,24 @@ spec: description: The list of relabelings for the `PodMonitor`. Applied to samples before scraping. items: - description: "RelabelConfig allows dynamic rewriting of the - label set for targets, alerts, scraped samples and remote - write samples. \n More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config" + description: |- + RelabelConfig allows dynamic rewriting of the label set for targets, alerts, + scraped samples and remote write samples. + + + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config properties: action: default: replace - description: "Action to perform based on the regex matching. - \n `Uppercase` and `Lowercase` actions require Prometheus - >= v2.36.0. `DropEqual` and `KeepEqual` actions require - Prometheus >= v2.41.0. \n Default: \"Replace\"" + description: |- + Action to perform based on the regex matching. + + + `Uppercase` and `Lowercase` actions require Prometheus >= v2.36.0. + `DropEqual` and `KeepEqual` actions require Prometheus >= v2.41.0. + + + Default: "Replace" enum: - replace - Replace @@ -3303,8 +3533,11 @@ spec: - DropEqual type: string modulus: - description: "Modulus to take of the hash of the source - label values. \n Only applicable when the action is `HashMod`." + description: |- + Modulus to take of the hash of the source label values. + + + Only applicable when the action is `HashMod`. format: int64 type: integer regex: @@ -3312,30 +3545,39 @@ spec: value is matched. type: string replacement: - description: "Replacement value against which a Replace - action is performed if the regular expression matches. - \n Regex capture groups are available." + description: |- + Replacement value against which a Replace action is performed if the + regular expression matches. + + + Regex capture groups are available. type: string separator: description: Separator is the string between concatenated SourceLabels. type: string sourceLabels: - description: The source labels select values from existing - labels. Their content is concatenated using the configured - Separator and matched against the configured regular expression. + description: |- + The source labels select values from existing labels. Their content is + concatenated using the configured Separator and matched against the + configured regular expression. items: - description: LabelName is a valid Prometheus label name - which may only contain ASCII letters, numbers, as well - as underscores. + description: |- + LabelName is a valid Prometheus label name which may only contain ASCII + letters, numbers, as well as underscores. pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ type: string type: array targetLabel: - description: "Label to which the resulting string is written - in a replacement. \n It is mandatory for `Replace`, `HashMod`, - `Lowercase`, `Uppercase`, `KeepEqual` and `DropEqual` - actions. \n Regex capture groups are available." + description: |- + Label to which the resulting string is written in a replacement. + + + It is mandatory for `Replace`, `HashMod`, `Lowercase`, `Uppercase`, + `KeepEqual` and `DropEqual` actions. + + + Regex capture groups are available. type: string type: object type: array @@ -3349,7 +3591,8 @@ spec: type: boolean reusePVC: default: true - description: Reuse the existing PVC (wait for the node to come + description: |- + Reuse the existing PVC (wait for the node to come up again) or not (recreate it elsewhere - when `instances` >1) type: boolean type: object @@ -3369,10 +3612,11 @@ spec: description: Configuration of the PostgreSQL server properties: enableAlterSystem: - description: If this parameter is true, the user will be able - to invoke `ALTER SYSTEM` on this CloudNativePG Cluster. This - should only be used for debugging and troubleshooting. Defaults - to false. + description: |- + If this parameter is true, the user will be able to invoke `ALTER SYSTEM` + on this CloudNativePG Cluster. + This should only be used for debugging and troubleshooting. + Defaults to false. type: boolean ldap: description: Options to specify LDAP configuration @@ -3405,9 +3649,10 @@ spec: be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or its key @@ -3449,22 +3694,24 @@ spec: description: PostgreSQL configuration options (postgresql.conf) type: object pg_hba: - description: PostgreSQL Host Based Authentication rules (lines - to be appended to the pg_hba.conf file) + description: |- + PostgreSQL Host Based Authentication rules (lines to be appended + to the pg_hba.conf file) items: type: string type: array pg_ident: - description: PostgreSQL User Name Maps rules (lines to be appended + description: |- + PostgreSQL User Name Maps rules (lines to be appended to the pg_ident.conf file) items: type: string type: array promotionTimeout: - description: Specifies the maximum number of seconds to wait when - promoting an instance to primary. Default value is 40000000, - greater than one year in seconds, big enough to simulate an - infinite timeout + description: |- + Specifies the maximum number of seconds to wait when promoting an instance to primary. + Default value is 40000000, greater than one year in seconds, + big enough to simulate an infinite timeout format: int32 type: integer shared_preload_libraries: @@ -3474,8 +3721,8 @@ spec: type: string type: array syncReplicaElectionConstraint: - description: Requirements to be met by sync replicas. This will - affect how the "synchronous_standby_names" parameter will be + description: |- + Requirements to be met by sync replicas. This will affect how the "synchronous_standby_names" parameter will be set up. properties: enabled: @@ -3493,43 +3740,44 @@ spec: type: object primaryUpdateMethod: default: restart - description: 'Method to follow to upgrade the primary server during - a rolling update procedure, after all replicas have been successfully - updated: it can be with a switchover (`switchover`) or in-place - (`restart` - default)' + description: |- + Method to follow to upgrade the primary server during a rolling + update procedure, after all replicas have been successfully updated: + it can be with a switchover (`switchover`) or in-place (`restart` - default) enum: - switchover - restart type: string primaryUpdateStrategy: default: unsupervised - description: 'Deployment strategy to follow to upgrade the primary - server during a rolling update procedure, after all replicas have - been successfully updated: it can be automated (`unsupervised` - - default) or manual (`supervised`)' + description: |- + Deployment strategy to follow to upgrade the primary server during a rolling + update procedure, after all replicas have been successfully updated: + it can be automated (`unsupervised` - default) or manual (`supervised`) enum: - unsupervised - supervised type: string priorityClassName: - description: Name of the priority class which will be used in every - generated Pod, if the PriorityClass specified does not exist, the - pod will not be able to schedule. Please refer to https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#priorityclass + description: |- + Name of the priority class which will be used in every generated Pod, if the PriorityClass + specified does not exist, the pod will not be able to schedule. Please refer to + https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#priorityclass for more information type: string projectedVolumeTemplate: - description: Template to be used to define projected volumes, projected - volumes will be mounted under `/projected` base folder + description: |- + Template to be used to define projected volumes, projected volumes will be mounted + under `/projected` base folder properties: defaultMode: - description: defaultMode are the mode bits used to set permissions - on created files by default. Must be an octal value between - 0000 and 0777 or a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal values - for mode bits. Directories within the path are not affected - by this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result can - be other mode bits set. + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer sources: @@ -3538,21 +3786,112 @@ spec: description: Projection that may be projected along with other supported volume types properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume root to write + the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object configMap: description: configMap information about the configMap data to project properties: items: - description: items if unspecified, each key-value pair - in the Data field of the referenced ConfigMap will - be projected into the volume as a file whose name - is the key and content is the value. If specified, - the listed keys will be projected into the specified - paths, and unlisted keys will not be present. If a - key is specified which is not present in the ConfigMap, - the volume setup will error unless it is marked optional. - Paths must be relative and may not contain the '..' - path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -3561,22 +3900,20 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode bits used - to set permissions on this file. Must be an - octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal - and decimal values, JSON requires decimal values - for mode bits. If not specified, the volume - defaultMode will be used. This might be in conflict - with other options that affect the file mode, - like fsGroup, and the result can be other mode - bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path of the - file to map the key to. May not be an absolute - path. May not contain the path element '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. May not start with the string '..'. type: string required: @@ -3585,8 +3922,10 @@ spec: type: object type: array name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: optional specify whether the ConfigMap @@ -3622,16 +3961,13 @@ spec: type: object x-kubernetes-map-type: atomic mode: - description: 'Optional: mode bits used to set - permissions on this file, must be an octal value - between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal - values, JSON requires decimal values for mode - bits. If not specified, the volume defaultMode - will be used. This might be in conflict with - other options that affect the file mode, like - fsGroup, and the result can be other mode bits - set.' + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: @@ -3642,10 +3978,9 @@ spec: path must not start with ''..''' type: string resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, requests.cpu and requests.memory) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. properties: containerName: description: 'Container name: required for @@ -3676,16 +4011,14 @@ spec: project properties: items: - description: items if unspecified, each key-value pair - in the Data field of the referenced Secret will be - projected into the volume as a file whose name is - the key and content is the value. If specified, the - listed keys will be projected into the specified paths, - and unlisted keys will not be present. If a key is - specified which is not present in the Secret, the - volume setup will error unless it is marked optional. - Paths must be relative and may not contain the '..' - path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -3694,22 +4027,20 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode bits used - to set permissions on this file. Must be an - octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both octal - and decimal values, JSON requires decimal values - for mode bits. If not specified, the volume - defaultMode will be used. This might be in conflict - with other options that affect the file mode, - like fsGroup, and the result can be other mode - bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path of the - file to map the key to. May not be an absolute - path. May not contain the path element '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. May not start with the string '..'. type: string required: @@ -3718,8 +4049,10 @@ spec: type: object type: array name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: optional field specify whether the Secret @@ -3732,26 +4065,26 @@ spec: serviceAccountToken data to project properties: audience: - description: audience is the intended audience of the - token. A recipient of a token must identify itself - with an identifier specified in the audience of the - token, and otherwise should reject the token. The - audience defaults to the identifier of the apiserver. + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. type: string expirationSeconds: - description: expirationSeconds is the requested duration - of validity of the service account token. As the token - approaches expiration, the kubelet volume plugin will - proactively rotate the service account token. The - kubelet will start trying to rotate the token if the - token is older than 80 percent of its time to live - or if the token is older than 24 hours.Defaults to - 1 hour and must be at least 10 minutes. + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. format: int64 type: integer path: - description: path is the path relative to the mount - point of the file to project the token into. + description: |- + path is the path relative to the mount point of the file to project the + token into. type: string required: - path @@ -3763,11 +4096,11 @@ spec: description: Replica cluster configuration properties: enabled: - description: If replica mode is enabled, this cluster will be - a replica of an existing cluster. Replica cluster can be created - from a recovery object store or via streaming through pg_basebackup. - Refer to the Replica clusters page of the documentation for - more information. + description: |- + If replica mode is enabled, this cluster will be a replica of an + existing cluster. Replica cluster can be created from a recovery + object store or via streaming through pg_basebackup. + Refer to the Replica clusters page of the documentation for more information. type: boolean source: description: The name of the external cluster which is the replication @@ -3791,49 +4124,57 @@ spec: properties: enabled: default: true - description: If enabled (default), the operator will automatically - manage replication slots on the primary instance and use - them in streaming replication connections with all the standby - instances that are part of the HA cluster. If disabled, - the operator will not take advantage of replication slots - in streaming connections with the replicas. This feature - also controls replication slots in replica cluster, from - the designated primary to its cascading replicas. + description: |- + If enabled (default), the operator will automatically manage replication slots + on the primary instance and use them in streaming replication + connections with all the standby instances that are part of the HA + cluster. If disabled, the operator will not take advantage + of replication slots in streaming connections with the replicas. + This feature also controls replication slots in replica cluster, + from the designated primary to its cascading replicas. type: boolean slotPrefix: default: _cnpg_ - description: Prefix for replication slots managed by the operator - for HA. It may only contain lower case letters, numbers, - and the underscore character. This can only be set at creation - time. By default set to `_cnpg_`. + description: |- + Prefix for replication slots managed by the operator for HA. + It may only contain lower case letters, numbers, and the underscore character. + This can only be set at creation time. By default set to `_cnpg_`. pattern: ^[0-9a-z_]*$ type: string type: object updateInterval: default: 30 - description: Standby will update the status of the local replication - slots every `updateInterval` seconds (default 30). + description: |- + Standby will update the status of the local replication slots + every `updateInterval` seconds (default 30). minimum: 1 type: integer type: object resources: - description: Resources requirements of every generated Pod. Please - refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + description: |- + Resources requirements of every generated Pod. Please refer to + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ for more information. properties: claims: - description: "Claims lists the names of resources, defined in - spec.resourceClaims, that are used by this container. \n This - is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be set - for containers." + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one entry in pod.spec.resourceClaims - of the Pod where this field is used. It makes that resource - available inside a container. + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. type: string required: - name @@ -3849,8 +4190,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources - allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -3859,35 +4201,41 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object schedulerName: - description: 'If specified, the pod will be dispatched by specified - Kubernetes scheduler. If not specified, the pod will be dispatched - by the default scheduler. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/' + description: |- + If specified, the pod will be dispatched by specified Kubernetes + scheduler. If not specified, the pod will be dispatched by the default + scheduler. More info: + https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/ type: string seccompProfile: - description: 'The SeccompProfile applied to every Pod and Container. - Defaults to: `RuntimeDefault`' + description: |- + The SeccompProfile applied to every Pod and Container. + Defaults to: `RuntimeDefault` properties: localhostProfile: - description: localhostProfile indicates a profile defined in a - file on the node should be used. The profile must be preconfigured - on the node to work. Must be a descending path, relative to - the kubelet's configured seccomp profile location. Must be set - if type is "Localhost". Must NOT be set for any other type. + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. type: string type: - description: "type indicates which kind of seccomp profile will - be applied. Valid options are: \n Localhost - a profile defined - in a file on the node should be used. RuntimeDefault - the container - runtime default profile should be used. Unconfined - no profile - should be applied." + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. type: string required: - type @@ -3896,25 +4244,27 @@ spec: description: Configure the generation of the service account properties: metadata: - description: Metadata are the metadata to be used for the generated + description: |- + Metadata are the metadata to be used for the generated service account properties: annotations: additionalProperties: type: string - description: 'Annotations is an unstructured key value map - stored with a resource that may be set by external tools - to store and retrieve arbitrary metadata. They are not queryable - and should be preserved when modifying objects. More info: - http://kubernetes.io/docs/user-guide/annotations' + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations type: object labels: additionalProperties: type: string - description: 'Map of string keys and values that can be used - to organize and categorize (scope and select) objects. May - match selectors of replication controllers and services. - More info: http://kubernetes.io/docs/user-guide/labels' + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: http://kubernetes.io/docs/user-guide/labels type: object type: object required: @@ -3922,24 +4272,26 @@ spec: type: object smartShutdownTimeout: default: 180 - description: 'The time in seconds that controls the window of time - reserved for the smart shutdown of Postgres to complete. Make sure - you reserve enough time for the operator to request a fast shutdown - of Postgres (that is: `stopDelay` - `smartShutdownTimeout`).' + description: |- + The time in seconds that controls the window of time reserved for the smart shutdown of Postgres to complete. + Make sure you reserve enough time for the operator to request a fast shutdown of Postgres + (that is: `stopDelay` - `smartShutdownTimeout`). format: int32 type: integer startDelay: default: 3600 - description: 'The time in seconds that is allowed for a PostgreSQL - instance to successfully start up (default 3600). The startup probe - failure threshold is derived from this value using the formula: - ceiling(startDelay / 10).' + description: |- + The time in seconds that is allowed for a PostgreSQL instance to + successfully start up (default 3600). + The startup probe failure threshold is derived from this value using the formula: + ceiling(startDelay / 10). format: int32 type: integer stopDelay: default: 1800 - description: The time in seconds that is allowed for a PostgreSQL - instance to gracefully shutdown (default 1800) + description: |- + The time in seconds that is allowed for a PostgreSQL instance to + gracefully shutdown (default 1800) format: int32 type: integer storage: @@ -3950,29 +4302,28 @@ spec: Claim properties: accessModes: - description: 'accessModes contains the desired access modes - the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 items: type: string type: array dataSource: - description: 'dataSource field can be used to specify either: + description: |- + dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the provisioner - or an external controller can support the specified data - source, it will create a new volume based on the contents - of the specified data source. When the AnyVolumeDataSource - feature gate is enabled, dataSource contents will be copied - to dataSourceRef, and dataSourceRef contents will be copied - to dataSource when dataSourceRef.namespace is not specified. - If the namespace is specified, then dataSourceRef will not - be copied to dataSource.' + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. properties: apiGroup: - description: APIGroup is the group for the resource being - referenced. If APIGroup is not specified, the specified - Kind must be in the core API group. For any other third-party - types, APIGroup is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource being referenced @@ -3986,38 +4337,36 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object from which - to populate the volume with data, if a non-empty volume - is desired. This may be any object from a non-empty API - group (non core object) or a PersistentVolumeClaim object. - When this field is specified, volume binding will only succeed - if the type of the specified object matches some installed - volume populator or dynamic provisioner. This field will - replace the functionality of the dataSource field and as - such if both fields are non-empty, they must have the same - value. For backwards compatibility, when namespace isn''t - specified in dataSourceRef, both fields (dataSource and - dataSourceRef) will be set to the same value automatically - if one of them is empty and the other is non-empty. When - namespace is specified in dataSourceRef, dataSource isn''t - set to the same value and must be empty. There are three - important differences between dataSource and dataSourceRef: - * While dataSource only allows two specific types of objects, - dataSourceRef allows any non-core object, as well as PersistentVolumeClaim - objects. * While dataSource ignores disallowed values (dropping - them), dataSourceRef preserves all values, and generates - an error if a disallowed value is specified. * While dataSource - only allows local objects, dataSourceRef allows objects - in any namespaces. (Beta) Using this field requires the - AnyVolumeDataSource feature gate to be enabled. (Alpha) - Using the namespace field of dataSourceRef requires the - CrossNamespaceVolumeDataSource feature gate to be enabled.' + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. properties: apiGroup: - description: APIGroup is the group for the resource being - referenced. If APIGroup is not specified, the specified - Kind must be in the core API group. For any other third-party - types, APIGroup is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource being referenced @@ -4026,49 +4375,23 @@ spec: description: Name is the name of resource being referenced type: string namespace: - description: Namespace is the namespace of resource being - referenced Note that when a namespace is specified, - a gateway.networking.k8s.io/ReferenceGrant object is - required in the referent namespace to allow that namespace's - owner to accept the reference. See the ReferenceGrant - documentation for details. (Alpha) This field requires - the CrossNamespaceVolumeDataSource feature gate to be - enabled. + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. type: string required: - kind - name type: object resources: - description: 'resources represents the minimum resources the - volume should have. If RecoverVolumeExpansionFailure feature - is enabled users are allowed to specify resource requirements - that are lower than previous value but must still be higher - than capacity recorded in the status field of the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. \n This field - is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -4076,8 +4399,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -4086,11 +4410,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of - compute resources required. If Requests is omitted for - a container, it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined value. - Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object selector: @@ -4101,8 +4425,8 @@ spec: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: @@ -4110,17 +4434,16 @@ spec: applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, - Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists or - DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -4132,22 +4455,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic storageClassName: - description: 'storageClassName is the name of the StorageClass - required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: - description: volumeMode defines what type of volume is required - by the claim. Value of Filesystem is implied when not included - in claim spec. + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. type: string volumeName: description: volumeName is the binding reference to the PersistentVolume @@ -4159,19 +4497,23 @@ spec: description: Resize existent PVCs, defaults to true type: boolean size: - description: Size of the storage. Required if not already specified - in the PVC template. Changes to this field are automatically - reapplied to the created PVCs. Size cannot be decreased. + description: |- + Size of the storage. Required if not already specified in the PVC template. + Changes to this field are automatically reapplied to the created PVCs. + Size cannot be decreased. type: string storageClass: - description: StorageClass to use for PVCs. Applied after evaluating - the PVC template, if available. If not specified, the generated - PVCs will use the default storage class + description: |- + StorageClass to use for PVCs. Applied after + evaluating the PVC template, if available. + If not specified, the generated PVCs will use the + default storage class type: string type: object superuserSecret: - description: The secret containing the superuser password. If not - defined a new secret will be created with a randomly generated password + description: |- + The secret containing the superuser password. If not defined a new + secret will be created with a randomly generated password properties: name: description: Name of the referent. @@ -4181,16 +4523,18 @@ spec: type: object switchoverDelay: default: 3600 - description: The time in seconds that is allowed for a primary PostgreSQL - instance to gracefully shutdown during a switchover. Default value - is 3600 seconds (1 hour). + description: |- + The time in seconds that is allowed for a primary PostgreSQL instance + to gracefully shutdown during a switchover. + Default value is 3600 seconds (1 hour). format: int32 type: integer tablespaces: description: The tablespaces configuration items: - description: TablespaceConfiguration is the configuration of a tablespace, - and includes the storage specification for the tablespace + description: |- + TablespaceConfiguration is the configuration of a tablespace, and includes + the storage specification for the tablespace properties: name: description: The name of the tablespace @@ -4209,28 +4553,27 @@ spec: Volume Claim properties: accessModes: - description: 'accessModes contains the desired access - modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 items: type: string type: array dataSource: - description: 'dataSource field can be used to specify - either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the provisioner - or an external controller can support the specified - data source, it will create a new volume based on - the contents of the specified data source. When the - AnyVolumeDataSource feature gate is enabled, dataSource - contents will be copied to dataSourceRef, and dataSourceRef - contents will be copied to dataSource when dataSourceRef.namespace - is not specified. If the namespace is specified, then - dataSourceRef will not be copied to dataSource.' + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. properties: apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. type: string kind: @@ -4247,40 +4590,35 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object from - which to populate the volume with data, if a non-empty - volume is desired. This may be any object from a non-empty - API group (non core object) or a PersistentVolumeClaim - object. When this field is specified, volume binding - will only succeed if the type of the specified object - matches some installed volume populator or dynamic - provisioner. This field will replace the functionality - of the dataSource field and as such if both fields - are non-empty, they must have the same value. For - backwards compatibility, when namespace isn''t specified - in dataSourceRef, both fields (dataSource and dataSourceRef) - will be set to the same value automatically if one - of them is empty and the other is non-empty. When - namespace is specified in dataSourceRef, dataSource - isn''t set to the same value and must be empty. There - are three important differences between dataSource - and dataSourceRef: * While dataSource only allows - two specific types of objects, dataSourceRef allows - any non-core object, as well as PersistentVolumeClaim - objects. * While dataSource ignores disallowed values - (dropping them), dataSourceRef preserves all values, - and generates an error if a disallowed value is specified. - * While dataSource only allows local objects, dataSourceRef - allows objects in any namespaces. (Beta) Using this - field requires the AnyVolumeDataSource feature gate - to be enabled. (Alpha) Using the namespace field of - dataSourceRef requires the CrossNamespaceVolumeDataSource - feature gate to be enabled.' + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. properties: apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. type: string kind: @@ -4292,51 +4630,23 @@ spec: referenced type: string namespace: - description: Namespace is the namespace of resource - being referenced Note that when a namespace is - specified, a gateway.networking.k8s.io/ReferenceGrant - object is required in the referent namespace to - allow that namespace's owner to accept the reference. - See the ReferenceGrant documentation for details. - (Alpha) This field requires the CrossNamespaceVolumeDataSource - feature gate to be enabled. + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. type: string required: - kind - name type: object resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify resource - requirements that are lower than previous value but - must still be higher than capacity recorded in the - status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources properties: - claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used - by this container. \n This is an alpha field and - requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can - only be set for containers." - items: - description: ResourceClaim references one entry - in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one - entry in pod.spec.resourceClaims of the - Pod where this field is used. It makes that - resource available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -4344,8 +4654,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -4354,12 +4665,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is - omitted for a container, it defaults to Limits - if that is explicitly specified, otherwise to - an implementation-defined value. Requests cannot - exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object selector: @@ -4370,26 +4680,25 @@ spec: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -4401,23 +4710,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic storageClassName: - description: 'storageClassName is the name of the StorageClass - required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: - description: volumeMode defines what type of volume - is required by the claim. Value of Filesystem is implied - when not included in claim spec. + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. type: string volumeName: description: volumeName is the binding reference to @@ -4429,24 +4752,26 @@ spec: description: Resize existent PVCs, defaults to true type: boolean size: - description: Size of the storage. Required if not already - specified in the PVC template. Changes to this field are - automatically reapplied to the created PVCs. Size cannot - be decreased. + description: |- + Size of the storage. Required if not already specified in the PVC template. + Changes to this field are automatically reapplied to the created PVCs. + Size cannot be decreased. type: string storageClass: - description: StorageClass to use for PVCs. Applied after - evaluating the PVC template, if available. If not specified, - the generated PVCs will use the default storage class + description: |- + StorageClass to use for PVCs. Applied after + evaluating the PVC template, if available. + If not specified, the generated PVCs will use the + default storage class type: string type: object temporary: default: false - description: When set to true, the tablespace will be added - as a `temp_tablespaces` entry in PostgreSQL, and will be available - to automatically house temp database objects, or other temporary - files. Please refer to PostgreSQL documentation for more information - on the `temp_tablespaces` GUC. + description: |- + When set to true, the tablespace will be added as a `temp_tablespaces` + entry in PostgreSQL, and will be available to automatically house temp + database objects, or other temporary files. Please refer to PostgreSQL + documentation for more information on the `temp_tablespaces` GUC. type: boolean required: - name @@ -4454,40 +4779,43 @@ spec: type: object type: array topologySpreadConstraints: - description: 'TopologySpreadConstraints specifies how to spread matching - pods among the given topology. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/' + description: |- + TopologySpreadConstraints specifies how to spread matching pods among the given topology. + More info: + https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/ items: description: TopologySpreadConstraint specifies how to spread matching pods among the given topology. properties: labelSelector: - description: LabelSelector is used to find matching pods. Pods - that match this label selector are counted to determine the - number of pods in their corresponding topology domain. + description: |- + LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine the number of pods + in their corresponding topology domain. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, - Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists or - DoesNotExist, the values array must be empty. This - array is replaced during a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -4499,126 +4827,134 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic matchLabelKeys: - description: "MatchLabelKeys is a set of pod label keys to select - the pods over which spreading will be calculated. The keys - are used to lookup values from the incoming pod labels, those - key-value labels are ANDed with labelSelector to select the - group of existing pods over which spreading will be calculated - for the incoming pod. The same key is forbidden to exist in - both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot - be set when LabelSelector isn't set. Keys that don't exist - in the incoming pod labels will be ignored. A null or empty - list means only match against labelSelector. \n This is a - beta field and requires the MatchLabelKeysInPodTopologySpread - feature gate to be enabled (enabled by default)." + description: |- + MatchLabelKeys is a set of pod label keys to select the pods over which + spreading will be calculated. The keys are used to lookup values from the + incoming pod labels, those key-value labels are ANDed with labelSelector + to select the group of existing pods over which spreading will be calculated + for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + MatchLabelKeys cannot be set when LabelSelector isn't set. + Keys that don't exist in the incoming pod labels will + be ignored. A null or empty list means only match against labelSelector. + + + This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic maxSkew: - description: 'MaxSkew describes the degree to which pods may - be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, - it is the maximum permitted difference between the number - of matching pods in the target topology and the global minimum. - The global minimum is the minimum number of matching pods - in an eligible domain or zero if the number of eligible domains - is less than MinDomains. For example, in a 3-zone cluster, - MaxSkew is set to 1, and pods with the same labelSelector - spread as 2/2/1: In this case, the global minimum is 1. | - zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew - is 1, incoming pod can only be scheduled to zone3 to become - 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) - on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming - pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, - it is used to give higher precedence to topologies that satisfy - it. It''s a required field. Default value is 1 and 0 is not - allowed.' + description: |- + MaxSkew describes the degree to which pods may be unevenly distributed. + When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference + between the number of matching pods in the target topology and the global minimum. + The global minimum is the minimum number of matching pods in an eligible domain + or zero if the number of eligible domains is less than MinDomains. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 2/2/1: + In this case, the global minimum is 1. + | zone1 | zone2 | zone3 | + | P P | P P | P | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; + scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) + violate MaxSkew(1). + - if MaxSkew is 2, incoming pod can be scheduled onto any zone. + When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence + to topologies that satisfy it. + It's a required field. Default value is 1 and 0 is not allowed. format: int32 type: integer minDomains: - description: "MinDomains indicates a minimum number of eligible - domains. When the number of eligible domains with matching - topology keys is less than minDomains, Pod Topology Spread - treats \"global minimum\" as 0, and then the calculation of - Skew is performed. And when the number of eligible domains - with matching topology keys equals or greater than minDomains, - this value has no effect on scheduling. As a result, when - the number of eligible domains is less than minDomains, scheduler - won't schedule more than maxSkew Pods to those domains. If - value is nil, the constraint behaves as if MinDomains is equal - to 1. Valid values are integers greater than 0. When value - is not nil, WhenUnsatisfiable must be DoNotSchedule. \n For - example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains - is set to 5 and pods with the same labelSelector spread as - 2/2/2: | zone1 | zone2 | zone3 | | P P | P P | P P | - The number of domains is less than 5(MinDomains), so \"global - minimum\" is treated as 0. In this situation, new pod with - the same labelSelector cannot be scheduled, because computed - skew will be 3(3 - 0) if new Pod is scheduled to any of the - three zones, it will violate MaxSkew. \n This is a beta field - and requires the MinDomainsInPodTopologySpread feature gate - to be enabled (enabled by default)." + description: |- + MinDomains indicates a minimum number of eligible domains. + When the number of eligible domains with matching topology keys is less than minDomains, + Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. + And when the number of eligible domains with matching topology keys equals or greater than minDomains, + this value has no effect on scheduling. + As a result, when the number of eligible domains is less than minDomains, + scheduler won't schedule more than maxSkew Pods to those domains. + If value is nil, the constraint behaves as if MinDomains is equal to 1. + Valid values are integers greater than 0. + When value is not nil, WhenUnsatisfiable must be DoNotSchedule. + + + For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same + labelSelector spread as 2/2/2: + | zone1 | zone2 | zone3 | + | P P | P P | P P | + The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. + In this situation, new pod with the same labelSelector cannot be scheduled, + because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, + it will violate MaxSkew. + + + This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default). format: int32 type: integer nodeAffinityPolicy: - description: "NodeAffinityPolicy indicates how we will treat - Pod's nodeAffinity/nodeSelector when calculating pod topology - spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector - are included in the calculations. - Ignore: nodeAffinity/nodeSelector - are ignored. All nodes are included in the calculations. \n - If this value is nil, the behavior is equivalent to the Honor - policy. This is a beta-level feature default enabled by the - NodeInclusionPolicyInPodTopologySpread feature flag." + description: |- + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector + when calculating pod topology spread skew. Options are: + - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. + - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. + + + If this value is nil, the behavior is equivalent to the Honor policy. + This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string nodeTaintsPolicy: - description: "NodeTaintsPolicy indicates how we will treat node - taints when calculating pod topology spread skew. Options - are: - Honor: nodes without taints, along with tainted nodes - for which the incoming pod has a toleration, are included. + description: |- + NodeTaintsPolicy indicates how we will treat node taints when calculating + pod topology spread skew. Options are: + - Honor: nodes without taints, along with tainted nodes for which the incoming pod + has a toleration, are included. - Ignore: node taints are ignored. All nodes are included. - \n If this value is nil, the behavior is equivalent to the - Ignore policy. This is a beta-level feature default enabled - by the NodeInclusionPolicyInPodTopologySpread feature flag." + + + If this value is nil, the behavior is equivalent to the Ignore policy. + This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string topologyKey: - description: TopologyKey is the key of node labels. Nodes that - have a label with this key and identical values are considered - to be in the same topology. We consider each - as a "bucket", and try to put balanced number of pods into - each bucket. We define a domain as a particular instance of - a topology. Also, we define an eligible domain as a domain - whose nodes meet the requirements of nodeAffinityPolicy and - nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname", - each Node is a domain of that topology. And, if TopologyKey - is "topology.kubernetes.io/zone", each zone is a domain of - that topology. It's a required field. + description: |- + TopologyKey is the key of node labels. Nodes that have a label with this key + and identical values are considered to be in the same topology. + We consider each as a "bucket", and try to put balanced number + of pods into each bucket. + We define a domain as a particular instance of a topology. + Also, we define an eligible domain as a domain whose nodes meet the requirements of + nodeAffinityPolicy and nodeTaintsPolicy. + e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. + And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. + It's a required field. type: string whenUnsatisfiable: - description: 'WhenUnsatisfiable indicates how to deal with a - pod if it doesn''t satisfy the spread constraint. - DoNotSchedule - (default) tells the scheduler not to schedule it. - ScheduleAnyway - tells the scheduler to schedule the pod in any location, but - giving higher precedence to topologies that would help reduce - the skew. A constraint is considered "Unsatisfiable" for an - incoming pod if and only if every possible node assignment - for that pod would violate "MaxSkew" on some topology. For - example, in a 3-zone cluster, MaxSkew is set to 1, and pods - with the same labelSelector spread as 3/1/1: | zone1 | zone2 - | zone3 | | P P P | P | P | If WhenUnsatisfiable is - set to DoNotSchedule, incoming pod can only be scheduled to - zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on - zone2(zone3) satisfies MaxSkew(1). In other words, the cluster - can still be imbalanced, but scheduler won''t make it *more* - imbalanced. It''s a required field.' + description: |- + WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy + the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule it. + - ScheduleAnyway tells the scheduler to schedule the pod in any location, + but giving higher precedence to topologies that would help reduce the + skew. + A constraint is considered "Unsatisfiable" for an incoming pod + if and only if every possible node assignment for that pod would violate + "MaxSkew" on some topology. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | + | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled + to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies + MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler + won't make it *more* imbalanced. + It's a required field. type: string required: - maxSkew @@ -4635,29 +4971,28 @@ spec: Claim properties: accessModes: - description: 'accessModes contains the desired access modes - the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 items: type: string type: array dataSource: - description: 'dataSource field can be used to specify either: + description: |- + dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the provisioner - or an external controller can support the specified data - source, it will create a new volume based on the contents - of the specified data source. When the AnyVolumeDataSource - feature gate is enabled, dataSource contents will be copied - to dataSourceRef, and dataSourceRef contents will be copied - to dataSource when dataSourceRef.namespace is not specified. - If the namespace is specified, then dataSourceRef will not - be copied to dataSource.' + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. properties: apiGroup: - description: APIGroup is the group for the resource being - referenced. If APIGroup is not specified, the specified - Kind must be in the core API group. For any other third-party - types, APIGroup is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource being referenced @@ -4671,38 +5006,36 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object from which - to populate the volume with data, if a non-empty volume - is desired. This may be any object from a non-empty API - group (non core object) or a PersistentVolumeClaim object. - When this field is specified, volume binding will only succeed - if the type of the specified object matches some installed - volume populator or dynamic provisioner. This field will - replace the functionality of the dataSource field and as - such if both fields are non-empty, they must have the same - value. For backwards compatibility, when namespace isn''t - specified in dataSourceRef, both fields (dataSource and - dataSourceRef) will be set to the same value automatically - if one of them is empty and the other is non-empty. When - namespace is specified in dataSourceRef, dataSource isn''t - set to the same value and must be empty. There are three - important differences between dataSource and dataSourceRef: - * While dataSource only allows two specific types of objects, - dataSourceRef allows any non-core object, as well as PersistentVolumeClaim - objects. * While dataSource ignores disallowed values (dropping - them), dataSourceRef preserves all values, and generates - an error if a disallowed value is specified. * While dataSource - only allows local objects, dataSourceRef allows objects - in any namespaces. (Beta) Using this field requires the - AnyVolumeDataSource feature gate to be enabled. (Alpha) - Using the namespace field of dataSourceRef requires the - CrossNamespaceVolumeDataSource feature gate to be enabled.' + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. properties: apiGroup: - description: APIGroup is the group for the resource being - referenced. If APIGroup is not specified, the specified - Kind must be in the core API group. For any other third-party - types, APIGroup is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource being referenced @@ -4711,49 +5044,23 @@ spec: description: Name is the name of resource being referenced type: string namespace: - description: Namespace is the namespace of resource being - referenced Note that when a namespace is specified, - a gateway.networking.k8s.io/ReferenceGrant object is - required in the referent namespace to allow that namespace's - owner to accept the reference. See the ReferenceGrant - documentation for details. (Alpha) This field requires - the CrossNamespaceVolumeDataSource feature gate to be - enabled. + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. type: string required: - kind - name type: object resources: - description: 'resources represents the minimum resources the - volume should have. If RecoverVolumeExpansionFailure feature - is enabled users are allowed to specify resource requirements - that are lower than previous value but must still be higher - than capacity recorded in the status field of the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. \n This field - is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -4761,8 +5068,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -4771,11 +5079,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of - compute resources required. If Requests is omitted for - a container, it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined value. - Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object selector: @@ -4786,8 +5094,8 @@ spec: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: @@ -4795,17 +5103,16 @@ spec: applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, - Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists or - DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -4817,22 +5124,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic storageClassName: - description: 'storageClassName is the name of the StorageClass - required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: - description: volumeMode defines what type of volume is required - by the claim. Value of Filesystem is implied when not included - in claim spec. + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. type: string volumeName: description: volumeName is the binding reference to the PersistentVolume @@ -4844,24 +5166,46 @@ spec: description: Resize existent PVCs, defaults to true type: boolean size: - description: Size of the storage. Required if not already specified - in the PVC template. Changes to this field are automatically - reapplied to the created PVCs. Size cannot be decreased. + description: |- + Size of the storage. Required if not already specified in the PVC template. + Changes to this field are automatically reapplied to the created PVCs. + Size cannot be decreased. type: string storageClass: - description: StorageClass to use for PVCs. Applied after evaluating - the PVC template, if available. If not specified, the generated - PVCs will use the default storage class + description: |- + StorageClass to use for PVCs. Applied after + evaluating the PVC template, if available. + If not specified, the generated PVCs will use the + default storage class type: string type: object required: - instances type: object status: - description: 'Most recently observed status of the cluster. This data - may not be up to date. Populated by the system. Read-only. More info: - https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + description: |- + Most recently observed status of the cluster. This data may not be up + to date. Populated by the system. Read-only. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status properties: + availableArchitectures: + description: AvailableArchitectures reports the available architectures + of a cluster + items: + description: AvailableArchitecture represents the state of a cluster's + architecture + properties: + goArch: + description: GoArch is the name of the executable architecture + type: string + hash: + description: Hash is the hash of the executable + type: string + required: + - goArch + - hash + type: object + type: array azurePVCUpdateEnabled: description: AzurePVCUpdateEnabled shows if the PVC online upgrade is enabled for this cluster @@ -4871,14 +5215,16 @@ spec: initialized with defaults. properties: clientCASecret: - description: 'The secret containing the Client CA certificate. - If not defined, a new secret will be created with a self-signed - CA and will be used to generate all the client certificates.

    Contains:

    - `ca.crt`: CA that should - be used to validate the client certificates, used as `ssl_ca_file` - of all the instances.
    - `ca.key`: key used to generate - client certificates, if ReplicationTLSSecret is provided, this - can be omitted.
    ' + description: |- + The secret containing the Client CA certificate. If not defined, a new secret will be created + with a self-signed CA and will be used to generate all the client certificates.
    +
    + Contains:
    +
    + - `ca.crt`: CA that should be used to validate the client certificates, + used as `ssl_ca_file` of all the instances.
    + - `ca.key`: key used to generate client certificates, if ReplicationTLSSecret is provided, + this can be omitted.
    type: string expirations: additionalProperties: @@ -4886,10 +5232,11 @@ spec: description: Expiration dates for all certificates. type: object replicationTLSSecret: - description: The secret of type kubernetes.io/tls containing the - client certificate to authenticate as the `streaming_replica` - user. If not defined, ClientCASecret must provide also `ca.key`, - and a new secret will be created using the provided CA. + description: |- + The secret of type kubernetes.io/tls containing the client certificate to authenticate as + the `streaming_replica` user. + If not defined, ClientCASecret must provide also `ca.key`, and a new secret will be + created using the provided CA. type: string serverAltDNSNames: description: The list of the server alternative DNS names to be @@ -4898,21 +5245,23 @@ spec: type: string type: array serverCASecret: - description: 'The secret containing the Server CA certificate. - If not defined, a new secret will be created with a self-signed - CA and will be used to generate the TLS certificate ServerTLSSecret.

    Contains:

    - `ca.crt`: CA that should - be used to validate the server certificate, used as `sslrootcert` - in client connection strings.
    - `ca.key`: key used to - generate Server SSL certs, if ServerTLSSecret is provided, this - can be omitted.
    ' + description: |- + The secret containing the Server CA certificate. If not defined, a new secret will be created + with a self-signed CA and will be used to generate the TLS certificate ServerTLSSecret.
    +
    + Contains:
    +
    + - `ca.crt`: CA that should be used to validate the server certificate, + used as `sslrootcert` in client connection strings.
    + - `ca.key`: key used to generate Server SSL certs, if ServerTLSSecret is provided, + this can be omitted.
    type: string serverTLSSecret: - description: The secret of type kubernetes.io/tls containing the - server TLS certificate and key that will be set as `ssl_cert_file` - and `ssl_key_file` so that clients can connect to postgres securely. - If not defined, ServerCASecret must provide also `ca.key` and - a new secret will be created using the provided CA. + description: |- + The secret of type kubernetes.io/tls containing the server TLS certificate and key that will be set as + `ssl_cert_file` and `ssl_key_file` so that clients can connect to postgres securely. + If not defined, ServerCASecret must provide also `ca.key` and a new secret will be + created using the provided CA. type: string type: object cloudNativePGCommitHash: @@ -4925,42 +5274,42 @@ spec: description: Conditions for cluster object items: description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 @@ -4974,11 +5323,12 @@ spec: - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -4991,39 +5341,43 @@ spec: type: object type: array configMapResourceVersion: - description: The list of resource versions of the configmaps, managed - by the operator. Every change here is done in the interest of the - instance manager, which will refresh the configmap data + description: |- + The list of resource versions of the configmaps, + managed by the operator. Every change here is done in the + interest of the instance manager, which will refresh the + configmap data properties: metrics: additionalProperties: type: string - description: A map with the versions of all the config maps used - to pass metrics. Map keys are the config map names, map values - are the versions + description: |- + A map with the versions of all the config maps used to pass metrics. + Map keys are the config map names, map values are the versions type: object type: object currentPrimary: description: Current primary instance type: string currentPrimaryFailingSinceTimestamp: - description: The timestamp when the primary was detected to be unhealthy - This field is reported when `.spec.failoverDelay` is populated or - during online upgrades + description: |- + The timestamp when the primary was detected to be unhealthy + This field is reported when `.spec.failoverDelay` is populated or during online upgrades type: string currentPrimaryTimestamp: description: The timestamp when the last actual promotion to primary has occurred type: string danglingPVC: - description: List of all the PVCs created by this cluster and still - available which are not attached to a Pod + description: |- + List of all the PVCs created by this cluster and still available + which are not attached to a Pod items: type: string type: array firstRecoverabilityPoint: - description: The first recoverability point, stored as a date in RFC3339 - format. This field is calculated from the content of FirstRecoverabilityPointByMethod + description: |- + The first recoverability point, stored as a date in RFC3339 format. + This field is calculated from the content of FirstRecoverabilityPointByMethod type: string firstRecoverabilityPointByMethod: additionalProperties: @@ -5085,7 +5439,8 @@ spec: description: Stored as a date in RFC3339 format type: string lastSuccessfulBackup: - description: Last successful backup, stored as a date in RFC3339 format + description: |- + Last successful backup, stored as a date in RFC3339 format This field is calculated from the content of LastSuccessfulBackupByMethod type: string lastSuccessfulBackupByMethod: @@ -5115,8 +5470,9 @@ spec: items: type: string type: array - description: CannotReconcile lists roles that cannot be reconciled - in PostgreSQL, with an explanation of the cause + description: |- + CannotReconcile lists roles that cannot be reconciled in PostgreSQL, + with an explanation of the cause type: object passwordStatus: additionalProperties: @@ -5176,9 +5532,11 @@ spec: type: string type: array secretsResourceVersion: - description: The list of resource versions of the secrets managed - by the operator. Every change here is done in the interest of the - instance manager, which will refresh the secret data + description: |- + The list of resource versions of the secrets + managed by the operator. Every change here is done in the + interest of the instance manager, which will refresh the + secret data properties: applicationSecretVersion: description: The resource version of the "app" user secret @@ -5207,9 +5565,9 @@ spec: metrics: additionalProperties: type: string - description: A map with the versions of all the secrets used to - pass metrics. Map keys are the secret names, map values are - the versions + description: |- + A map with the versions of all the secrets used to pass metrics. + Map keys are the secret names, map values are the versions type: object replicationSecretVersion: description: The resource version of the "streaming_replica" user @@ -5252,8 +5610,9 @@ spec: type: object type: array targetPrimary: - description: Target primary instance, this is different from the previous - one during a switchover or a failover + description: |- + Target primary instance, this is different from the previous one + during a switchover or a failover type: string targetPrimaryTimestamp: description: The timestamp when the last request for a new primary @@ -5275,18 +5634,18 @@ spec: description: Instances contains the pod topology of the instances type: object nodesUsed: - description: NodesUsed represents the count of distinct nodes - accommodating the instances. A value of '1' suggests that all - instances are hosted on a single node, implying the absence - of High Availability (HA). Ideally, this value should be the - same as the number of instances in the Postgres HA cluster, - implying shared nothing architecture on the compute side. + description: |- + NodesUsed represents the count of distinct nodes accommodating the instances. + A value of '1' suggests that all instances are hosted on a single node, + implying the absence of High Availability (HA). Ideally, this value should + be the same as the number of instances in the Postgres HA cluster, implying + shared nothing architecture on the compute side. format: int32 type: integer successfullyExtracted: - description: SuccessfullyExtracted indicates if the topology data - was extract. It is useful to enact fallback behaviors in synchronous - replica election in case of failures + description: |- + SuccessfullyExtracted indicates if the topology data was extract. It is useful to enact fallback behaviors + in synchronous replica election in case of failures type: boolean type: object unusablePVC: @@ -5315,7 +5674,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 helm.sh/resource-policy: keep name: poolers.postgresql.cnpg.io spec: @@ -5343,25 +5702,31 @@ spec: description: Pooler is the Schema for the poolers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: - description: 'Specification of the desired behavior of the Pooler. More - info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + description: |- + Specification of the desired behavior of the Pooler. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status properties: cluster: - description: This is the cluster reference on which the Pooler will - work. Pooler name should never match with any cluster name within - the same namespace. + description: |- + This is the cluster reference on which the Pooler will work. + Pooler name should never match with any cluster name within the same namespace. properties: name: description: Name of the referent. @@ -5374,42 +5739,45 @@ spec: existing pods with new ones properties: rollingUpdate: - description: 'Rolling update config params. Present only if DeploymentStrategyType - = RollingUpdate. --- TODO: Update this to follow our convention - for oneOf, whatever we decide it to be.' + description: |- + Rolling update config params. Present only if DeploymentStrategyType = + RollingUpdate. + --- + TODO: Update this to follow our convention for oneOf, whatever we decide it + to be. properties: maxSurge: anyOf: - type: integer - type: string - description: 'The maximum number of pods that can be scheduled - above the desired number of pods. Value can be an absolute - number (ex: 5) or a percentage of desired pods (ex: 10%). - This can not be 0 if MaxUnavailable is 0. Absolute number - is calculated from percentage by rounding up. Defaults to - 25%. Example: when this is set to 30%, the new ReplicaSet - can be scaled up immediately when the rolling update starts, - such that the total number of old and new pods do not exceed - 130% of desired pods. Once old pods have been killed, new - ReplicaSet can be scaled up further, ensuring that total - number of pods running at any time during the update is - at most 130% of desired pods.' + description: |- + The maximum number of pods that can be scheduled above the desired number of + pods. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + This can not be 0 if MaxUnavailable is 0. + Absolute number is calculated from percentage by rounding up. + Defaults to 25%. + Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when + the rolling update starts, such that the total number of old and new pods do not exceed + 130% of desired pods. Once old pods have been killed, + new ReplicaSet can be scaled up further, ensuring that total number of pods running + at any time during the update is at most 130% of desired pods. x-kubernetes-int-or-string: true maxUnavailable: anyOf: - type: integer - type: string - description: 'The maximum number of pods that can be unavailable - during the update. Value can be an absolute number (ex: - 5) or a percentage of desired pods (ex: 10%). Absolute number - is calculated from percentage by rounding down. This can - not be 0 if MaxSurge is 0. Defaults to 25%. Example: when - this is set to 30%, the old ReplicaSet can be scaled down - to 70% of desired pods immediately when the rolling update - starts. Once new pods are ready, old ReplicaSet can be scaled - down further, followed by scaling up the new ReplicaSet, - ensuring that the total number of pods available at all - times during the update is at least 70% of desired pods.' + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding down. + This can not be 0 if MaxSurge is 0. + Defaults to 25%. + Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods + immediately when the rolling update starts. Once new pods are ready, old ReplicaSet + can be scaled down further, followed by scaling up the new ReplicaSet, ensuring + that the total number of pods available at all times during the update is at + least 70% of desired pods. x-kubernetes-int-or-string: true type: object type: @@ -5434,16 +5802,24 @@ spec: description: The list of metric relabelings for the `PodMonitor`. Applied to samples before ingestion. items: - description: "RelabelConfig allows dynamic rewriting of the - label set for targets, alerts, scraped samples and remote - write samples. \n More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config" + description: |- + RelabelConfig allows dynamic rewriting of the label set for targets, alerts, + scraped samples and remote write samples. + + + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config properties: action: default: replace - description: "Action to perform based on the regex matching. - \n `Uppercase` and `Lowercase` actions require Prometheus - >= v2.36.0. `DropEqual` and `KeepEqual` actions require - Prometheus >= v2.41.0. \n Default: \"Replace\"" + description: |- + Action to perform based on the regex matching. + + + `Uppercase` and `Lowercase` actions require Prometheus >= v2.36.0. + `DropEqual` and `KeepEqual` actions require Prometheus >= v2.41.0. + + + Default: "Replace" enum: - replace - Replace @@ -5469,8 +5845,11 @@ spec: - DropEqual type: string modulus: - description: "Modulus to take of the hash of the source - label values. \n Only applicable when the action is `HashMod`." + description: |- + Modulus to take of the hash of the source label values. + + + Only applicable when the action is `HashMod`. format: int64 type: integer regex: @@ -5478,30 +5857,39 @@ spec: value is matched. type: string replacement: - description: "Replacement value against which a Replace - action is performed if the regular expression matches. - \n Regex capture groups are available." + description: |- + Replacement value against which a Replace action is performed if the + regular expression matches. + + + Regex capture groups are available. type: string separator: description: Separator is the string between concatenated SourceLabels. type: string sourceLabels: - description: The source labels select values from existing - labels. Their content is concatenated using the configured - Separator and matched against the configured regular expression. + description: |- + The source labels select values from existing labels. Their content is + concatenated using the configured Separator and matched against the + configured regular expression. items: - description: LabelName is a valid Prometheus label name - which may only contain ASCII letters, numbers, as well - as underscores. + description: |- + LabelName is a valid Prometheus label name which may only contain ASCII + letters, numbers, as well as underscores. pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ type: string type: array targetLabel: - description: "Label to which the resulting string is written - in a replacement. \n It is mandatory for `Replace`, `HashMod`, - `Lowercase`, `Uppercase`, `KeepEqual` and `DropEqual` - actions. \n Regex capture groups are available." + description: |- + Label to which the resulting string is written in a replacement. + + + It is mandatory for `Replace`, `HashMod`, `Lowercase`, `Uppercase`, + `KeepEqual` and `DropEqual` actions. + + + Regex capture groups are available. type: string type: object type: array @@ -5509,16 +5897,24 @@ spec: description: The list of relabelings for the `PodMonitor`. Applied to samples before scraping. items: - description: "RelabelConfig allows dynamic rewriting of the - label set for targets, alerts, scraped samples and remote - write samples. \n More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config" + description: |- + RelabelConfig allows dynamic rewriting of the label set for targets, alerts, + scraped samples and remote write samples. + + + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config properties: action: default: replace - description: "Action to perform based on the regex matching. - \n `Uppercase` and `Lowercase` actions require Prometheus - >= v2.36.0. `DropEqual` and `KeepEqual` actions require - Prometheus >= v2.41.0. \n Default: \"Replace\"" + description: |- + Action to perform based on the regex matching. + + + `Uppercase` and `Lowercase` actions require Prometheus >= v2.36.0. + `DropEqual` and `KeepEqual` actions require Prometheus >= v2.41.0. + + + Default: "Replace" enum: - replace - Replace @@ -5544,8 +5940,11 @@ spec: - DropEqual type: string modulus: - description: "Modulus to take of the hash of the source - label values. \n Only applicable when the action is `HashMod`." + description: |- + Modulus to take of the hash of the source label values. + + + Only applicable when the action is `HashMod`. format: int64 type: integer regex: @@ -5553,30 +5952,39 @@ spec: value is matched. type: string replacement: - description: "Replacement value against which a Replace - action is performed if the regular expression matches. - \n Regex capture groups are available." + description: |- + Replacement value against which a Replace action is performed if the + regular expression matches. + + + Regex capture groups are available. type: string separator: description: Separator is the string between concatenated SourceLabels. type: string sourceLabels: - description: The source labels select values from existing - labels. Their content is concatenated using the configured - Separator and matched against the configured regular expression. + description: |- + The source labels select values from existing labels. Their content is + concatenated using the configured Separator and matched against the + configured regular expression. items: - description: LabelName is a valid Prometheus label name - which may only contain ASCII letters, numbers, as well - as underscores. + description: |- + LabelName is a valid Prometheus label name which may only contain ASCII + letters, numbers, as well as underscores. pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ type: string type: array targetLabel: - description: "Label to which the resulting string is written - in a replacement. \n It is mandatory for `Replace`, `HashMod`, - `Lowercase`, `Uppercase`, `KeepEqual` and `DropEqual` - actions. \n Regex capture groups are available." + description: |- + Label to which the resulting string is written in a replacement. + + + It is mandatory for `Replace`, `HashMod`, `Lowercase`, `Uppercase`, + `KeepEqual` and `DropEqual` actions. + + + Regex capture groups are available. type: string type: object type: array @@ -5585,18 +5993,18 @@ spec: description: The PgBouncer configuration properties: authQuery: - description: 'The query that will be used to download the hash - of the password of a certain user. Default: "SELECT usename, - passwd FROM user_search($1)". In case it is specified, also - an AuthQuerySecret has to be specified and no automatic CNPG - Cluster integration will be triggered.' + description: |- + The query that will be used to download the hash of the password + of a certain user. Default: "SELECT usename, passwd FROM public.user_search($1)". + In case it is specified, also an AuthQuerySecret has to be specified and + no automatic CNPG Cluster integration will be triggered. type: string authQuerySecret: - description: The credentials of the user that need to be used - for the authentication query. In case it is specified, also - an AuthQuery (e.g. "SELECT usename, passwd FROM pg_shadow WHERE - usename=$1") has to be specified and no automatic CNPG Cluster - integration will be triggered. + description: |- + The credentials of the user that need to be used for the authentication + query. In case it is specified, also an AuthQuery + (e.g. "SELECT usename, passwd FROM pg_catalog.pg_shadow WHERE usename=$1") + has to be specified and no automatic CNPG Cluster integration will be triggered. properties: name: description: Name of the referent. @@ -5607,21 +6015,22 @@ spec: parameters: additionalProperties: type: string - description: Additional parameters to be passed to PgBouncer - - please check the CNPG documentation for a list of options you - can configure + description: |- + Additional parameters to be passed to PgBouncer - please check + the CNPG documentation for a list of options you can configure type: object paused: default: false - description: When set to `true`, PgBouncer will disconnect from - the PostgreSQL server, first waiting for all queries to complete, - and pause all new client connections until this value is set - to `false` (default). Internally, the operator calls PgBouncer's - `PAUSE` and `RESUME` commands. + description: |- + When set to `true`, PgBouncer will disconnect from the PostgreSQL + server, first waiting for all queries to complete, and pause all new + client connections until this value is set to `false` (default). Internally, + the operator calls PgBouncer's `PAUSE` and `RESUME` commands. type: boolean pg_hba: - description: PostgreSQL Host Based Authentication rules (lines - to be appended to the pg_hba.conf file) + description: |- + PostgreSQL Host Based Authentication rules (lines to be appended + to the pg_hba.conf file) items: type: string type: array @@ -5637,34 +6046,38 @@ spec: description: The template of the Pod to be created properties: metadata: - description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + description: |- + Standard object's metadata. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata properties: annotations: additionalProperties: type: string - description: 'Annotations is an unstructured key value map - stored with a resource that may be set by external tools - to store and retrieve arbitrary metadata. They are not queryable - and should be preserved when modifying objects. More info: - http://kubernetes.io/docs/user-guide/annotations' + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations type: object labels: additionalProperties: type: string - description: 'Map of string keys and values that can be used - to organize and categorize (scope and select) objects. May - match selectors of replication controllers and services. - More info: http://kubernetes.io/docs/user-guide/labels' + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: http://kubernetes.io/docs/user-guide/labels type: object type: object spec: - description: 'Specification of the desired behavior of the pod. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + description: |- + Specification of the desired behavior of the pod. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status properties: activeDeadlineSeconds: - description: Optional duration in seconds the pod may be active - on the node relative to StartTime before the system will - actively try to mark it failed and kill associated containers. + description: |- + Optional duration in seconds the pod may be active on the node relative to + StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer. format: int64 type: integer @@ -5676,23 +6089,20 @@ spec: for the pod. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node matches the corresponding matchExpressions; - the node(s) with the highest sum are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: description: A node selector term, associated @@ -5702,32 +6112,26 @@ spec: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -5740,32 +6144,26 @@ spec: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -5788,53 +6186,46 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to an update), the system may or may not try - to eventually evict the pod from its node. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: description: Required. A list of node selector terms. The terms are ORed. items: - description: A null or empty node selector term - matches no objects. The requirements of them - are ANDed. The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -5847,32 +6238,26 @@ spec: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. - If the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -5895,19 +6280,16 @@ spec: other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements - of this field and adding "weight" to the sum if - the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -5918,18 +6300,18 @@ spec: associated with the corresponding weight. properties: labelSelector: - description: A label query over a set of - resources, in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -5937,20 +6319,16 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -5962,36 +6340,59 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. + Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -5999,20 +6400,16 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -6024,46 +6421,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -6072,59 +6460,52 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - affinity requirements specified by this field cease - to be met at some point during pod execution (e.g. - due to a pod label update), the system may or may - not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes - corresponding to each podAffinityTerm are intersected, - i.e. all terms must be satisfied. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -6136,53 +6517,75 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. + Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -6194,34 +6597,28 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: @@ -6235,19 +6632,16 @@ spec: etc. as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. The - node that is most preferred is the one with the - greatest sum of weights, i.e. for each node that - meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating through - the elements of this field and adding "weight" to - the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum - are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred @@ -6258,18 +6652,18 @@ spec: associated with the corresponding weight. properties: labelSelector: - description: A label query over a set of - resources, in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -6277,20 +6671,16 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -6302,36 +6692,59 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. + Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies to. - The term is applied to the union of the - namespaces selected by this field and - the ones listed in the namespaces field. - null selector and null or empty namespaces - list means "this pod's namespace". An - empty selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -6339,20 +6752,16 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -6364,46 +6773,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -6412,59 +6812,52 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, the - pod will not be scheduled onto the node. If the - anti-affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node - whose value of the label with key - matches that of any node on which a pod of the - set of pods is running + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -6476,53 +6869,75 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. + Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -6534,34 +6949,28 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. Empty topologyKey is not allowed. type: string required: @@ -6575,46 +6984,45 @@ spec: a service account token should be automatically mounted. type: boolean containers: - description: List of containers belonging to the pod. Containers - cannot currently be added or removed. There must be at least - one container in a Pod. Cannot be updated. + description: |- + List of containers belonging to the pod. + Containers cannot currently be added or removed. + There must be at least one container in a Pod. + Cannot be updated. items: description: A single application container that you want to run within a pod. properties: args: - description: 'Arguments to the entrypoint. The container - image''s CMD is used if this is not provided. Variable - references $(VAR_NAME) are expanded using the container''s - environment. If a variable cannot be resolved, the - reference in the input string will be unchanged. Double - $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce - the string literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of whether the - variable exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell items: type: string type: array command: - description: 'Entrypoint array. Not executed within - a shell. The container image''s ENTRYPOINT is used - if this is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. If - a variable cannot be resolved, the reference in the - input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) - syntax: i.e. "$$(VAR_NAME)" will produce the string - literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists - or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell items: type: string type: array env: - description: List of environment variables to set in - the container. Cannot be updated. + description: |- + List of environment variables to set in the container. + Cannot be updated. items: description: EnvVar represents an environment variable present in a Container. @@ -6624,17 +7032,16 @@ spec: Must be a C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) - are expanded using the previously defined environment - variables in the container and any service environment - variables. If a variable cannot be resolved, - the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" - will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults - to "".' + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's @@ -6647,10 +7054,10 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap @@ -6661,11 +7068,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: - supports metadata.name, metadata.namespace, - `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the @@ -6681,11 +7086,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory and requests.ephemeral-storage) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required @@ -6717,10 +7120,10 @@ spec: key. type: string name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret @@ -6736,14 +7139,13 @@ spec: type: object type: array envFrom: - description: List of sources to populate environment - variables in the container. The keys defined within - a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is - starting. When a key exists in multiple sources, the - value associated with the last source will take precedence. - Values defined by an Env with a duplicate key will - take precedence. Cannot be updated. + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. items: description: EnvFromSource represents the source of a set of ConfigMaps @@ -6752,10 +7154,10 @@ spec: description: The ConfigMap to select from properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap @@ -6771,10 +7173,10 @@ spec: description: The Secret to select from properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret must @@ -6785,44 +7187,42 @@ spec: type: object type: array image: - description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level config - management to default or override container images - in workload controllers like Deployments and StatefulSets.' + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. type: string imagePullPolicy: - description: 'Image pull policy. One of Always, Never, - IfNotPresent. Defaults to Always if :latest tag is - specified, or IfNotPresent otherwise. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images type: string lifecycle: - description: Actions that the management system should - take in response to container lifecycle events. Cannot - be updated. + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. properties: postStart: - description: 'PostStart is called immediately after - a container is created. If the handler fails, - the container is terminated and restarted according - to its restart policy. Other management of the - container blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line - to execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside - a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, - you need to explicitly call out to that - shell. Exit status of 0 is treated as - live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array @@ -6832,8 +7232,8 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. type: string httpHeaders: @@ -6844,10 +7244,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. - This will be canonicalized upon - output, so case-variant names will - be understood as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -6865,24 +7264,36 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object + sleep: + description: Sleep represents the duration that + the container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward - compatibility. There are no validation of - this field and lifecycle hooks will fail in - runtime when tcp handler is specified. + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for the backward compatibility. There are no validation of this field and + lifecycle hooks will fail in runtime when tcp handler is specified. properties: host: description: 'Optional: Host name to connect @@ -6892,44 +7303,37 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object type: object preStop: - description: 'PreStop is called immediately before - a container is terminated due to an API request - or management event such as liveness/startup probe - failure, preemption, resource contention, etc. - The handler is not called if the container crashes - or exits. The Pod''s termination grace period - countdown begins before the PreStop hook is executed. - Regardless of the outcome of the handler, the - container will eventually terminate within the - Pod''s termination grace period (unless delayed - by finalizers). Other management of the container - blocks until the hook completes or until the termination - grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line - to execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside - a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, - you need to explicitly call out to that - shell. Exit status of 0 is treated as - live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array @@ -6939,8 +7343,8 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. type: string httpHeaders: @@ -6951,10 +7355,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. - This will be canonicalized upon - output, so case-variant names will - be understood as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -6972,24 +7375,36 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object + sleep: + description: Sleep represents the duration that + the container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward - compatibility. There are no validation of - this field and lifecycle hooks will fail in - runtime when tcp handler is specified. + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for the backward compatibility. There are no validation of this field and + lifecycle hooks will fail in runtime when tcp handler is specified. properties: host: description: 'Optional: Host name to connect @@ -6999,10 +7414,10 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port @@ -7010,30 +7425,29 @@ spec: type: object type: object livenessProbe: - description: 'Periodic probe of container liveness. - Container will be restarted if the probe fails. Cannot - be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') in - the container's filesystem. The command is - simply exec'd, it is not run inside a shell, - so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is - treated as live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array type: object failureThreshold: - description: Minimum consecutive failures for the - probe to be considered failed after having succeeded. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. format: int32 type: integer @@ -7047,11 +7461,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service - to place in the gRPC HealthCheckRequest (see - https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -7061,9 +7476,9 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set "Host" - in httpHeaders instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. @@ -7073,10 +7488,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. This - will be canonicalized upon output, so - case-variant names will be understood - as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -7093,34 +7507,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to - the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the - probe. Default to 10 seconds. Minimum value is - 1. + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the - probe to be considered successful after having - failed. Defaults to 1. Must be 1 for liveness - and startup. Minimum value is 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -7135,61 +7550,59 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully upon probe failure. - The grace period is the duration in seconds after - the processes running in the pod are sent a termination - signal and the time when the processes are forcibly - halted with a kill signal. Set this value longer - than the expected cleanup time for your process. - If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides - the value provided by the pod spec. Value must - be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity - to shut down). This is a beta field and requires - enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object name: - description: Name of the container specified as a DNS_LABEL. + description: |- + Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated. type: string ports: - description: List of ports to expose from the container. - Not specifying a port here DOES NOT prevent that port - from being exposed. Any port which is listening on - the default "0.0.0.0" address inside a container will - be accessible from the network. Modifying this array - with strategic merge patch may corrupt the data. For - more information See https://github.com/kubernetes/kubernetes/issues/108255. + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated. items: description: ContainerPort represents a network port in a single container. properties: containerPort: - description: Number of port to expose on the pod's - IP address. This must be a valid port number, - 0 < x < 65536. + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. format: int32 type: integer hostIP: @@ -7197,23 +7610,24 @@ spec: port to. type: string hostPort: - description: Number of port to expose on the host. - If specified, this must be a valid port number, - 0 < x < 65536. If HostNetwork is specified, - this must match ContainerPort. Most containers - do not need this. + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. format: int32 type: integer name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port in - a pod must have a unique name. Name for the - port that can be referred to by services. + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. type: string protocol: default: TCP - description: Protocol for port. Must be UDP, TCP, - or SCTP. Defaults to "TCP". + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". type: string required: - containerPort @@ -7224,30 +7638,29 @@ spec: - protocol x-kubernetes-list-type: map readinessProbe: - description: 'Periodic probe of container service readiness. - Container will be removed from service endpoints if - the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') in - the container's filesystem. The command is - simply exec'd, it is not run inside a shell, - so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is - treated as live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array type: object failureThreshold: - description: Minimum consecutive failures for the - probe to be considered failed after having succeeded. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. format: int32 type: integer @@ -7261,11 +7674,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service - to place in the gRPC HealthCheckRequest (see - https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -7275,9 +7689,9 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set "Host" - in httpHeaders instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. @@ -7287,10 +7701,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. This - will be canonicalized upon output, so - case-variant names will be understood - as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -7307,34 +7720,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to - the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the - probe. Default to 10 seconds. Minimum value is - 1. + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the - probe to be considered successful after having - failed. Defaults to 1. Must be 1 for liveness - and startup. Minimum value is 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -7349,36 +7763,33 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully upon probe failure. - The grace period is the duration in seconds after - the processes running in the pod are sent a termination - signal and the time when the processes are forcibly - halted with a kill signal. Set this value longer - than the expected cleanup time for your process. - If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides - the value provided by the pod spec. Value must - be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity - to shut down). This is a beta field and requires - enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object @@ -7389,14 +7800,14 @@ spec: resize policy for the container. properties: resourceName: - description: 'Name of the resource to which this - resource resize policy applies. Supported values: - cpu, memory.' + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. type: string restartPolicy: - description: Restart policy to apply when specified - resource is resized. If not specified, it defaults - to NotRequired. + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. type: string required: - resourceName @@ -7405,25 +7816,31 @@ spec: type: array x-kubernetes-list-type: atomic resources: - description: 'Compute Resources required by this container. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ properties: claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used - by this container. \n This is an alpha field and - requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can - only be set for containers." + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one - entry in pod.spec.resourceClaims of the - Pod where this field is used. It makes that - resource available inside a container. + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. type: string required: - name @@ -7439,8 +7856,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -7449,57 +7867,52 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is - omitted for a container, it defaults to Limits - if that is explicitly specified, otherwise to - an implementation-defined value. Requests cannot - exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object restartPolicy: - description: 'RestartPolicy defines the restart behavior - of individual containers in a pod. This field may - only be set for init containers, and the only allowed - value is "Always". For non-init containers or when - this field is not specified, the restart behavior - is defined by the Pod''s restart policy and the container - type. Setting the RestartPolicy as "Always" for the - init container will have the following effect: this - init container will be continually restarted on exit - until all regular containers have terminated. Once - all regular containers have completed, all init containers - with restartPolicy "Always" will be shut down. This - lifecycle differs from normal init containers and - is often referred to as a "sidecar" container. Although - this init container still starts in the init container - sequence, it does not wait for the container to complete - before proceeding to the next init container. Instead, - the next init container starts immediately after this - init container is started, or after any startupProbe - has successfully completed.' + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This field may only be set for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Setting the RestartPolicy as "Always" for the init container will have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. type: string securityContext: - description: 'SecurityContext defines the security options - the container should be run with. If set, the fields - of SecurityContext override the equivalent fields - of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ properties: allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls - whether a process can gain more privileges than - its parent process. This bool directly controls - if the no_new_privs flag will be set on the container - process. AllowPrivilegeEscalation is true always - when the container is: 1) run as Privileged 2) - has CAP_SYS_ADMIN Note that this field cannot - be set when spec.os.name is windows.' + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. type: boolean capabilities: - description: The capabilities to add/drop when running - containers. Defaults to the default set of capabilities - granted by the container runtime. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. properties: add: description: Added capabilities @@ -7517,66 +7930,60 @@ spec: type: array type: object privileged: - description: Run container in privileged mode. Processes - in privileged containers are essentially equivalent - to root on the host. Defaults to false. Note that - this field cannot be set when spec.os.name is - windows. + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. type: boolean procMount: - description: procMount denotes the type of proc - mount to use for the containers. The default is - DefaultProcMount which uses the container runtime - defaults for readonly paths and masked paths. - This requires the ProcMountType feature flag to - be enabled. Note that this field cannot be set - when spec.os.name is windows. + description: |- + procMount denotes the type of proc mount to use for the containers. + The default is DefaultProcMount which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. type: string readOnlyRootFilesystem: - description: Whether this container has a read-only - root filesystem. Default is false. Note that this - field cannot be set when spec.os.name is windows. + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. type: boolean runAsGroup: - description: The GID to run the entrypoint of the - container process. Uses runtime default if unset. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is windows. + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer runAsNonRoot: - description: Indicates that the container must run - as a non-root user. If true, the Kubelet will - validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start - the container if it does. If unset or false, no - such validation will be performed. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in - SecurityContext takes precedence. + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: boolean runAsUser: - description: The UID to run the entrypoint of the - container process. Defaults to user specified - in image metadata if unspecified. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in - SecurityContext takes precedence. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer seLinuxOptions: - description: The SELinux context to be applied to - the container. If unspecified, the container runtime - will allocate a random SELinux context for each - container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is windows. + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. properties: level: description: Level is SELinux level label that @@ -7596,104 +8003,92 @@ spec: type: string type: object seccompProfile: - description: The seccomp options to use by this - container. If seccomp options are provided at - both the pod & container level, the container - options override the pod options. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. properties: localhostProfile: - description: localhostProfile indicates a profile - defined in a file on the node should be used. - The profile must be preconfigured on the node - to work. Must be a descending path, relative - to the kubelet's configured seccomp profile - location. Must be set if type is "Localhost". - Must NOT be set for any other type. + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. type: string type: - description: "type indicates which kind of seccomp - profile will be applied. Valid options are: - \n Localhost - a profile defined in a file - on the node should be used. RuntimeDefault - - the container runtime default profile should - be used. Unconfined - no profile should be - applied." + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. type: string required: - type type: object windowsOptions: - description: The Windows specific settings applied - to all containers. If unspecified, the options - from the PodSecurityContext will be used. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is linux. + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. properties: gmsaCredentialSpec: - description: GMSACredentialSpec is where the - GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential - spec named by the GMSACredentialSpecName field. + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. type: string gmsaCredentialSpecName: description: GMSACredentialSpecName is the name of the GMSA credential spec to use. type: string hostProcess: - description: HostProcess determines if a container - should be run as a 'Host Process' container. - All of a Pod's containers must have the same - effective HostProcess value (it is not allowed - to have a mix of HostProcess containers and - non-HostProcess containers). In addition, - if HostProcess is true then HostNetwork must - also be set to true. + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean runAsUserName: - description: The UserName in Windows to run - the entrypoint of the container process. Defaults - to the user specified in image metadata if - unspecified. May also be set in PodSecurityContext. - If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: string type: object type: object startupProbe: - description: 'StartupProbe indicates that the Pod has - successfully initialized. If specified, no other probes - are executed until this completes successfully. If - this probe fails, the Pod will be restarted, just - as if the livenessProbe failed. This can be used to - provide different probe parameters at the beginning - of a Pod''s lifecycle, when it might take a long time - to load data or warm a cache, than during steady-state - operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') in - the container's filesystem. The command is - simply exec'd, it is not run inside a shell, - so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is - treated as live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array type: object failureThreshold: - description: Minimum consecutive failures for the - probe to be considered failed after having succeeded. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. format: int32 type: integer @@ -7707,11 +8102,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service - to place in the gRPC HealthCheckRequest (see - https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -7721,9 +8117,9 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set "Host" - in httpHeaders instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. @@ -7733,10 +8129,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. This - will be canonicalized upon output, so - case-variant names will be understood - as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -7753,34 +8148,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to - the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the - probe. Default to 10 seconds. Minimum value is - 1. + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the - probe to be considered successful after having - failed. Defaults to 1. Must be 1 for liveness - and startup. Minimum value is 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -7795,83 +8191,75 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully upon probe failure. - The grace period is the duration in seconds after - the processes running in the pod are sent a termination - signal and the time when the processes are forcibly - halted with a kill signal. Set this value longer - than the expected cleanup time for your process. - If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides - the value provided by the pod spec. Value must - be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity - to shut down). This is a beta field and requires - enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object stdin: - description: Whether this container should allocate - a buffer for stdin in the container runtime. If this - is not set, reads from stdin in the container will - always result in EOF. Default is false. + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. type: boolean stdinOnce: - description: Whether the container runtime should close - the stdin channel after it has been opened by a single - attach. When stdin is true the stdin stream will remain - open across multiple attach sessions. If stdinOnce - is set to true, stdin is opened on container start, - is empty until the first client attaches to stdin, - and then remains open and accepts data until the client - disconnects, at which time stdin is closed and remains - closed until the container is restarted. If this flag - is false, a container processes that reads from stdin - will never receive an EOF. Default is false + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false type: boolean terminationMessagePath: - description: 'Optional: Path at which the file to which - the container''s termination message will be written - is mounted into the container''s filesystem. Message - written is intended to be brief final status, such - as an assertion failure message. Will be truncated - by the node if greater than 4096 bytes. The total - message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot - be updated.' + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. type: string terminationMessagePolicy: - description: Indicate how the termination message should - be populated. File will use the contents of terminationMessagePath - to populate the container status message on both success - and failure. FallbackToLogsOnError will use the last - chunk of container log output if the termination message - file is empty and the container exited with an error. - The log output is limited to 2048 bytes or 80 lines, - whichever is smaller. Defaults to File. Cannot be - updated. + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. type: string tty: - description: Whether this container should allocate - a TTY for itself, also requires 'stdin' to be true. + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false. type: boolean volumeDevices: @@ -7896,44 +8284,44 @@ spec: type: object type: array volumeMounts: - description: Pod volumes to mount into the container's - filesystem. Cannot be updated. + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. items: description: VolumeMount describes a mounting of a Volume within a container. properties: mountPath: - description: Path within the container at which - the volume should be mounted. Must not contain - ':'. + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. type: string mountPropagation: - description: mountPropagation determines how mounts - are propagated from the host to container and - the other way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. type: string name: description: This must match the Name of a Volume. type: string readOnly: - description: Mounted read-only if true, read-write - otherwise (false or unspecified). Defaults to - false. + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. type: boolean subPath: - description: Path within the volume from which - the container's volume should be mounted. Defaults - to "" (volume's root). + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). type: string subPathExpr: - description: Expanded path within the volume from - which the container's volume should be mounted. - Behaves similarly to SubPath but environment - variable references $(VAR_NAME) are expanded - using the container's environment. Defaults - to "" (volume's root). SubPathExpr and SubPath - are mutually exclusive. + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. type: string required: - mountPath @@ -7941,33 +8329,36 @@ spec: type: object type: array workingDir: - description: Container's working directory. If not specified, - the container runtime's default will be used, which - might be configured in the container image. Cannot - be updated. + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. type: string required: - name type: object type: array dnsConfig: - description: Specifies the DNS parameters of a pod. Parameters - specified here will be merged to the generated DNS configuration - based on DNSPolicy. + description: |- + Specifies the DNS parameters of a pod. + Parameters specified here will be merged to the generated DNS + configuration based on DNSPolicy. properties: nameservers: - description: A list of DNS name server IP addresses. This - will be appended to the base nameservers generated from - DNSPolicy. Duplicated nameservers will be removed. + description: |- + A list of DNS name server IP addresses. + This will be appended to the base nameservers generated from DNSPolicy. + Duplicated nameservers will be removed. items: type: string type: array options: - description: A list of DNS resolver options. This will - be merged with the base options generated from DNSPolicy. - Duplicated entries will be removed. Resolution options - given in Options will override those that appear in - the base DNSPolicy. + description: |- + A list of DNS resolver options. + This will be merged with the base options generated from DNSPolicy. + Duplicated entries will be removed. Resolution options given in Options + will override those that appear in the base DNSPolicy. items: description: PodDNSConfigOption defines DNS resolver options of a pod. @@ -7980,80 +8371,77 @@ spec: type: object type: array searches: - description: A list of DNS search domains for host-name - lookup. This will be appended to the base search paths - generated from DNSPolicy. Duplicated search paths will - be removed. + description: |- + A list of DNS search domains for host-name lookup. + This will be appended to the base search paths generated from DNSPolicy. + Duplicated search paths will be removed. items: type: string type: array type: object dnsPolicy: - description: Set DNS policy for the pod. Defaults to "ClusterFirst". - Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', - 'Default' or 'None'. DNS parameters given in DNSConfig will - be merged with the policy selected with DNSPolicy. To have - DNS options set along with hostNetwork, you have to specify - DNS policy explicitly to 'ClusterFirstWithHostNet'. + description: |- + Set DNS policy for the pod. + Defaults to "ClusterFirst". + Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. + DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. + To have DNS options set along with hostNetwork, you have to specify DNS policy + explicitly to 'ClusterFirstWithHostNet'. type: string enableServiceLinks: - description: 'EnableServiceLinks indicates whether information - about services should be injected into pod''s environment - variables, matching the syntax of Docker links. Optional: - Defaults to true.' + description: |- + EnableServiceLinks indicates whether information about services should be injected into pod's + environment variables, matching the syntax of Docker links. + Optional: Defaults to true. type: boolean ephemeralContainers: - description: List of ephemeral containers run in this pod. - Ephemeral containers may be run in an existing pod to perform - user-initiated actions such as debugging. This list cannot - be specified when creating a pod, and it cannot be modified - by updating the pod spec. In order to add an ephemeral container - to an existing pod, use the pod's ephemeralcontainers subresource. + description: |- + List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing + pod to perform user-initiated actions such as debugging. This list cannot be specified when + creating a pod, and it cannot be modified by updating the pod spec. In order to add an + ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. items: - description: "An EphemeralContainer is a temporary container - that you may add to an existing Pod for user-initiated - activities such as debugging. Ephemeral containers have - no resource or scheduling guarantees, and they will not - be restarted when they exit or when a Pod is removed or - restarted. The kubelet may evict a Pod if an ephemeral - container causes the Pod to exceed its resource allocation. - \n To add an ephemeral container, use the ephemeralcontainers - subresource of an existing Pod. Ephemeral containers may - not be removed or restarted." + description: |- + An EphemeralContainer is a temporary container that you may add to an existing Pod for + user-initiated activities such as debugging. Ephemeral containers have no resource or + scheduling guarantees, and they will not be restarted when they exit or when a Pod is + removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the + Pod to exceed its resource allocation. + + + To add an ephemeral container, use the ephemeralcontainers subresource of an existing + Pod. Ephemeral containers may not be removed or restarted. properties: args: - description: 'Arguments to the entrypoint. The image''s - CMD is used if this is not provided. Variable references - $(VAR_NAME) are expanded using the container''s environment. - If a variable cannot be resolved, the reference in - the input string will be unchanged. Double $$ are - reduced to a single $, which allows for escaping the - $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce - the string literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of whether the - variable exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + description: |- + Arguments to the entrypoint. + The image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell items: type: string type: array command: - description: 'Entrypoint array. Not executed within - a shell. The image''s ENTRYPOINT is used if this is - not provided. Variable references $(VAR_NAME) are - expanded using the container''s environment. If a - variable cannot be resolved, the reference in the - input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) - syntax: i.e. "$$(VAR_NAME)" will produce the string - literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists - or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + description: |- + Entrypoint array. Not executed within a shell. + The image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell items: type: string type: array env: - description: List of environment variables to set in - the container. Cannot be updated. + description: |- + List of environment variables to set in the container. + Cannot be updated. items: description: EnvVar represents an environment variable present in a Container. @@ -8063,17 +8451,16 @@ spec: Must be a C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) - are expanded using the previously defined environment - variables in the container and any service environment - variables. If a variable cannot be resolved, - the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" - will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults - to "".' + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's @@ -8086,10 +8473,10 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap @@ -8100,11 +8487,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: - supports metadata.name, metadata.namespace, - `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the @@ -8120,11 +8505,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory and requests.ephemeral-storage) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required @@ -8156,10 +8539,10 @@ spec: key. type: string name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret @@ -8175,14 +8558,13 @@ spec: type: object type: array envFrom: - description: List of sources to populate environment - variables in the container. The keys defined within - a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is - starting. When a key exists in multiple sources, the - value associated with the last source will take precedence. - Values defined by an Env with a duplicate key will - take precedence. Cannot be updated. + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. items: description: EnvFromSource represents the source of a set of ConfigMaps @@ -8191,10 +8573,10 @@ spec: description: The ConfigMap to select from properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap @@ -8210,10 +8592,10 @@ spec: description: The Secret to select from properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret must @@ -8224,40 +8606,39 @@ spec: type: object type: array image: - description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images' + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images type: string imagePullPolicy: - description: 'Image pull policy. One of Always, Never, - IfNotPresent. Defaults to Always if :latest tag is - specified, or IfNotPresent otherwise. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images type: string lifecycle: description: Lifecycle is not allowed for ephemeral containers. properties: postStart: - description: 'PostStart is called immediately after - a container is created. If the handler fails, - the container is terminated and restarted according - to its restart policy. Other management of the - container blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line - to execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside - a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, - you need to explicitly call out to that - shell. Exit status of 0 is treated as - live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array @@ -8267,8 +8648,8 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. type: string httpHeaders: @@ -8279,10 +8660,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. - This will be canonicalized upon - output, so case-variant names will - be understood as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -8300,24 +8680,36 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object + sleep: + description: Sleep represents the duration that + the container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward - compatibility. There are no validation of - this field and lifecycle hooks will fail in - runtime when tcp handler is specified. + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for the backward compatibility. There are no validation of this field and + lifecycle hooks will fail in runtime when tcp handler is specified. properties: host: description: 'Optional: Host name to connect @@ -8327,44 +8719,37 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object type: object preStop: - description: 'PreStop is called immediately before - a container is terminated due to an API request - or management event such as liveness/startup probe - failure, preemption, resource contention, etc. - The handler is not called if the container crashes - or exits. The Pod''s termination grace period - countdown begins before the PreStop hook is executed. - Regardless of the outcome of the handler, the - container will eventually terminate within the - Pod''s termination grace period (unless delayed - by finalizers). Other management of the container - blocks until the hook completes or until the termination - grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line - to execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside - a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, - you need to explicitly call out to that - shell. Exit status of 0 is treated as - live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array @@ -8374,8 +8759,8 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. type: string httpHeaders: @@ -8386,10 +8771,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. - This will be canonicalized upon - output, so case-variant names will - be understood as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -8407,24 +8791,36 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object + sleep: + description: Sleep represents the duration that + the container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward - compatibility. There are no validation of - this field and lifecycle hooks will fail in - runtime when tcp handler is specified. + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for the backward compatibility. There are no validation of this field and + lifecycle hooks will fail in runtime when tcp handler is specified. properties: host: description: 'Optional: Host name to connect @@ -8434,10 +8830,10 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port @@ -8451,22 +8847,19 @@ spec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') in - the container's filesystem. The command is - simply exec'd, it is not run inside a shell, - so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is - treated as live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array type: object failureThreshold: - description: Minimum consecutive failures for the - probe to be considered failed after having succeeded. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. format: int32 type: integer @@ -8480,11 +8873,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service - to place in the gRPC HealthCheckRequest (see - https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -8494,9 +8888,9 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set "Host" - in httpHeaders instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. @@ -8506,10 +8900,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. This - will be canonicalized upon output, so - case-variant names will be understood - as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -8526,34 +8919,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to - the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the - probe. Default to 10 seconds. Minimum value is - 1. + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the - probe to be considered successful after having - failed. Defaults to 1. Must be 1 for liveness - and startup. Minimum value is 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -8568,43 +8962,40 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully upon probe failure. - The grace period is the duration in seconds after - the processes running in the pod are sent a termination - signal and the time when the processes are forcibly - halted with a kill signal. Set this value longer - than the expected cleanup time for your process. - If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides - the value provided by the pod spec. Value must - be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity - to shut down). This is a beta field and requires - enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object name: - description: Name of the ephemeral container specified - as a DNS_LABEL. This name must be unique among all - containers, init containers and ephemeral containers. + description: |- + Name of the ephemeral container specified as a DNS_LABEL. + This name must be unique among all containers, init containers and ephemeral containers. type: string ports: description: Ports are not allowed for ephemeral containers. @@ -8613,9 +9004,9 @@ spec: in a single container. properties: containerPort: - description: Number of port to expose on the pod's - IP address. This must be a valid port number, - 0 < x < 65536. + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. format: int32 type: integer hostIP: @@ -8623,23 +9014,24 @@ spec: port to. type: string hostPort: - description: Number of port to expose on the host. - If specified, this must be a valid port number, - 0 < x < 65536. If HostNetwork is specified, - this must match ContainerPort. Most containers - do not need this. + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. format: int32 type: integer name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port in - a pod must have a unique name. Name for the - port that can be referred to by services. + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. type: string protocol: default: TCP - description: Protocol for port. Must be UDP, TCP, - or SCTP. Defaults to "TCP". + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". type: string required: - containerPort @@ -8656,22 +9048,19 @@ spec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') in - the container's filesystem. The command is - simply exec'd, it is not run inside a shell, - so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is - treated as live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array type: object failureThreshold: - description: Minimum consecutive failures for the - probe to be considered failed after having succeeded. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. format: int32 type: integer @@ -8685,11 +9074,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service - to place in the gRPC HealthCheckRequest (see - https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -8699,9 +9089,9 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set "Host" - in httpHeaders instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. @@ -8711,10 +9101,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. This - will be canonicalized upon output, so - case-variant names will be understood - as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -8731,34 +9120,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to - the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the - probe. Default to 10 seconds. Minimum value is - 1. + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the - probe to be considered successful after having - failed. Defaults to 1. Must be 1 for liveness - and startup. Minimum value is 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -8773,36 +9163,33 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully upon probe failure. - The grace period is the duration in seconds after - the processes running in the pod are sent a termination - signal and the time when the processes are forcibly - halted with a kill signal. Set this value longer - than the expected cleanup time for your process. - If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides - the value provided by the pod spec. Value must - be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity - to shut down). This is a beta field and requires - enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object @@ -8813,14 +9200,14 @@ spec: resize policy for the container. properties: resourceName: - description: 'Name of the resource to which this - resource resize policy applies. Supported values: - cpu, memory.' + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. type: string restartPolicy: - description: Restart policy to apply when specified - resource is resized. If not specified, it defaults - to NotRequired. + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. type: string required: - resourceName @@ -8829,26 +9216,30 @@ spec: type: array x-kubernetes-list-type: atomic resources: - description: Resources are not allowed for ephemeral - containers. Ephemeral containers use spare resources + description: |- + Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod. properties: claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used - by this container. \n This is an alpha field and - requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can - only be set for containers." + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one - entry in pod.spec.resourceClaims of the - Pod where this field is used. It makes that - resource available inside a container. + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. type: string required: - name @@ -8864,8 +9255,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -8874,41 +9266,40 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is - omitted for a container, it defaults to Limits - if that is explicitly specified, otherwise to - an implementation-defined value. Requests cannot - exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object restartPolicy: - description: Restart policy for the container to manage - the restart behavior of each container within a pod. - This may only be set for init containers. You cannot - set this field on ephemeral containers. + description: |- + Restart policy for the container to manage the restart behavior of each + container within a pod. + This may only be set for init containers. You cannot set this field on + ephemeral containers. type: string securityContext: - description: 'Optional: SecurityContext defines the - security options the ephemeral container should be - run with. If set, the fields of SecurityContext override - the equivalent fields of PodSecurityContext.' + description: |- + Optional: SecurityContext defines the security options the ephemeral container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. properties: allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls - whether a process can gain more privileges than - its parent process. This bool directly controls - if the no_new_privs flag will be set on the container - process. AllowPrivilegeEscalation is true always - when the container is: 1) run as Privileged 2) - has CAP_SYS_ADMIN Note that this field cannot - be set when spec.os.name is windows.' + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. type: boolean capabilities: - description: The capabilities to add/drop when running - containers. Defaults to the default set of capabilities - granted by the container runtime. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. properties: add: description: Added capabilities @@ -8926,66 +9317,60 @@ spec: type: array type: object privileged: - description: Run container in privileged mode. Processes - in privileged containers are essentially equivalent - to root on the host. Defaults to false. Note that - this field cannot be set when spec.os.name is - windows. + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. type: boolean procMount: - description: procMount denotes the type of proc - mount to use for the containers. The default is - DefaultProcMount which uses the container runtime - defaults for readonly paths and masked paths. - This requires the ProcMountType feature flag to - be enabled. Note that this field cannot be set - when spec.os.name is windows. + description: |- + procMount denotes the type of proc mount to use for the containers. + The default is DefaultProcMount which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. type: string readOnlyRootFilesystem: - description: Whether this container has a read-only - root filesystem. Default is false. Note that this - field cannot be set when spec.os.name is windows. + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. type: boolean runAsGroup: - description: The GID to run the entrypoint of the - container process. Uses runtime default if unset. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is windows. + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer runAsNonRoot: - description: Indicates that the container must run - as a non-root user. If true, the Kubelet will - validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start - the container if it does. If unset or false, no - such validation will be performed. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in - SecurityContext takes precedence. + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: boolean runAsUser: - description: The UID to run the entrypoint of the - container process. Defaults to user specified - in image metadata if unspecified. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in - SecurityContext takes precedence. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer seLinuxOptions: - description: The SELinux context to be applied to - the container. If unspecified, the container runtime - will allocate a random SELinux context for each - container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is windows. + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. properties: level: description: Level is SELinux level label that @@ -9005,70 +9390,62 @@ spec: type: string type: object seccompProfile: - description: The seccomp options to use by this - container. If seccomp options are provided at - both the pod & container level, the container - options override the pod options. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. properties: localhostProfile: - description: localhostProfile indicates a profile - defined in a file on the node should be used. - The profile must be preconfigured on the node - to work. Must be a descending path, relative - to the kubelet's configured seccomp profile - location. Must be set if type is "Localhost". - Must NOT be set for any other type. + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. type: string type: - description: "type indicates which kind of seccomp - profile will be applied. Valid options are: - \n Localhost - a profile defined in a file - on the node should be used. RuntimeDefault - - the container runtime default profile should - be used. Unconfined - no profile should be - applied." + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. type: string required: - type type: object windowsOptions: - description: The Windows specific settings applied - to all containers. If unspecified, the options - from the PodSecurityContext will be used. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is linux. + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. properties: gmsaCredentialSpec: - description: GMSACredentialSpec is where the - GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential - spec named by the GMSACredentialSpecName field. + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. type: string gmsaCredentialSpecName: description: GMSACredentialSpecName is the name of the GMSA credential spec to use. type: string hostProcess: - description: HostProcess determines if a container - should be run as a 'Host Process' container. - All of a Pod's containers must have the same - effective HostProcess value (it is not allowed - to have a mix of HostProcess containers and - non-HostProcess containers). In addition, - if HostProcess is true then HostNetwork must - also be set to true. + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean runAsUserName: - description: The UserName in Windows to run - the entrypoint of the container process. Defaults - to the user specified in image metadata if - unspecified. May also be set in PodSecurityContext. - If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: string type: object type: object @@ -9079,22 +9456,19 @@ spec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') in - the container's filesystem. The command is - simply exec'd, it is not run inside a shell, - so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is - treated as live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array type: object failureThreshold: - description: Minimum consecutive failures for the - probe to be considered failed after having succeeded. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. format: int32 type: integer @@ -9108,11 +9482,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service - to place in the gRPC HealthCheckRequest (see - https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -9122,9 +9497,9 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set "Host" - in httpHeaders instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. @@ -9134,10 +9509,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. This - will be canonicalized upon output, so - case-variant names will be understood - as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -9154,34 +9528,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to - the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the - probe. Default to 10 seconds. Minimum value is - 1. + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the - probe to be considered successful after having - failed. Defaults to 1. Must be 1 for liveness - and startup. Minimum value is 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -9196,94 +9571,85 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully upon probe failure. - The grace period is the duration in seconds after - the processes running in the pod are sent a termination - signal and the time when the processes are forcibly - halted with a kill signal. Set this value longer - than the expected cleanup time for your process. - If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides - the value provided by the pod spec. Value must - be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity - to shut down). This is a beta field and requires - enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object stdin: - description: Whether this container should allocate - a buffer for stdin in the container runtime. If this - is not set, reads from stdin in the container will - always result in EOF. Default is false. + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. type: boolean stdinOnce: - description: Whether the container runtime should close - the stdin channel after it has been opened by a single - attach. When stdin is true the stdin stream will remain - open across multiple attach sessions. If stdinOnce - is set to true, stdin is opened on container start, - is empty until the first client attaches to stdin, - and then remains open and accepts data until the client - disconnects, at which time stdin is closed and remains - closed until the container is restarted. If this flag - is false, a container processes that reads from stdin - will never receive an EOF. Default is false + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false type: boolean targetContainerName: - description: "If set, the name of the container from - PodSpec that this ephemeral container targets. The - ephemeral container will be run in the namespaces - (IPC, PID, etc) of this container. If not set then - the ephemeral container uses the namespaces configured - in the Pod spec. \n The container runtime must implement - support for this feature. If the runtime does not - support namespace targeting then the result of setting - this field is undefined." + description: |- + If set, the name of the container from PodSpec that this ephemeral container targets. + The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. + If not set then the ephemeral container uses the namespaces configured in the Pod spec. + + + The container runtime must implement support for this feature. If the runtime does not + support namespace targeting then the result of setting this field is undefined. type: string terminationMessagePath: - description: 'Optional: Path at which the file to which - the container''s termination message will be written - is mounted into the container''s filesystem. Message - written is intended to be brief final status, such - as an assertion failure message. Will be truncated - by the node if greater than 4096 bytes. The total - message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot - be updated.' + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. type: string terminationMessagePolicy: - description: Indicate how the termination message should - be populated. File will use the contents of terminationMessagePath - to populate the container status message on both success - and failure. FallbackToLogsOnError will use the last - chunk of container log output if the termination message - file is empty and the container exited with an error. - The log output is limited to 2048 bytes or 80 lines, - whichever is smaller. Defaults to File. Cannot be - updated. + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. type: string tty: - description: Whether this container should allocate - a TTY for itself, also requires 'stdin' to be true. + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false. type: boolean volumeDevices: @@ -9308,45 +9674,44 @@ spec: type: object type: array volumeMounts: - description: Pod volumes to mount into the container's - filesystem. Subpath mounts are not allowed for ephemeral - containers. Cannot be updated. + description: |- + Pod volumes to mount into the container's filesystem. Subpath mounts are not allowed for ephemeral containers. + Cannot be updated. items: description: VolumeMount describes a mounting of a Volume within a container. properties: mountPath: - description: Path within the container at which - the volume should be mounted. Must not contain - ':'. + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. type: string mountPropagation: - description: mountPropagation determines how mounts - are propagated from the host to container and - the other way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. type: string name: description: This must match the Name of a Volume. type: string readOnly: - description: Mounted read-only if true, read-write - otherwise (false or unspecified). Defaults to - false. + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. type: boolean subPath: - description: Path within the volume from which - the container's volume should be mounted. Defaults - to "" (volume's root). + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). type: string subPathExpr: - description: Expanded path within the volume from - which the container's volume should be mounted. - Behaves similarly to SubPath but environment - variable references $(VAR_NAME) are expanded - using the container's environment. Defaults - to "" (volume's root). SubPathExpr and SubPath - are mutually exclusive. + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. type: string required: - mountPath @@ -9354,23 +9719,24 @@ spec: type: object type: array workingDir: - description: Container's working directory. If not specified, - the container runtime's default will be used, which - might be configured in the container image. Cannot - be updated. + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. type: string required: - name type: object type: array hostAliases: - description: HostAliases is an optional list of hosts and - IPs that will be injected into the pod's hosts file if specified. - This is only valid for non-hostNetwork pods. + description: |- + HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts + file if specified. This is only valid for non-hostNetwork pods. items: - description: HostAlias holds the mapping between IP and - hostnames that will be injected as an entry in the pod's - hosts file. + description: |- + HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the + pod's hosts file. properties: hostnames: description: Hostnames for the above IP address. @@ -9383,105 +9749,106 @@ spec: type: object type: array hostIPC: - description: 'Use the host''s ipc namespace. Optional: Default - to false.' + description: |- + Use the host's ipc namespace. + Optional: Default to false. type: boolean hostNetwork: - description: Host networking requested for this pod. Use the - host's network namespace. If this option is set, the ports - that will be used must be specified. Default to false. + description: |- + Host networking requested for this pod. Use the host's network namespace. + If this option is set, the ports that will be used must be specified. + Default to false. type: boolean hostPID: - description: 'Use the host''s pid namespace. Optional: Default - to false.' + description: |- + Use the host's pid namespace. + Optional: Default to false. type: boolean hostUsers: - description: 'Use the host''s user namespace. Optional: Default - to true. If set to true or not present, the pod will be - run in the host user namespace, useful for when the pod - needs a feature only available to the host user namespace, - such as loading a kernel module with CAP_SYS_MODULE. When - set to false, a new userns is created for the pod. Setting - false is useful for mitigating container breakout vulnerabilities - even allowing users to run their containers as root without - actually having root privileges on the host. This field - is alpha-level and is only honored by servers that enable - the UserNamespacesSupport feature.' + description: |- + Use the host's user namespace. + Optional: Default to true. + If set to true or not present, the pod will be run in the host user namespace, useful + for when the pod needs a feature only available to the host user namespace, such as + loading a kernel module with CAP_SYS_MODULE. + When set to false, a new userns is created for the pod. Setting false is useful for + mitigating container breakout vulnerabilities even allowing users to run their + containers as root without actually having root privileges on the host. + This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature. type: boolean hostname: - description: Specifies the hostname of the Pod If not specified, - the pod's hostname will be set to a system-defined value. + description: |- + Specifies the hostname of the Pod + If not specified, the pod's hostname will be set to a system-defined value. type: string imagePullSecrets: - description: 'ImagePullSecrets is an optional list of references - to secrets in the same namespace to use for pulling any - of the images used by this PodSpec. If specified, these - secrets will be passed to individual puller implementations - for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod items: - description: LocalObjectReference contains enough information - to let you locate the referenced object inside the same - namespace. + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic type: array initContainers: - description: 'List of initialization containers belonging - to the pod. Init containers are executed in order prior - to containers being started. If any init container fails, - the pod is considered to have failed and is handled according - to its restartPolicy. The name for an init container or - normal container must be unique among all containers. Init - containers may not have Lifecycle actions, Readiness probes, - Liveness probes, or Startup probes. The resourceRequirements - of an init container are taken into account during scheduling - by finding the highest request/limit for each resource type, - and then using the max of of that value or the sum of the - normal containers. Limits are applied to init containers - in a similar fashion. Init containers cannot currently be - added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' + description: |- + List of initialization containers belonging to the pod. + Init containers are executed in order prior to containers being started. If any + init container fails, the pod is considered to have failed and is handled according + to its restartPolicy. The name for an init container or normal container must be + unique among all containers. + Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. + The resourceRequirements of an init container are taken into account during scheduling + by finding the highest request/limit for each resource type, and then using the max of + of that value or the sum of the normal containers. Limits are applied to init containers + in a similar fashion. + Init containers cannot currently be added or removed. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ items: description: A single application container that you want to run within a pod. properties: args: - description: 'Arguments to the entrypoint. The container - image''s CMD is used if this is not provided. Variable - references $(VAR_NAME) are expanded using the container''s - environment. If a variable cannot be resolved, the - reference in the input string will be unchanged. Double - $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce - the string literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of whether the - variable exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell items: type: string type: array command: - description: 'Entrypoint array. Not executed within - a shell. The container image''s ENTRYPOINT is used - if this is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. If - a variable cannot be resolved, the reference in the - input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) - syntax: i.e. "$$(VAR_NAME)" will produce the string - literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists - or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell items: type: string type: array env: - description: List of environment variables to set in - the container. Cannot be updated. + description: |- + List of environment variables to set in the container. + Cannot be updated. items: description: EnvVar represents an environment variable present in a Container. @@ -9491,17 +9858,16 @@ spec: Must be a C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) - are expanded using the previously defined environment - variables in the container and any service environment - variables. If a variable cannot be resolved, - the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" - will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults - to "".' + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's @@ -9514,10 +9880,10 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap @@ -9528,11 +9894,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: - supports metadata.name, metadata.namespace, - `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the @@ -9548,11 +9912,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory and requests.ephemeral-storage) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required @@ -9584,10 +9946,10 @@ spec: key. type: string name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret @@ -9603,14 +9965,13 @@ spec: type: object type: array envFrom: - description: List of sources to populate environment - variables in the container. The keys defined within - a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is - starting. When a key exists in multiple sources, the - value associated with the last source will take precedence. - Values defined by an Env with a duplicate key will - take precedence. Cannot be updated. + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. items: description: EnvFromSource represents the source of a set of ConfigMaps @@ -9619,10 +9980,10 @@ spec: description: The ConfigMap to select from properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap @@ -9638,10 +9999,10 @@ spec: description: The Secret to select from properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret must @@ -9652,44 +10013,42 @@ spec: type: object type: array image: - description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level config - management to default or override container images - in workload controllers like Deployments and StatefulSets.' + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. type: string imagePullPolicy: - description: 'Image pull policy. One of Always, Never, - IfNotPresent. Defaults to Always if :latest tag is - specified, or IfNotPresent otherwise. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images type: string lifecycle: - description: Actions that the management system should - take in response to container lifecycle events. Cannot - be updated. + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. properties: postStart: - description: 'PostStart is called immediately after - a container is created. If the handler fails, - the container is terminated and restarted according - to its restart policy. Other management of the - container blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line - to execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside - a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, - you need to explicitly call out to that - shell. Exit status of 0 is treated as - live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array @@ -9699,8 +10058,8 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. type: string httpHeaders: @@ -9711,10 +10070,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. - This will be canonicalized upon - output, so case-variant names will - be understood as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -9732,24 +10090,36 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object + sleep: + description: Sleep represents the duration that + the container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward - compatibility. There are no validation of - this field and lifecycle hooks will fail in - runtime when tcp handler is specified. + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for the backward compatibility. There are no validation of this field and + lifecycle hooks will fail in runtime when tcp handler is specified. properties: host: description: 'Optional: Host name to connect @@ -9759,44 +10129,37 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object type: object preStop: - description: 'PreStop is called immediately before - a container is terminated due to an API request - or management event such as liveness/startup probe - failure, preemption, resource contention, etc. - The handler is not called if the container crashes - or exits. The Pod''s termination grace period - countdown begins before the PreStop hook is executed. - Regardless of the outcome of the handler, the - container will eventually terminate within the - Pod''s termination grace period (unless delayed - by finalizers). Other management of the container - blocks until the hook completes or until the termination - grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line - to execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside - a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, - you need to explicitly call out to that - shell. Exit status of 0 is treated as - live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array @@ -9806,8 +10169,8 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead. type: string httpHeaders: @@ -9818,10 +10181,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. - This will be canonicalized upon - output, so case-variant names will - be understood as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -9839,24 +10201,36 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object + sleep: + description: Sleep represents the duration that + the container should sleep before being terminated. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward - compatibility. There are no validation of - this field and lifecycle hooks will fail in - runtime when tcp handler is specified. + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for the backward compatibility. There are no validation of this field and + lifecycle hooks will fail in runtime when tcp handler is specified. properties: host: description: 'Optional: Host name to connect @@ -9866,10 +10240,10 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port @@ -9877,30 +10251,29 @@ spec: type: object type: object livenessProbe: - description: 'Periodic probe of container liveness. - Container will be restarted if the probe fails. Cannot - be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') in - the container's filesystem. The command is - simply exec'd, it is not run inside a shell, - so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is - treated as live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array type: object failureThreshold: - description: Minimum consecutive failures for the - probe to be considered failed after having succeeded. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. format: int32 type: integer @@ -9914,11 +10287,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service - to place in the gRPC HealthCheckRequest (see - https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -9928,9 +10302,9 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set "Host" - in httpHeaders instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. @@ -9940,10 +10314,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. This - will be canonicalized upon output, so - case-variant names will be understood - as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -9960,34 +10333,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to - the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the - probe. Default to 10 seconds. Minimum value is - 1. + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the - probe to be considered successful after having - failed. Defaults to 1. Must be 1 for liveness - and startup. Minimum value is 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -10002,61 +10376,59 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully upon probe failure. - The grace period is the duration in seconds after - the processes running in the pod are sent a termination - signal and the time when the processes are forcibly - halted with a kill signal. Set this value longer - than the expected cleanup time for your process. - If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides - the value provided by the pod spec. Value must - be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity - to shut down). This is a beta field and requires - enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object name: - description: Name of the container specified as a DNS_LABEL. + description: |- + Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated. type: string ports: - description: List of ports to expose from the container. - Not specifying a port here DOES NOT prevent that port - from being exposed. Any port which is listening on - the default "0.0.0.0" address inside a container will - be accessible from the network. Modifying this array - with strategic merge patch may corrupt the data. For - more information See https://github.com/kubernetes/kubernetes/issues/108255. + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated. items: description: ContainerPort represents a network port in a single container. properties: containerPort: - description: Number of port to expose on the pod's - IP address. This must be a valid port number, - 0 < x < 65536. + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. format: int32 type: integer hostIP: @@ -10064,23 +10436,24 @@ spec: port to. type: string hostPort: - description: Number of port to expose on the host. - If specified, this must be a valid port number, - 0 < x < 65536. If HostNetwork is specified, - this must match ContainerPort. Most containers - do not need this. + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. format: int32 type: integer name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port in - a pod must have a unique name. Name for the - port that can be referred to by services. + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. type: string protocol: default: TCP - description: Protocol for port. Must be UDP, TCP, - or SCTP. Defaults to "TCP". + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". type: string required: - containerPort @@ -10091,30 +10464,29 @@ spec: - protocol x-kubernetes-list-type: map readinessProbe: - description: 'Periodic probe of container service readiness. - Container will be removed from service endpoints if - the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') in - the container's filesystem. The command is - simply exec'd, it is not run inside a shell, - so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is - treated as live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array type: object failureThreshold: - description: Minimum consecutive failures for the - probe to be considered failed after having succeeded. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. format: int32 type: integer @@ -10128,11 +10500,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service - to place in the gRPC HealthCheckRequest (see - https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -10142,9 +10515,9 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set "Host" - in httpHeaders instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. @@ -10154,10 +10527,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. This - will be canonicalized upon output, so - case-variant names will be understood - as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -10174,34 +10546,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to - the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the - probe. Default to 10 seconds. Minimum value is - 1. + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the - probe to be considered successful after having - failed. Defaults to 1. Must be 1 for liveness - and startup. Minimum value is 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -10216,36 +10589,33 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully upon probe failure. - The grace period is the duration in seconds after - the processes running in the pod are sent a termination - signal and the time when the processes are forcibly - halted with a kill signal. Set this value longer - than the expected cleanup time for your process. - If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides - the value provided by the pod spec. Value must - be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity - to shut down). This is a beta field and requires - enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object @@ -10256,14 +10626,14 @@ spec: resize policy for the container. properties: resourceName: - description: 'Name of the resource to which this - resource resize policy applies. Supported values: - cpu, memory.' + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. type: string restartPolicy: - description: Restart policy to apply when specified - resource is resized. If not specified, it defaults - to NotRequired. + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. type: string required: - resourceName @@ -10272,25 +10642,31 @@ spec: type: array x-kubernetes-list-type: atomic resources: - description: 'Compute Resources required by this container. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ properties: claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used - by this container. \n This is an alpha field and - requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can - only be set for containers." + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: name: - description: Name must match the name of one - entry in pod.spec.resourceClaims of the - Pod where this field is used. It makes that - resource available inside a container. + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. type: string required: - name @@ -10306,8 +10682,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -10316,57 +10693,52 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is - omitted for a container, it defaults to Limits - if that is explicitly specified, otherwise to - an implementation-defined value. Requests cannot - exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object restartPolicy: - description: 'RestartPolicy defines the restart behavior - of individual containers in a pod. This field may - only be set for init containers, and the only allowed - value is "Always". For non-init containers or when - this field is not specified, the restart behavior - is defined by the Pod''s restart policy and the container - type. Setting the RestartPolicy as "Always" for the - init container will have the following effect: this - init container will be continually restarted on exit - until all regular containers have terminated. Once - all regular containers have completed, all init containers - with restartPolicy "Always" will be shut down. This - lifecycle differs from normal init containers and - is often referred to as a "sidecar" container. Although - this init container still starts in the init container - sequence, it does not wait for the container to complete - before proceeding to the next init container. Instead, - the next init container starts immediately after this - init container is started, or after any startupProbe - has successfully completed.' + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This field may only be set for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Setting the RestartPolicy as "Always" for the init container will have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. type: string securityContext: - description: 'SecurityContext defines the security options - the container should be run with. If set, the fields - of SecurityContext override the equivalent fields - of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ properties: allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls - whether a process can gain more privileges than - its parent process. This bool directly controls - if the no_new_privs flag will be set on the container - process. AllowPrivilegeEscalation is true always - when the container is: 1) run as Privileged 2) - has CAP_SYS_ADMIN Note that this field cannot - be set when spec.os.name is windows.' + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. type: boolean capabilities: - description: The capabilities to add/drop when running - containers. Defaults to the default set of capabilities - granted by the container runtime. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. properties: add: description: Added capabilities @@ -10384,66 +10756,60 @@ spec: type: array type: object privileged: - description: Run container in privileged mode. Processes - in privileged containers are essentially equivalent - to root on the host. Defaults to false. Note that - this field cannot be set when spec.os.name is - windows. + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. type: boolean procMount: - description: procMount denotes the type of proc - mount to use for the containers. The default is - DefaultProcMount which uses the container runtime - defaults for readonly paths and masked paths. - This requires the ProcMountType feature flag to - be enabled. Note that this field cannot be set - when spec.os.name is windows. + description: |- + procMount denotes the type of proc mount to use for the containers. + The default is DefaultProcMount which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. type: string readOnlyRootFilesystem: - description: Whether this container has a read-only - root filesystem. Default is false. Note that this - field cannot be set when spec.os.name is windows. + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. type: boolean runAsGroup: - description: The GID to run the entrypoint of the - container process. Uses runtime default if unset. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is windows. + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer runAsNonRoot: - description: Indicates that the container must run - as a non-root user. If true, the Kubelet will - validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start - the container if it does. If unset or false, no - such validation will be performed. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in - SecurityContext takes precedence. + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: boolean runAsUser: - description: The UID to run the entrypoint of the - container process. Defaults to user specified - in image metadata if unspecified. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in - SecurityContext takes precedence. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer seLinuxOptions: - description: The SELinux context to be applied to - the container. If unspecified, the container runtime - will allocate a random SELinux context for each - container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is windows. + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. properties: level: description: Level is SELinux level label that @@ -10463,104 +10829,92 @@ spec: type: string type: object seccompProfile: - description: The seccomp options to use by this - container. If seccomp options are provided at - both the pod & container level, the container - options override the pod options. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. properties: localhostProfile: - description: localhostProfile indicates a profile - defined in a file on the node should be used. - The profile must be preconfigured on the node - to work. Must be a descending path, relative - to the kubelet's configured seccomp profile - location. Must be set if type is "Localhost". - Must NOT be set for any other type. + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. type: string type: - description: "type indicates which kind of seccomp - profile will be applied. Valid options are: - \n Localhost - a profile defined in a file - on the node should be used. RuntimeDefault - - the container runtime default profile should - be used. Unconfined - no profile should be - applied." + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. type: string required: - type type: object windowsOptions: - description: The Windows specific settings applied - to all containers. If unspecified, the options - from the PodSecurityContext will be used. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is linux. + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. properties: gmsaCredentialSpec: - description: GMSACredentialSpec is where the - GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential - spec named by the GMSACredentialSpecName field. + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. type: string gmsaCredentialSpecName: description: GMSACredentialSpecName is the name of the GMSA credential spec to use. type: string hostProcess: - description: HostProcess determines if a container - should be run as a 'Host Process' container. - All of a Pod's containers must have the same - effective HostProcess value (it is not allowed - to have a mix of HostProcess containers and - non-HostProcess containers). In addition, - if HostProcess is true then HostNetwork must - also be set to true. + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean runAsUserName: - description: The UserName in Windows to run - the entrypoint of the container process. Defaults - to the user specified in image metadata if - unspecified. May also be set in PodSecurityContext. - If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: string type: object type: object startupProbe: - description: 'StartupProbe indicates that the Pod has - successfully initialized. If specified, no other probes - are executed until this completes successfully. If - this probe fails, the Pod will be restarted, just - as if the livenessProbe failed. This can be used to - provide different probe parameters at the beginning - of a Pod''s lifecycle, when it might take a long time - to load data or warm a cache, than during steady-state - operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes properties: exec: description: Exec specifies the action to take. properties: command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') in - the container's filesystem. The command is - simply exec'd, it is not run inside a shell, - so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is - treated as live/healthy and non-zero is unhealthy. + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. items: type: string type: array type: object failureThreshold: - description: Minimum consecutive failures for the - probe to be considered failed after having succeeded. + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. format: int32 type: integer @@ -10574,11 +10928,12 @@ spec: format: int32 type: integer service: - description: "Service is the name of the service - to place in the gRPC HealthCheckRequest (see - https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + + If this is not specified, the default behavior is defined by gRPC. type: string required: - port @@ -10588,9 +10943,9 @@ spec: to perform. properties: host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set "Host" - in httpHeaders instead. + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. type: string httpHeaders: description: Custom headers to set in the request. @@ -10600,10 +10955,9 @@ spec: header to be used in HTTP probes properties: name: - description: The header field name. This - will be canonicalized upon output, so - case-variant names will be understood - as the same header. + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. type: string value: description: The header field value @@ -10620,34 +10974,35 @@ spec: anyOf: - type: integer - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true scheme: - description: Scheme to use for connecting to - the host. Defaults to HTTP. + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - port type: object initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer periodSeconds: - description: How often (in seconds) to perform the - probe. Default to 10 seconds. Minimum value is - 1. + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. format: int32 type: integer successThreshold: - description: Minimum consecutive successes for the - probe to be considered successful after having - failed. Defaults to 1. Must be 1 for liveness - and startup. Minimum value is 1. + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. format: int32 type: integer tcpSocket: @@ -10662,83 +11017,75 @@ spec: anyOf: - type: integer - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true required: - port type: object terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully upon probe failure. - The grace period is the duration in seconds after - the processes running in the pod are sent a termination - signal and the time when the processes are forcibly - halted with a kill signal. Set this value longer - than the expected cleanup time for your process. - If this value is nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value overrides - the value provided by the pod spec. Value must - be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity - to shut down). This is a beta field and requires - enabling ProbeTerminationGracePeriod feature gate. - Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. format: int64 type: integer timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes format: int32 type: integer type: object stdin: - description: Whether this container should allocate - a buffer for stdin in the container runtime. If this - is not set, reads from stdin in the container will - always result in EOF. Default is false. + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. type: boolean stdinOnce: - description: Whether the container runtime should close - the stdin channel after it has been opened by a single - attach. When stdin is true the stdin stream will remain - open across multiple attach sessions. If stdinOnce - is set to true, stdin is opened on container start, - is empty until the first client attaches to stdin, - and then remains open and accepts data until the client - disconnects, at which time stdin is closed and remains - closed until the container is restarted. If this flag - is false, a container processes that reads from stdin - will never receive an EOF. Default is false + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false type: boolean terminationMessagePath: - description: 'Optional: Path at which the file to which - the container''s termination message will be written - is mounted into the container''s filesystem. Message - written is intended to be brief final status, such - as an assertion failure message. Will be truncated - by the node if greater than 4096 bytes. The total - message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot - be updated.' + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. type: string terminationMessagePolicy: - description: Indicate how the termination message should - be populated. File will use the contents of terminationMessagePath - to populate the container status message on both success - and failure. FallbackToLogsOnError will use the last - chunk of container log output if the termination message - file is empty and the container exited with an error. - The log output is limited to 2048 bytes or 80 lines, - whichever is smaller. Defaults to File. Cannot be - updated. + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. type: string tty: - description: Whether this container should allocate - a TTY for itself, also requires 'stdin' to be true. + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false. type: boolean volumeDevices: @@ -10763,44 +11110,44 @@ spec: type: object type: array volumeMounts: - description: Pod volumes to mount into the container's - filesystem. Cannot be updated. + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. items: description: VolumeMount describes a mounting of a Volume within a container. properties: mountPath: - description: Path within the container at which - the volume should be mounted. Must not contain - ':'. + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. type: string mountPropagation: - description: mountPropagation determines how mounts - are propagated from the host to container and - the other way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. type: string name: description: This must match the Name of a Volume. type: string readOnly: - description: Mounted read-only if true, read-write - otherwise (false or unspecified). Defaults to - false. + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. type: boolean subPath: - description: Path within the volume from which - the container's volume should be mounted. Defaults - to "" (volume's root). + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). type: string subPathExpr: - description: Expanded path within the volume from - which the container's volume should be mounted. - Behaves similarly to SubPath but environment - variable references $(VAR_NAME) are expanded - using the container's environment. Defaults - to "" (volume's root). SubPathExpr and SubPath - are mutually exclusive. + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. type: string required: - mountPath @@ -10808,54 +11155,70 @@ spec: type: object type: array workingDir: - description: Container's working directory. If not specified, - the container runtime's default will be used, which - might be configured in the container image. Cannot - be updated. + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. type: string required: - name type: object type: array nodeName: - description: NodeName is a request to schedule this pod onto - a specific node. If it is non-empty, the scheduler simply - schedules this pod onto that node, assuming that it fits - resource requirements. + description: |- + NodeName is a request to schedule this pod onto a specific node. If it is non-empty, + the scheduler simply schedules this pod onto that node, assuming that it fits resource + requirements. type: string nodeSelector: additionalProperties: type: string - description: 'NodeSelector is a selector which must be true - for the pod to fit on a node. Selector which must match - a node''s labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + description: |- + NodeSelector is a selector which must be true for the pod to fit on a node. + Selector which must match a node's labels for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ type: object x-kubernetes-map-type: atomic os: - description: "Specifies the OS of the containers in the pod. - Some pod and container fields are restricted if this is - set. \n If the OS field is set to linux, the following fields - must be unset: -securityContext.windowsOptions \n If the - OS field is set to windows, following fields must be unset: - - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.seLinuxOptions - - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - - spec.shareProcessNamespace - spec.securityContext.runAsUser - - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - - spec.containers[*].securityContext.runAsGroup" + description: |- + Specifies the OS of the containers in the pod. + Some pod and container fields are restricted if this is set. + + + If the OS field is set to linux, the following fields must be unset: + -securityContext.windowsOptions + + + If the OS field is set to windows, following fields must be unset: + - spec.hostPID + - spec.hostIPC + - spec.hostUsers + - spec.securityContext.seLinuxOptions + - spec.securityContext.seccompProfile + - spec.securityContext.fsGroup + - spec.securityContext.fsGroupChangePolicy + - spec.securityContext.sysctls + - spec.shareProcessNamespace + - spec.securityContext.runAsUser + - spec.securityContext.runAsGroup + - spec.securityContext.supplementalGroups + - spec.containers[*].securityContext.seLinuxOptions + - spec.containers[*].securityContext.seccompProfile + - spec.containers[*].securityContext.capabilities + - spec.containers[*].securityContext.readOnlyRootFilesystem + - spec.containers[*].securityContext.privileged + - spec.containers[*].securityContext.allowPrivilegeEscalation + - spec.containers[*].securityContext.procMount + - spec.containers[*].securityContext.runAsUser + - spec.containers[*].securityContext.runAsGroup properties: name: - description: 'Name is the name of the operating system. - The currently supported values are linux and windows. - Additional value may be defined in future and can be - one of: https://github.com/opencontainers/runtime-spec/blob/master/config.md#platform-specific-configuration - Clients should expect to handle additional values and - treat unrecognized values in this field as os: null' + description: |- + Name is the name of the operating system. The currently supported values are linux and windows. + Additional value may be defined in future and can be one of: + https://github.com/opencontainers/runtime-spec/blob/master/config.md#platform-specific-configuration + Clients should expect to handle additional values and treat unrecognized values in this field as os: null type: string required: - name @@ -10867,46 +11230,45 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Overhead represents the resource overhead associated - with running a pod for a given RuntimeClass. This field - will be autopopulated at admission time by the RuntimeClass - admission controller. If the RuntimeClass admission controller - is enabled, overhead must not be set in Pod create requests. - The RuntimeClass admission controller will reject Pod create - requests which have the overhead already set. If RuntimeClass - is configured and selected in the PodSpec, Overhead will - be set to the value defined in the corresponding RuntimeClass, - otherwise it will remain unset and treated as zero. More - info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md' + description: |- + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. + This field will be autopopulated at admission time by the RuntimeClass admission controller. If + the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. + The RuntimeClass admission controller will reject Pod create requests which have the overhead already + set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value + defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. + More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md type: object preemptionPolicy: - description: PreemptionPolicy is the Policy for preempting - pods with lower priority. One of Never, PreemptLowerPriority. + description: |- + PreemptionPolicy is the Policy for preempting pods with lower priority. + One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. type: string priority: - description: The priority value. Various system components - use this field to find the priority of the pod. When Priority - Admission Controller is enabled, it prevents users from - setting this field. The admission controller populates this - field from PriorityClassName. The higher the value, the - higher the priority. + description: |- + The priority value. Various system components use this field to find the + priority of the pod. When Priority Admission Controller is enabled, it + prevents users from setting this field. The admission controller populates + this field from PriorityClassName. + The higher the value, the higher the priority. format: int32 type: integer priorityClassName: - description: If specified, indicates the pod's priority. "system-node-critical" - and "system-cluster-critical" are two special keywords which - indicate the highest priorities with the former being the - highest priority. Any other name must be defined by creating - a PriorityClass object with that name. If not specified, - the pod priority will be default or zero if there is no + description: |- + If specified, indicates the pod's priority. "system-node-critical" and + "system-cluster-critical" are two special keywords which indicate the + highest priorities with the former being the highest priority. Any other + name must be defined by creating a PriorityClass object with that name. + If not specified, the pod priority will be default or zero if there is no default. type: string readinessGates: - description: 'If specified, all readiness gates will be evaluated - for pod readiness. A pod is ready when all its containers - are ready AND all conditions specified in the readiness - gates have status equal to "True" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates' + description: |- + If specified, all readiness gates will be evaluated for pod readiness. + A pod is ready when all its containers are ready AND + all conditions specified in the readiness gates have status equal to "True" + More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates items: description: PodReadinessGate contains the reference to a pod condition @@ -10920,45 +11282,53 @@ spec: type: object type: array resourceClaims: - description: "ResourceClaims defines which ResourceClaims - must be allocated and reserved before the Pod is allowed - to start. The resources will be made available to those - containers which consume them by name. \n This is an alpha - field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." + description: |- + ResourceClaims defines which ResourceClaims must be allocated + and reserved before the Pod is allowed to start. The resources + will be made available to those containers which consume them + by name. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. items: - description: PodResourceClaim references exactly one ResourceClaim - through a ClaimSource. It adds a name to it that uniquely - identifies the ResourceClaim inside the Pod. Containers - that need access to the ResourceClaim reference it with - this name. + description: |- + PodResourceClaim references exactly one ResourceClaim through a ClaimSource. + It adds a name to it that uniquely identifies the ResourceClaim inside the Pod. + Containers that need access to the ResourceClaim reference it with this name. properties: name: - description: Name uniquely identifies this resource - claim inside the pod. This must be a DNS_LABEL. + description: |- + Name uniquely identifies this resource claim inside the pod. + This must be a DNS_LABEL. type: string source: description: Source describes where to find the ResourceClaim. properties: resourceClaimName: - description: ResourceClaimName is the name of a - ResourceClaim object in the same namespace as - this pod. + description: |- + ResourceClaimName is the name of a ResourceClaim object in the same + namespace as this pod. type: string resourceClaimTemplateName: - description: "ResourceClaimTemplateName is the name - of a ResourceClaimTemplate object in the same - namespace as this pod. \n The template will be - used to create a new ResourceClaim, which will - be bound to this pod. When this pod is deleted, - the ResourceClaim will also be deleted. The pod - name and resource name, along with a generated - component, will be used to form a unique name - for the ResourceClaim, which will be recorded - in pod.status.resourceClaimStatuses. \n This field - is immutable and no changes will be made to the - corresponding ResourceClaim by the control plane - after creating the ResourceClaim." + description: |- + ResourceClaimTemplateName is the name of a ResourceClaimTemplate + object in the same namespace as this pod. + + + The template will be used to create a new ResourceClaim, which will + be bound to this pod. When this pod is deleted, the ResourceClaim + will also be deleted. The pod name and resource name, along with a + generated component, will be used to form a unique name for the + ResourceClaim, which will be recorded in pod.status.resourceClaimStatuses. + + + This field is immutable and no changes will be made to the + corresponding ResourceClaim by the control plane after creating the + ResourceClaim. type: string type: object required: @@ -10969,40 +11339,44 @@ spec: - name x-kubernetes-list-type: map restartPolicy: - description: 'Restart policy for all containers within the - pod. One of Always, OnFailure, Never. In some contexts, - only a subset of those values may be permitted. Default - to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' + description: |- + Restart policy for all containers within the pod. + One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. + Default to Always. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy type: string runtimeClassName: - description: 'RuntimeClassName refers to a RuntimeClass object - in the node.k8s.io group, which should be used to run this - pod. If no RuntimeClass resource matches the named class, - the pod will not be run. If unset or empty, the "legacy" - RuntimeClass will be used, which is an implicit class with - an empty definition that uses the default runtime handler. - More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class' + description: |- + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used + to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. + If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an + empty definition that uses the default runtime handler. + More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class type: string schedulerName: - description: If specified, the pod will be dispatched by specified - scheduler. If not specified, the pod will be dispatched - by default scheduler. + description: |- + If specified, the pod will be dispatched by specified scheduler. + If not specified, the pod will be dispatched by default scheduler. type: string schedulingGates: - description: "SchedulingGates is an opaque list of values - that if specified will block scheduling the pod. If schedulingGates - is not empty, the pod will stay in the SchedulingGated state - and the scheduler will not attempt to schedule the pod. - \n SchedulingGates can only be set at pod creation time, - and be removed only afterwards. \n This is a beta feature - enabled by the PodSchedulingReadiness feature gate." + description: |- + SchedulingGates is an opaque list of values that if specified will block scheduling the pod. + If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the + scheduler will not attempt to schedule the pod. + + + SchedulingGates can only be set at pod creation time, and be removed only afterwards. + + + This is a beta feature enabled by the PodSchedulingReadiness feature gate. items: description: PodSchedulingGate is associated to a Pod to guard its scheduling. properties: name: - description: Name of the scheduling gate. Each scheduling - gate must have a unique name field. + description: |- + Name of the scheduling gate. + Each scheduling gate must have a unique name field. type: string required: - name @@ -11012,71 +11386,73 @@ spec: - name x-kubernetes-list-type: map securityContext: - description: 'SecurityContext holds pod-level security attributes - and common container settings. Optional: Defaults to empty. See - type description for default values of each field.' + description: |- + SecurityContext holds pod-level security attributes and common container settings. + Optional: Defaults to empty. See type description for default values of each field. properties: fsGroup: - description: "A special supplemental group that applies - to all containers in a pod. Some volume types allow - the Kubelet to change the ownership of that volume to - be owned by the pod: \n 1. The owning GID will be the - FSGroup 2. The setgid bit is set (new files created - in the volume will be owned by FSGroup) 3. The permission - bits are OR'd with rw-rw---- \n If unset, the Kubelet - will not modify the ownership and permissions of any - volume. Note that this field cannot be set when spec.os.name - is windows." + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of - changing ownership and permission of the volume before - being exposed inside Pod. This field will only apply - to volume types which support fsGroup based ownership(and - permissions). It will have no effect on ephemeral volume - types such as: secret, configmaps and emptydir. Valid - values are "OnRootMismatch" and "Always". If not specified, - "Always" is used. Note that this field cannot be set - when spec.os.name is windows.' + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. type: string runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be - set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer runAsNonRoot: - description: Indicates that the container must run as - a non-root user. If true, the Kubelet will validate - the image at runtime to ensure that it does not run - as UID 0 (root) and fail to start the container if it - does. If unset or false, no such validation will be - performed. May also be set in SecurityContext. If set - in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: boolean runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence - for that container. Note that this field cannot be set - when spec.os.name is windows. + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. format: int64 type: integer seLinuxOptions: - description: The SELinux context to be applied to all - containers. If unspecified, the container runtime will - allocate a random SELinux context for each container. May - also be set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this - field cannot be set when spec.os.name is windows. + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. properties: level: description: Level is SELinux level label that applies @@ -11096,50 +11472,48 @@ spec: type: string type: object seccompProfile: - description: The seccomp options to use by the containers - in this pod. Note that this field cannot be set when - spec.os.name is windows. + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. properties: localhostProfile: - description: localhostProfile indicates a profile - defined in a file on the node should be used. The - profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's - configured seccomp profile location. Must be set - if type is "Localhost". Must NOT be set for any - other type. + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. type: string type: - description: "type indicates which kind of seccomp - profile will be applied. Valid options are: \n Localhost - - a profile defined in a file on the node should - be used. RuntimeDefault - the container runtime - default profile should be used. Unconfined - no - profile should be applied." + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. type: string required: - type type: object supplementalGroups: - description: A list of groups applied to the first process - run in each container, in addition to the container's - primary GID, the fsGroup (if specified), and group memberships - defined in the container image for the uid of the container - process. If unspecified, no additional groups are added - to any container. Note that group memberships defined - in the container image for the uid of the container - process are still effective, even if they are not included - in this list. Note that this field cannot be set when - spec.os.name is windows. + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsGroup (if specified), and group memberships + defined in the container image for the uid of the container process. If unspecified, + no additional groups are added to any container. Note that group memberships + defined in the container image for the uid of the container process are still effective, + even if they are not included in this list. + Note that this field cannot be set when spec.os.name is windows. items: format: int64 type: integer type: array sysctls: - description: Sysctls hold a list of namespaced sysctls - used for the pod. Pods with unsupported sysctls (by - the container runtime) might fail to launch. Note that - this field cannot be set when spec.os.name is windows. + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. items: description: Sysctl defines a kernel parameter to be set @@ -11156,170 +11530,158 @@ spec: type: object type: array windowsOptions: - description: The Windows specific settings applied to - all containers. If unspecified, the options within a - container's SecurityContext will be used. If set in - both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is linux. + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. properties: gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA - admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec - named by the GMSACredentialSpecName field. + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. type: string gmsaCredentialSpecName: description: GMSACredentialSpecName is the name of the GMSA credential spec to use. type: string hostProcess: - description: HostProcess determines if a container - should be run as a 'Host Process' container. All - of a Pod's containers must have the same effective - HostProcess value (it is not allowed to have a mix - of HostProcess containers and non-HostProcess containers). - In addition, if HostProcess is true then HostNetwork - must also be set to true. + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. type: boolean runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. type: string type: object type: object serviceAccount: - description: 'DeprecatedServiceAccount is a depreciated alias - for ServiceAccountName. Deprecated: Use serviceAccountName - instead.' + description: |- + DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. + Deprecated: Use serviceAccountName instead. type: string serviceAccountName: - description: 'ServiceAccountName is the name of the ServiceAccount - to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' + description: |- + ServiceAccountName is the name of the ServiceAccount to use to run this pod. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ type: string setHostnameAsFQDN: - description: If true the pod's hostname will be configured - as the pod's FQDN, rather than the leaf name (the default). - In Linux containers, this means setting the FQDN in the - hostname field of the kernel (the nodename field of struct - utsname). In Windows containers, this means setting the - registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters - to FQDN. If a pod does not have FQDN, this has no effect. + description: |- + If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). + In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). + In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN. + If a pod does not have FQDN, this has no effect. Default to false. type: boolean shareProcessNamespace: - description: 'Share a single process namespace between all - of the containers in a pod. When this is set containers - will be able to view and signal processes from other containers - in the same pod, and the first process in each container - will not be assigned PID 1. HostPID and ShareProcessNamespace - cannot both be set. Optional: Default to false.' + description: |- + Share a single process namespace between all of the containers in a pod. + When this is set containers will be able to view and signal processes from other containers + in the same pod, and the first process in each container will not be assigned PID 1. + HostPID and ShareProcessNamespace cannot both be set. + Optional: Default to false. type: boolean subdomain: - description: If specified, the fully qualified Pod hostname - will be "...svc.". If not specified, the pod will not have a domainname - at all. + description: |- + If specified, the fully qualified Pod hostname will be "...svc.". + If not specified, the pod will not have a domainname at all. type: string terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs to - terminate gracefully. May be decreased in delete request. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). If this value is nil, the default grace period - will be used instead. The grace period is the duration in - seconds after the processes running in the pod are sent - a termination signal and the time when the processes are - forcibly halted with a kill signal. Set this value longer - than the expected cleanup time for your process. Defaults - to 30 seconds. + description: |- + Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + If this value is nil, the default grace period will be used instead. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + Defaults to 30 seconds. format: int64 type: integer tolerations: description: If specified, the pod's tolerations. items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . properties: effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, - allowed values are NoSchedule, PreferNoSchedule and - NoExecute. + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. type: string key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If the - key is empty, operator must be Exists; this combination - means to match all values and all keys. + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. type: string operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints of - a particular category. + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. type: string tolerationSeconds: - description: TolerationSeconds represents the period - of time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the - taint forever (do not evict). Zero and negative values - will be treated as 0 (evict immediately) by the system. + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. format: int64 type: integer value: - description: Value is the taint value the toleration - matches to. If the operator is Exists, the value should - be empty, otherwise just a regular string. + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. type: string type: object type: array topologySpreadConstraints: - description: TopologySpreadConstraints describes how a group - of pods ought to spread across topology domains. Scheduler - will schedule pods in a way which abides by the constraints. + description: |- + TopologySpreadConstraints describes how a group of pods ought to spread across topology + domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed. items: description: TopologySpreadConstraint specifies how to spread matching pods among the given topology. properties: labelSelector: - description: LabelSelector is used to find matching - pods. Pods that match this label selector are counted - to determine the number of pods in their corresponding - topology domain. + description: |- + LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine the number of pods + in their corresponding topology domain. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -11331,139 +11693,134 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic matchLabelKeys: - description: "MatchLabelKeys is a set of pod label keys - to select the pods over which spreading will be calculated. - The keys are used to lookup values from the incoming - pod labels, those key-value labels are ANDed with - labelSelector to select the group of existing pods - over which spreading will be calculated for the incoming - pod. The same key is forbidden to exist in both MatchLabelKeys - and LabelSelector. MatchLabelKeys cannot be set when - LabelSelector isn't set. Keys that don't exist in - the incoming pod labels will be ignored. A null or - empty list means only match against labelSelector. - \n This is a beta field and requires the MatchLabelKeysInPodTopologySpread - feature gate to be enabled (enabled by default)." + description: |- + MatchLabelKeys is a set of pod label keys to select the pods over which + spreading will be calculated. The keys are used to lookup values from the + incoming pod labels, those key-value labels are ANDed with labelSelector + to select the group of existing pods over which spreading will be calculated + for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + MatchLabelKeys cannot be set when LabelSelector isn't set. + Keys that don't exist in the incoming pod labels will + be ignored. A null or empty list means only match against labelSelector. + + + This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). items: type: string type: array x-kubernetes-list-type: atomic maxSkew: - description: 'MaxSkew describes the degree to which - pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, - it is the maximum permitted difference between the - number of matching pods in the target topology and - the global minimum. The global minimum is the minimum - number of matching pods in an eligible domain or zero - if the number of eligible domains is less than MinDomains. - For example, in a 3-zone cluster, MaxSkew is set to - 1, and pods with the same labelSelector spread as - 2/2/1: In this case, the global minimum is 1. | zone1 - | zone2 | zone3 | | P P | P P | P | - if MaxSkew - is 1, incoming pod can only be scheduled to zone3 - to become 2/2/2; scheduling it onto zone1(zone2) would - make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - - if MaxSkew is 2, incoming pod can be scheduled onto - any zone. When `whenUnsatisfiable=ScheduleAnyway`, - it is used to give higher precedence to topologies - that satisfy it. It''s a required field. Default value - is 1 and 0 is not allowed.' + description: |- + MaxSkew describes the degree to which pods may be unevenly distributed. + When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference + between the number of matching pods in the target topology and the global minimum. + The global minimum is the minimum number of matching pods in an eligible domain + or zero if the number of eligible domains is less than MinDomains. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 2/2/1: + In this case, the global minimum is 1. + | zone1 | zone2 | zone3 | + | P P | P P | P | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; + scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) + violate MaxSkew(1). + - if MaxSkew is 2, incoming pod can be scheduled onto any zone. + When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence + to topologies that satisfy it. + It's a required field. Default value is 1 and 0 is not allowed. format: int32 type: integer minDomains: - description: "MinDomains indicates a minimum number - of eligible domains. When the number of eligible domains - with matching topology keys is less than minDomains, - Pod Topology Spread treats \"global minimum\" as 0, - and then the calculation of Skew is performed. And - when the number of eligible domains with matching - topology keys equals or greater than minDomains, this - value has no effect on scheduling. As a result, when - the number of eligible domains is less than minDomains, - scheduler won't schedule more than maxSkew Pods to - those domains. If value is nil, the constraint behaves - as if MinDomains is equal to 1. Valid values are integers - greater than 0. When value is not nil, WhenUnsatisfiable - must be DoNotSchedule. \n For example, in a 3-zone - cluster, MaxSkew is set to 2, MinDomains is set to - 5 and pods with the same labelSelector spread as 2/2/2: - | zone1 | zone2 | zone3 | | P P | P P | P P | - The number of domains is less than 5(MinDomains), - so \"global minimum\" is treated as 0. In this situation, - new pod with the same labelSelector cannot be scheduled, - because computed skew will be 3(3 - 0) if new Pod - is scheduled to any of the three zones, it will violate - MaxSkew. \n This is a beta field and requires the - MinDomainsInPodTopologySpread feature gate to be enabled - (enabled by default)." + description: |- + MinDomains indicates a minimum number of eligible domains. + When the number of eligible domains with matching topology keys is less than minDomains, + Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. + And when the number of eligible domains with matching topology keys equals or greater than minDomains, + this value has no effect on scheduling. + As a result, when the number of eligible domains is less than minDomains, + scheduler won't schedule more than maxSkew Pods to those domains. + If value is nil, the constraint behaves as if MinDomains is equal to 1. + Valid values are integers greater than 0. + When value is not nil, WhenUnsatisfiable must be DoNotSchedule. + + + For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same + labelSelector spread as 2/2/2: + | zone1 | zone2 | zone3 | + | P P | P P | P P | + The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. + In this situation, new pod with the same labelSelector cannot be scheduled, + because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, + it will violate MaxSkew. + + + This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default). format: int32 type: integer nodeAffinityPolicy: - description: "NodeAffinityPolicy indicates how we will - treat Pod's nodeAffinity/nodeSelector when calculating - pod topology spread skew. Options are: - Honor: only - nodes matching nodeAffinity/nodeSelector are included - in the calculations. - Ignore: nodeAffinity/nodeSelector - are ignored. All nodes are included in the calculations. - \n If this value is nil, the behavior is equivalent - to the Honor policy. This is a beta-level feature - default enabled by the NodeInclusionPolicyInPodTopologySpread - feature flag." + description: |- + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector + when calculating pod topology spread skew. Options are: + - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. + - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. + + + If this value is nil, the behavior is equivalent to the Honor policy. + This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string nodeTaintsPolicy: - description: "NodeTaintsPolicy indicates how we will - treat node taints when calculating pod topology spread - skew. Options are: - Honor: nodes without taints, - along with tainted nodes for which the incoming pod - has a toleration, are included. - Ignore: node taints - are ignored. All nodes are included. \n If this value - is nil, the behavior is equivalent to the Ignore policy. - This is a beta-level feature default enabled by the - NodeInclusionPolicyInPodTopologySpread feature flag." + description: |- + NodeTaintsPolicy indicates how we will treat node taints when calculating + pod topology spread skew. Options are: + - Honor: nodes without taints, along with tainted nodes for which the incoming pod + has a toleration, are included. + - Ignore: node taints are ignored. All nodes are included. + + + If this value is nil, the behavior is equivalent to the Ignore policy. + This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string topologyKey: - description: TopologyKey is the key of node labels. - Nodes that have a label with this key and identical - values are considered to be in the same topology. - We consider each as a "bucket", and try - to put balanced number of pods into each bucket. We - define a domain as a particular instance of a topology. - Also, we define an eligible domain as a domain whose - nodes meet the requirements of nodeAffinityPolicy - and nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname", - each Node is a domain of that topology. And, if TopologyKey - is "topology.kubernetes.io/zone", each zone is a domain - of that topology. It's a required field. + description: |- + TopologyKey is the key of node labels. Nodes that have a label with this key + and identical values are considered to be in the same topology. + We consider each as a "bucket", and try to put balanced number + of pods into each bucket. + We define a domain as a particular instance of a topology. + Also, we define an eligible domain as a domain whose nodes meet the requirements of + nodeAffinityPolicy and nodeTaintsPolicy. + e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. + And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. + It's a required field. type: string whenUnsatisfiable: - description: 'WhenUnsatisfiable indicates how to deal - with a pod if it doesn''t satisfy the spread constraint. - - DoNotSchedule (default) tells the scheduler not - to schedule it. - ScheduleAnyway tells the scheduler - to schedule the pod in any location, but giving higher - precedence to topologies that would help reduce the - skew. A constraint is considered "Unsatisfiable" for - an incoming pod if and only if every possible node - assignment for that pod would violate "MaxSkew" on - some topology. For example, in a 3-zone cluster, MaxSkew - is set to 1, and pods with the same labelSelector - spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P - | P | P | If WhenUnsatisfiable is set to DoNotSchedule, - incoming pod can only be scheduled to zone2(zone3) - to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) - satisfies MaxSkew(1). In other words, the cluster - can still be imbalanced, but scheduler won''t make - it *more* imbalanced. It''s a required field.' + description: |- + WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy + the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule it. + - ScheduleAnyway tells the scheduler to schedule the pod in any location, + but giving higher precedence to topologies that would help reduce the + skew. + A constraint is considered "Unsatisfiable" for an incoming pod + if and only if every possible node assignment for that pod would violate + "MaxSkew" on some topology. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | + | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled + to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies + MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler + won't make it *more* imbalanced. + It's a required field. type: string required: - maxSkew @@ -11476,44 +11833,44 @@ spec: - whenUnsatisfiable x-kubernetes-list-type: map volumes: - description: 'List of volumes that can be mounted by containers - belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes' + description: |- + List of volumes that can be mounted by containers belonging to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes items: description: Volume represents a named volume in a pod that may be accessed by any container in the pod. properties: awsElasticBlockStore: - description: 'awsElasticBlockStore represents an AWS - Disk resource that is attached to a kubelet''s host - machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + description: |- + awsElasticBlockStore represents an AWS Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore properties: fsType: - description: 'fsType is the filesystem type of the - volume that you want to mount. Tip: Ensure that - the filesystem type is supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem - from compromising the machine' + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising the machine type: string partition: - description: 'partition is the partition in the - volume that you want to mount. If omitted, the - default is to mount by volume name. Examples: - For volume /dev/sda1, you specify the partition - as "1". Similarly, the volume partition for /dev/sda - is "0" (or you can leave the property empty).' + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). format: int32 type: integer readOnly: - description: 'readOnly value true will force the - readOnly setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + description: |- + readOnly value true will force the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore type: boolean volumeID: - description: 'volumeID is unique ID of the persistent - disk resource in AWS (Amazon EBS volume). More - info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + description: |- + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore type: string required: - volumeID @@ -11535,10 +11892,10 @@ spec: the blob storage type: string fsType: - description: fsType is Filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. + description: |- + fsType is Filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string kind: description: 'kind expected values are Shared: multiple @@ -11548,9 +11905,9 @@ spec: set). defaults to shared' type: string readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean required: - diskName @@ -11561,9 +11918,9 @@ spec: mount on the host and bind mount to the pod. properties: readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretName: description: secretName is the name of secret that @@ -11581,8 +11938,9 @@ spec: host that shares a pod's lifetime properties: monitors: - description: 'monitors is Required: Monitors is - a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + monitors is Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it items: type: string type: array @@ -11592,67 +11950,72 @@ spec: is /' type: string readOnly: - description: 'readOnly is Optional: Defaults to - false (read/write). ReadOnly here will force the - ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it type: boolean secretFile: - description: 'secretFile is Optional: SecretFile - is the path to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it type: string secretRef: - description: 'secretRef is Optional: SecretRef is - reference to the authentication secret for User, - default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic user: - description: 'user is optional: User is the rados - user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: |- + user is optional: User is the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it type: string required: - monitors type: object cinder: - description: 'cinder represents a cinder volume attached - and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + cinder represents a cinder volume attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md properties: fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Examples: "ext4", "xfs", "ntfs". - Implicitly inferred to be "ext4" if unspecified. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md type: string readOnly: - description: 'readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md type: boolean secretRef: - description: 'secretRef is optional: points to a - secret object containing parameters used to connect - to OpenStack.' + description: |- + secretRef is optional: points to a secret object containing parameters used to connect + to OpenStack. properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic volumeID: - description: 'volumeID used to identify the volume - in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: |- + volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md type: string required: - volumeID @@ -11662,30 +12025,25 @@ spec: populate this volume properties: defaultMode: - description: 'defaultMode is optional: mode bits - used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. Directories - within the path are not affected by this setting. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: - description: items if unspecified, each key-value - pair in the Data field of the referenced ConfigMap - will be projected into the volume as a file whose - name is the key and content is the value. If specified, - the listed keys will be projected into the specified - paths, and unlisted keys will not be present. - If a key is specified which is not present in - the ConfigMap, the volume setup will error unless - it is marked optional. Paths must be relative - and may not contain the '..' path or start with - '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -11694,25 +12052,21 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode bits - used to set permissions on this file. Must - be an octal value between 0000 and 0777 - or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON - requires decimal values for mode bits. If - not specified, the volume defaultMode will - be used. This might be in conflict with - other options that affect the file mode, - like fsGroup, and the result can be other - mode bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path of - the file to map the key to. May not be an - absolute path. May not contain the path - element '..'. May not start with the string - '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - key @@ -11720,9 +12074,10 @@ spec: type: object type: array name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: optional specify whether the ConfigMap @@ -11736,45 +12091,43 @@ spec: CSI drivers (Beta feature). properties: driver: - description: driver is the name of the CSI driver - that handles this volume. Consult with your admin - for the correct name as registered in the cluster. + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. type: string fsType: - description: fsType to mount. Ex. "ext4", "xfs", - "ntfs". If not provided, the empty value is passed - to the associated CSI driver which will determine - the default filesystem to apply. + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. type: string nodePublishSecretRef: - description: nodePublishSecretRef is a reference - to the secret object containing sensitive information - to pass to the CSI driver to complete the CSI + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. - This field is optional, and may be empty if no - secret is required. If the secret object contains - more than one secret, all secret references are - passed. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic readOnly: - description: readOnly specifies a read-only configuration - for the volume. Defaults to false (read/write). + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). type: boolean volumeAttributes: additionalProperties: type: string - description: volumeAttributes stores driver-specific - properties that are passed to the CSI driver. - Consult your driver's documentation for supported - values. + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. type: object required: - driver @@ -11784,17 +12137,15 @@ spec: the pod that should populate this volume properties: defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a Optional: mode bits - used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. Directories - within the path are not affected by this setting. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' + description: |- + Optional: mode bits to use on created files by default. Must be a + Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: @@ -11824,16 +12175,13 @@ spec: type: object x-kubernetes-map-type: atomic mode: - description: 'Optional: mode bits used to - set permissions on this file, must be an - octal value between 0000 and 0777 or a decimal - value between 0 and 511. YAML accepts both - octal and decimal values, JSON requires - decimal values for mode bits. If not specified, - the volume defaultMode will be used. This - might be in conflict with other options - that affect the file mode, like fsGroup, - and the result can be other mode bits set.' + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: @@ -11844,10 +12192,9 @@ spec: the relative path must not start with ''..''' type: string resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, requests.cpu and requests.memory) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. properties: containerName: description: 'Container name: required @@ -11875,121 +12222,125 @@ spec: type: array type: object emptyDir: - description: 'emptyDir represents a temporary directory - that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + description: |- + emptyDir represents a temporary directory that shares a pod's lifetime. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir properties: medium: - description: 'medium represents what type of storage - medium should back this directory. The default - is "" which means to use the node''s default medium. - Must be an empty string (default) or Memory. More - info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir type: string sizeLimit: anyOf: - type: integer - type: string - description: 'sizeLimit is the total amount of local - storage required for this EmptyDir volume. The - size limit is also applicable for memory medium. - The maximum usage on memory medium EmptyDir would - be the minimum value between the SizeLimit specified - here and the sum of memory limits of all containers - in a pod. The default is nil which means that - the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object ephemeral: - description: "ephemeral represents a volume that is - handled by a cluster storage driver. The volume's - lifecycle is tied to the pod that defines it - it - will be created before the pod starts, and deleted - when the pod is removed. \n Use this if: a) the volume - is only needed while the pod runs, b) features of - normal volumes like restoring from snapshot or capacity - tracking are needed, c) the storage driver is specified - through a storage class, and d) the storage driver - supports dynamic volume provisioning through a PersistentVolumeClaim - (see EphemeralVolumeSource for more information on - the connection between this volume type and PersistentVolumeClaim). - \n Use PersistentVolumeClaim or one of the vendor-specific - APIs for volumes that persist for longer than the - lifecycle of an individual pod. \n Use CSI for light-weight - local ephemeral volumes if the CSI driver is meant - to be used that way - see the documentation of the - driver for more information. \n A pod can use both - types of ephemeral volumes and persistent volumes - at the same time." + description: |- + ephemeral represents a volume that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, + and deleted when the pod is removed. + + + Use this if: + a) the volume is only needed while the pod runs, + b) features of normal volumes like restoring from snapshot or capacity + tracking are needed, + c) the storage driver is specified through a storage class, and + d) the storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for more + information on the connection between this volume type + and PersistentVolumeClaim). + + + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. + + + Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to + be used that way - see the documentation of the driver for + more information. + + + A pod can use both types of ephemeral volumes and + persistent volumes at the same time. properties: volumeClaimTemplate: - description: "Will be used to create a stand-alone - PVC to provision the volume. The pod in which - this EphemeralVolumeSource is embedded will be - the owner of the PVC, i.e. the PVC will be deleted - together with the pod. The name of the PVC will - be `-` where `` - is the name from the `PodSpec.Volumes` array entry. - Pod validation will reject the pod if the concatenated - name is not valid for a PVC (for example, too - long). \n An existing PVC with that name that - is not owned by the pod will *not* be used for - the pod to avoid using an unrelated volume by - mistake. Starting the pod is then blocked until - the unrelated PVC is removed. If such a pre-created - PVC is meant to be used by the pod, the PVC has - to updated with an owner reference to the pod - once the pod exists. Normally this should not - be necessary, but it may be useful when manually - reconstructing a broken cluster. \n This field - is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. \n Required, - must not be nil." + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). + + + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. + + + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. + + + Required, must not be nil. properties: metadata: - description: May contain labels and annotations - that will be copied into the PVC when creating - it. No other fields are allowed and will be - rejected during validation. + description: |- + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. type: object spec: - description: The specification for the PersistentVolumeClaim. - The entire content is copied unchanged into - the PVC that gets created from this template. - The same fields as in a PersistentVolumeClaim + description: |- + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim are also valid here. properties: accessModes: - description: 'accessModes contains the desired - access modes the volume should have. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 items: type: string type: array dataSource: - description: 'dataSource field can be used - to specify either: * An existing VolumeSnapshot - object (snapshot.storage.k8s.io/VolumeSnapshot) + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external controller - can support the specified data source, - it will create a new volume based on the - contents of the specified data source. - When the AnyVolumeDataSource feature gate - is enabled, dataSource contents will be - copied to dataSourceRef, and dataSourceRef - contents will be copied to dataSource - when dataSourceRef.namespace is not specified. - If the namespace is specified, then dataSourceRef - will not be copied to dataSource.' + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. properties: apiGroup: - description: APIGroup is the group for - the resource being referenced. If - APIGroup is not specified, the specified - Kind must be in the core API group. - For any other third-party types, APIGroup - is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource @@ -12005,50 +12356,36 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the - object from which to populate the volume - with data, if a non-empty volume is desired. - This may be any object from a non-empty - API group (non core object) or a PersistentVolumeClaim - object. When this field is specified, - volume binding will only succeed if the - type of the specified object matches some - installed volume populator or dynamic - provisioner. This field will replace the - functionality of the dataSource field - and as such if both fields are non-empty, - they must have the same value. For backwards - compatibility, when namespace isn''t specified - in dataSourceRef, both fields (dataSource - and dataSourceRef) will be set to the - same value automatically if one of them - is empty and the other is non-empty. When - namespace is specified in dataSourceRef, - dataSource isn''t set to the same value - and must be empty. There are three important - differences between dataSource and dataSourceRef: - * While dataSource only allows two specific - types of objects, dataSourceRef allows - any non-core object, as well as PersistentVolumeClaim - objects. * While dataSource ignores disallowed - values (dropping them), dataSourceRef - preserves all values, and generates an - error if a disallowed value is specified. - * While dataSource only allows local objects, - dataSourceRef allows objects in any namespaces. - (Beta) Using this field requires the AnyVolumeDataSource - feature gate to be enabled. (Alpha) Using - the namespace field of dataSourceRef requires - the CrossNamespaceVolumeDataSource feature - gate to be enabled.' + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. properties: apiGroup: - description: APIGroup is the group for - the resource being referenced. If - APIGroup is not specified, the specified - Kind must be in the core API group. - For any other third-party types, APIGroup - is required. + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. type: string kind: description: Kind is the type of resource @@ -12059,58 +12396,23 @@ spec: being referenced type: string namespace: - description: Namespace is the namespace - of resource being referenced Note - that when a namespace is specified, - a gateway.networking.k8s.io/ReferenceGrant - object is required in the referent - namespace to allow that namespace's - owner to accept the reference. See - the ReferenceGrant documentation for - details. (Alpha) This field requires - the CrossNamespaceVolumeDataSource - feature gate to be enabled. + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. type: string required: - kind - name type: object resources: - description: 'resources represents the minimum - resources the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to - specify resource requirements that are - lower than previous value but must still - be higher than capacity recorded in the - status field of the claim. More info: - https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources properties: - claims: - description: "Claims lists the names - of resources, defined in spec.resourceClaims, - that are used by this container. \n - This is an alpha field and requires - enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. - It can only be set for containers." - items: - description: ResourceClaim references - one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the - name of one entry in pod.spec.resourceClaims - of the Pod where this field - is used. It makes that resource - available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -12118,9 +12420,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object requests: additionalProperties: @@ -12129,14 +12431,11 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the - minimum amount of compute resources - required. If Requests is omitted for - a container, it defaults to Limits - if that is explicitly specified, otherwise - to an implementation-defined value. - Requests cannot exceed Limits. More - info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object selector: @@ -12148,10 +12447,9 @@ spec: of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -12159,20 +12457,16 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. - This array is replaced during - a strategic merge patch. + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string type: array @@ -12184,27 +12478,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of - {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic storageClassName: - description: 'storageClassName is the name - of the StorageClass required by the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: - description: volumeMode defines what type - of volume is required by the claim. Value - of Filesystem is implied when not included - in claim spec. + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. type: string volumeName: description: volumeName is the binding reference @@ -12221,21 +12525,20 @@ spec: exposed to the pod. properties: fsType: - description: 'fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. TODO: how - do we prevent errors in the filesystem from compromising - the machine' + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising the machine type: string lun: description: 'lun is Optional: FC target lun number' format: int32 type: integer readOnly: - description: 'readOnly is Optional: Defaults to - false (read/write). ReadOnly here will force the - ReadOnly setting in VolumeMounts.' + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean targetWWNs: description: 'targetWWNs is Optional: FC target @@ -12244,28 +12547,27 @@ spec: type: string type: array wwids: - description: 'wwids Optional: FC volume world wide - identifiers (wwids) Either wwids or combination - of targetWWNs and lun must be set, but not both - simultaneously.' + description: |- + wwids Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. items: type: string type: array type: object flexVolume: - description: flexVolume represents a generic volume - resource that is provisioned/attached using an exec - based plugin. + description: |- + flexVolume represents a generic volume resource that is + provisioned/attached using an exec based plugin. properties: driver: description: driver is the name of the driver to use for this volume. type: string fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". The - default filesystem depends on FlexVolume script. + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. type: string options: additionalProperties: @@ -12274,23 +12576,23 @@ spec: extra command options if any.' type: object readOnly: - description: 'readOnly is Optional: defaults to - false (read/write). ReadOnly here will force the - ReadOnly setting in VolumeMounts.' + description: |- + readOnly is Optional: defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: 'secretRef is Optional: secretRef is - reference to the secret object containing sensitive - information to pass to the plugin scripts. This - may be empty if no secret object is specified. - If the secret object contains more than one secret, - all secrets are passed to the plugin scripts.' + description: |- + secretRef is Optional: secretRef is reference to the secret object containing + sensitive information to pass to the plugin scripts. This may be + empty if no secret object is specified. If the secret object + contains more than one secret, all secrets are passed to the plugin + scripts. properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -12303,9 +12605,9 @@ spec: control service being running properties: datasetName: - description: datasetName is Name of the dataset - stored as metadata -> name on the dataset for - Flocker should be considered as deprecated + description: |- + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker + should be considered as deprecated type: string datasetUUID: description: datasetUUID is the UUID of the dataset. @@ -12313,57 +12615,55 @@ spec: type: string type: object gcePersistentDisk: - description: 'gcePersistentDisk represents a GCE Disk - resource that is attached to a kubelet''s host machine - and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + gcePersistentDisk represents a GCE Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk properties: fsType: - description: 'fsType is filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem - from compromising the machine' + description: |- + fsType is filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising the machine type: string partition: - description: 'partition is the partition in the - volume that you want to mount. If omitted, the - default is to mount by volume name. Examples: - For volume /dev/sda1, you specify the partition - as "1". Similarly, the volume partition for /dev/sda - is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk format: int32 type: integer pdName: - description: 'pdName is unique name of the PD resource - in GCE. Used to identify the disk in GCE. More - info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk type: string readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More - info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk type: boolean required: - pdName type: object gitRepo: - description: 'gitRepo represents a git repository at - a particular revision. DEPRECATED: GitRepo is deprecated. - To provision a container with a git repo, mount an - EmptyDir into an InitContainer that clones the repo - using git, then mount the EmptyDir into the Pod''s - container.' + description: |- + gitRepo represents a git repository at a particular revision. + DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir + into the Pod's container. properties: directory: - description: directory is the target directory name. - Must not contain or start with '..'. If '.' is - supplied, the volume directory will be the git - repository. Otherwise, if specified, the volume - will contain the git repository in the subdirectory - with the given name. + description: |- + directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, the volume directory will be the + git repository. Otherwise, if specified, the volume will contain the git repository in + the subdirectory with the given name. type: string repository: description: repository is the URL @@ -12376,54 +12676,61 @@ spec: - repository type: object glusterfs: - description: 'glusterfs represents a Glusterfs mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/glusterfs/README.md' + description: |- + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md properties: endpoints: - description: 'endpoints is the endpoint name that - details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: |- + endpoints is the endpoint name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod type: string path: - description: 'path is the Glusterfs volume path. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: |- + path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod type: string readOnly: - description: 'readOnly here will force the Glusterfs - volume to be mounted with read-only permissions. - Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: |- + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. + Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod type: boolean required: - endpoints - path type: object hostPath: - description: 'hostPath represents a pre-existing file - or directory on the host machine that is directly - exposed to the container. This is generally used for - system agents or other privileged things that are - allowed to see the host machine. Most containers will - NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use - host directory mounts and who can/can not mount host - directories as read/write.' + description: |- + hostPath represents a pre-existing file or directory on the host + machine that is directly exposed to the container. This is generally + used for system agents or other privileged things that are allowed + to see the host machine. Most containers will NOT need this. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- + TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not + mount host directories as read/write. properties: path: - description: 'path of the directory on the host. - If the path is a symlink, it will follow the link - to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + description: |- + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath type: string type: - description: 'type for HostPath Volume Defaults - to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath type: string required: - path type: object iscsi: - description: 'iscsi represents an ISCSI Disk resource - that is attached to a kubelet''s host machine and - then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + description: |- + iscsi represents an ISCSI Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md properties: chapAuthDiscovery: description: chapAuthDiscovery defines whether support @@ -12434,62 +12741,59 @@ spec: iSCSI Session CHAP authentication type: boolean fsType: - description: 'fsType is the filesystem type of the - volume that you want to mount. Tip: Ensure that - the filesystem type is supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem - from compromising the machine' + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising the machine type: string initiatorName: - description: initiatorName is the custom iSCSI Initiator - Name. If initiatorName is specified with iscsiInterface - simultaneously, new iSCSI interface : will be created for the connection. + description: |- + initiatorName is the custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface + : will be created for the connection. type: string iqn: description: iqn is the target iSCSI Qualified Name. type: string iscsiInterface: - description: iscsiInterface is the interface Name - that uses an iSCSI transport. Defaults to 'default' - (tcp). + description: |- + iscsiInterface is the interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). type: string lun: description: lun represents iSCSI Target Lun number. format: int32 type: integer portals: - description: portals is the iSCSI Target Portal - List. The portal is either an IP or ip_addr:port - if the port is other than default (typically TCP - ports 860 and 3260). + description: |- + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). items: type: string type: array readOnly: - description: readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. type: boolean secretRef: description: secretRef is the CHAP Secret for iSCSI target and initiator authentication properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic targetPortal: - description: targetPortal is iSCSI Target Portal. - The Portal is either an IP or ip_addr:port if - the port is other than default (typically TCP - ports 860 and 3260). + description: |- + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). type: string required: - iqn @@ -12497,43 +12801,51 @@ spec: - targetPortal type: object name: - description: 'name of the volume. Must be a DNS_LABEL - and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string nfs: - description: 'nfs represents an NFS mount on the host - that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + nfs represents an NFS mount on the host that shares a pod's lifetime + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs properties: path: - description: 'path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs type: string readOnly: - description: 'readOnly here will force the NFS export - to be mounted with read-only permissions. Defaults - to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + readOnly here will force the NFS export to be mounted with read-only permissions. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs type: boolean server: - description: 'server is the hostname or IP address - of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + description: |- + server is the hostname or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs type: string required: - path - server type: object persistentVolumeClaim: - description: 'persistentVolumeClaimVolumeSource represents - a reference to a PersistentVolumeClaim in the same - namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + description: |- + persistentVolumeClaimVolumeSource represents a reference to a + PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims properties: claimName: - description: 'claimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + description: |- + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims type: string readOnly: - description: readOnly Will force the ReadOnly setting - in VolumeMounts. Default false. + description: |- + readOnly Will force the ReadOnly setting in VolumeMounts. + Default false. type: boolean required: - claimName @@ -12544,10 +12856,10 @@ spec: machine properties: fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string pdID: description: pdID is the ID that identifies Photon @@ -12561,15 +12873,15 @@ spec: attached and mounted on kubelets host machine properties: fsType: - description: fSType represents the filesystem type - to mount Must be a filesystem type supported by - the host operating system. Ex. "ext4", "xfs". - Implicitly inferred to be "ext4" if unspecified. + description: |- + fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. type: string readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean volumeID: description: volumeID uniquely identifies a Portworx @@ -12583,16 +12895,13 @@ spec: secrets, configmaps, and downward API properties: defaultMode: - description: defaultMode are the mode bits used - to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Directories within the path - are not affected by this setting. This might be - in conflict with other options that affect the - file mode, like fsGroup, and the result can be - other mode bits set. + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer sources: @@ -12601,24 +12910,114 @@ spec: description: Projection that may be projected along with other supported volume types properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a + list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume + root to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object configMap: description: configMap information about the configMap data to project properties: items: - description: items if unspecified, each - key-value pair in the Data field of - the referenced ConfigMap will be projected - into the volume as a file whose name - is the key and content is the value. - If specified, the listed keys will be - projected into the specified paths, - and unlisted keys will not be present. - If a key is specified which is not present - in the ConfigMap, the volume setup will - error unless it is marked optional. - Paths must be relative and may not contain - the '..' path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -12627,29 +13026,21 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: - mode bits used to set permissions - on this file. Must be an octal - value between 0000 and 0777 or - a decimal value between 0 and - 511. YAML accepts both octal and - decimal values, JSON requires - decimal values for mode bits. - If not specified, the volume defaultMode - will be used. This might be in - conflict with other options that - affect the file mode, like fsGroup, - and the result can be other mode - bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative - path of the file to map the key - to. May not be an absolute path. - May not contain the path element - '..'. May not start with the string - '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - key @@ -12657,10 +13048,10 @@ spec: type: object type: array name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: optional specify whether @@ -12701,20 +13092,13 @@ spec: type: object x-kubernetes-map-type: atomic mode: - description: 'Optional: mode bits - used to set permissions on this - file, must be an octal value between - 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts - both octal and decimal values, - JSON requires decimal values for - mode bits. If not specified, the - volume defaultMode will be used. - This might be in conflict with - other options that affect the - file mode, like fsGroup, and the - result can be other mode bits - set.' + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: @@ -12727,12 +13111,9 @@ spec: start with ''..''' type: string resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, requests.cpu and - requests.memory) are currently - supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. properties: containerName: description: 'Container name: @@ -12766,19 +13147,14 @@ spec: secret data to project properties: items: - description: items if unspecified, each - key-value pair in the Data field of - the referenced Secret will be projected - into the volume as a file whose name - is the key and content is the value. - If specified, the listed keys will be - projected into the specified paths, - and unlisted keys will not be present. - If a key is specified which is not present - in the Secret, the volume setup will - error unless it is marked optional. - Paths must be relative and may not contain - the '..' path or start with '..'. + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -12787,29 +13163,21 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: - mode bits used to set permissions - on this file. Must be an octal - value between 0000 and 0777 or - a decimal value between 0 and - 511. YAML accepts both octal and - decimal values, JSON requires - decimal values for mode bits. - If not specified, the volume defaultMode - will be used. This might be in - conflict with other options that - affect the file mode, like fsGroup, - and the result can be other mode - bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative - path of the file to map the key - to. May not be an absolute path. - May not contain the path element - '..'. May not start with the string - '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - key @@ -12817,10 +13185,10 @@ spec: type: object type: array name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: optional field specify whether @@ -12833,32 +13201,26 @@ spec: about the serviceAccountToken data to project properties: audience: - description: audience is the intended - audience of the token. A recipient of - a token must identify itself with an - identifier specified in the audience - of the token, and otherwise should reject - the token. The audience defaults to - the identifier of the apiserver. + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. type: string expirationSeconds: - description: expirationSeconds is the - requested duration of validity of the - service account token. As the token - approaches expiration, the kubelet volume - plugin will proactively rotate the service - account token. The kubelet will start - trying to rotate the token if the token - is older than 80 percent of its time - to live or if the token is older than - 24 hours.Defaults to 1 hour and must - be at least 10 minutes. + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. format: int64 type: integer path: - description: path is the path relative - to the mount point of the file to project - the token into. + description: |- + path is the path relative to the mount point of the file to project the + token into. type: string required: - path @@ -12871,29 +13233,30 @@ spec: host that shares a pod's lifetime properties: group: - description: group to map volume access to Default - is no group + description: |- + group to map volume access to + Default is no group type: string readOnly: - description: readOnly here will force the Quobyte - volume to be mounted with read-only permissions. + description: |- + readOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false. type: boolean registry: - description: registry represents a single or multiple - Quobyte Registry services specified as a string - as host:port pair (multiple entries are separated - with commas) which acts as the central registry - for volumes + description: |- + registry represents a single or multiple Quobyte Registry services + specified as a string as host:port pair (multiple entries are separated with commas) + which acts as the central registry for volumes type: string tenant: - description: tenant owning the given Quobyte volume - in the Backend Used with dynamically provisioned - Quobyte volumes, value is set by the plugin + description: |- + tenant owning the given Quobyte volume in the Backend + Used with dynamically provisioned Quobyte volumes, value is set by the plugin type: string user: - description: user to map volume access to Defaults - to serivceaccount user + description: |- + user to map volume access to + Defaults to serivceaccount user type: string volume: description: volume is a string that references @@ -12904,60 +13267,68 @@ spec: - volume type: object rbd: - description: 'rbd represents a Rados Block Device mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/rbd/README.md' + description: |- + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md properties: fsType: - description: 'fsType is the filesystem type of the - volume that you want to mount. Tip: Ensure that - the filesystem type is supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem - from compromising the machine' + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising the machine type: string image: - description: 'image is the rados image name. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string keyring: - description: 'keyring is the path to key ring for - RBDUser. Default is /etc/ceph/keyring. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string monitors: - description: 'monitors is a collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it items: type: string type: array pool: - description: 'pool is the rados pool name. Default - is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + pool is the rados pool name. + Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string readOnly: - description: 'readOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: boolean secretRef: - description: 'secretRef is name of the authentication - secret for RBDUser. If provided overrides keyring. - Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + secretRef is name of the authentication secret for RBDUser. If provided + overrides keyring. + Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic user: - description: 'user is the rados user name. Default - is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: |- + user is the rados user name. + Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string required: - image @@ -12968,10 +13339,11 @@ spec: volume attached and mounted on Kubernetes nodes. properties: fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Default - is "xfs". + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". + Default is "xfs". type: string gateway: description: gateway is the host address of the @@ -12982,21 +13354,20 @@ spec: ScaleIO Protection Domain for the configured storage. type: string readOnly: - description: readOnly Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: secretRef references to the secret - for ScaleIO user and other sensitive information. - If this is not provided, Login operation will - fail. + description: |- + secretRef references to the secret for ScaleIO user and other + sensitive information. If this is not provided, Login operation will fail. properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -13005,8 +13376,8 @@ spec: communication with Gateway, default false type: boolean storageMode: - description: storageMode indicates whether the storage - for a volume should be ThickProvisioned or ThinProvisioned. + description: |- + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. type: string storagePool: @@ -13018,9 +13389,9 @@ spec: as configured in ScaleIO. type: string volumeName: - description: volumeName is the name of a volume - already created in the ScaleIO system that is - associated with this volume source. + description: |- + volumeName is the name of a volume already created in the ScaleIO system + that is associated with this volume source. type: string required: - gateway @@ -13028,34 +13399,30 @@ spec: - system type: object secret: - description: 'secret represents a secret that should - populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret properties: defaultMode: - description: 'defaultMode is Optional: mode bits - used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML accepts - both octal and decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. Directories - within the path are not affected by this setting. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer items: - description: items If unspecified, each key-value - pair in the Data field of the referenced Secret - will be projected into the volume as a file whose - name is the key and content is the value. If specified, - the listed keys will be projected into the specified - paths, and unlisted keys will not be present. - If a key is specified which is not present in - the Secret, the volume setup will error unless - it is marked optional. Paths must be relative - and may not contain the '..' path or start with - '..'. + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -13064,25 +13431,21 @@ spec: description: key is the key to project. type: string mode: - description: 'mode is Optional: mode bits - used to set permissions on this file. Must - be an octal value between 0000 and 0777 - or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON - requires decimal values for mode bits. If - not specified, the volume defaultMode will - be used. This might be in conflict with - other options that affect the file mode, - like fsGroup, and the result can be other - mode bits set.' + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. format: int32 type: integer path: - description: path is the relative path of - the file to map the key to. May not be an - absolute path. May not contain the path - element '..'. May not start with the string - '..'. + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - key @@ -13094,8 +13457,9 @@ spec: Secret or its keys must be defined type: boolean secretName: - description: 'secretName is the name of the secret - in the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret type: string type: object storageos: @@ -13103,44 +13467,42 @@ spec: attached and mounted on Kubernetes nodes. properties: fsType: - description: fsType is the filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string readOnly: - description: readOnly defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: secretRef specifies the secret to use - for obtaining the StorageOS API credentials. If - not specified, default values will be attempted. + description: |- + secretRef specifies the secret to use for obtaining the StorageOS API + credentials. If not specified, default values will be attempted. properties: name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic volumeName: - description: volumeName is the human-readable name - of the StorageOS volume. Volume names are only - unique within a namespace. + description: |- + volumeName is the human-readable name of the StorageOS volume. Volume + names are only unique within a namespace. type: string volumeNamespace: - description: volumeNamespace specifies the scope - of the volume within StorageOS. If no namespace - is specified then the Pod's namespace will be - used. This allows the Kubernetes name scoping - to be mirrored within StorageOS for tighter integration. - Set VolumeName to any name to override the default - behaviour. Set to "default" if you are not using - namespaces within StorageOS. Namespaces that do - not pre-exist within StorageOS will be created. + description: |- + volumeNamespace specifies the scope of the volume within StorageOS. If no + namespace is specified then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default behaviour. + Set to "default" if you are not using namespaces within StorageOS. + Namespaces that do not pre-exist within StorageOS will be created. type: string type: object vsphereVolume: @@ -13148,10 +13510,10 @@ spec: attached and mounted on kubelets host machine properties: fsType: - description: fsType is filesystem type to mount. - Must be a filesystem type supported by the host - operating system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. + description: |- + fsType is filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string storagePolicyID: description: storagePolicyID is the storage Policy @@ -13189,8 +13551,10 @@ spec: - pgbouncer type: object status: - description: 'Most recently observed status of the Pooler. This data may - not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + description: |- + Most recently observed status of the Pooler. This data may not be up to + date. Populated by the system. Read-only. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status properties: instances: description: The number of pods trying to be scheduled @@ -13261,7 +13625,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 helm.sh/resource-policy: keep name: scheduledbackups.postgresql.cnpg.io spec: @@ -13289,28 +13653,34 @@ spec: description: ScheduledBackup is the Schema for the scheduledbackups API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: - description: 'Specification of the desired behavior of the ScheduledBackup. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + description: |- + Specification of the desired behavior of the ScheduledBackup. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status properties: backupOwnerReference: default: none - description: 'Indicates which ownerReference should be put inside - the created backup resources.
    - none: no owner reference for - created backup objects (same behavior as before the field was introduced)
    - self: sets the Scheduled backup object as owner of the backup
    - cluster: set the cluster as owner of the backup
    ' + description: |- + Indicates which ownerReference should be put inside the created backup resources.
    + - none: no owner reference for created backup objects (same behavior as before the field was introduced)
    + - self: sets the Scheduled backup object as owner of the backup
    + - cluster: set the cluster as owner of the backup
    enum: - none - self @@ -13331,58 +13701,63 @@ spec: type: boolean method: default: barmanObjectStore - description: 'The backup method to be used, possible options are `barmanObjectStore` - and `volumeSnapshot`. Defaults to: `barmanObjectStore`.' + description: |- + The backup method to be used, possible options are `barmanObjectStore` + and `volumeSnapshot`. Defaults to: `barmanObjectStore`. enum: - barmanObjectStore - volumeSnapshot type: string online: - description: Whether the default type of backup with volume snapshots - is online/hot (`true`, default) or offline/cold (`false`) Overrides - the default setting specified in the cluster field '.spec.backup.volumeSnapshot.online' + description: |- + Whether the default type of backup with volume snapshots is + online/hot (`true`, default) or offline/cold (`false`) + Overrides the default setting specified in the cluster field '.spec.backup.volumeSnapshot.online' type: boolean onlineConfiguration: - description: Configuration parameters to control the online/hot backup - with volume snapshots Overrides the default settings specified in - the cluster '.backup.volumeSnapshot.onlineConfiguration' stanza + description: |- + Configuration parameters to control the online/hot backup with volume snapshots + Overrides the default settings specified in the cluster '.backup.volumeSnapshot.onlineConfiguration' stanza properties: immediateCheckpoint: - description: Control whether the I/O workload for the backup initial - checkpoint will be limited, according to the `checkpoint_completion_target` - setting on the PostgreSQL server. If set to true, an immediate - checkpoint will be used, meaning PostgreSQL will complete the - checkpoint as soon as possible. `false` by default. + description: |- + Control whether the I/O workload for the backup initial checkpoint will + be limited, according to the `checkpoint_completion_target` setting on + the PostgreSQL server. If set to true, an immediate checkpoint will be + used, meaning PostgreSQL will complete the checkpoint as soon as + possible. `false` by default. type: boolean waitForArchive: default: true - description: If false, the function will return immediately after - the backup is completed, without waiting for WAL to be archived. - This behavior is only useful with backup software that independently - monitors WAL archiving. Otherwise, WAL required to make the - backup consistent might be missing and make the backup useless. - By default, or when this parameter is true, pg_backup_stop will - wait for WAL to be archived when archiving is enabled. On a - standby, this means that it will wait only when archive_mode - = always. If write activity on the primary is low, it may be - useful to run pg_switch_wal on the primary in order to trigger + description: |- + If false, the function will return immediately after the backup is completed, + without waiting for WAL to be archived. + This behavior is only useful with backup software that independently monitors WAL archiving. + Otherwise, WAL required to make the backup consistent might be missing and make the backup useless. + By default, or when this parameter is true, pg_backup_stop will wait for WAL to be archived when archiving is + enabled. + On a standby, this means that it will wait only when archive_mode = always. + If write activity on the primary is low, it may be useful to run pg_switch_wal on the primary in order to trigger an immediate segment switch. type: boolean type: object schedule: - description: The schedule does not follow the same format used in - Kubernetes CronJobs as it includes an additional seconds specifier, + description: |- + The schedule does not follow the same format used in Kubernetes CronJobs + as it includes an additional seconds specifier, see https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format type: string suspend: description: If this backup is suspended or not type: boolean target: - description: The policy to decide which instance should perform this - backup. If empty, it defaults to `cluster.spec.backup.target`. Available - options are empty string, `primary` and `prefer-standby`. `primary` - to have backups run always on primary instances, `prefer-standby` - to have backups run preferably on the most updated standby, if available. + description: |- + The policy to decide which instance should perform this backup. If empty, + it defaults to `cluster.spec.backup.target`. + Available options are empty string, `primary` and `prefer-standby`. + `primary` to have backups run always on primary instances, + `prefer-standby` to have backups run preferably on the most updated + standby, if available. enum: - primary - prefer-standby @@ -13392,9 +13767,10 @@ spec: - schedule type: object status: - description: 'Most recently observed status of the ScheduledBackup. This - data may not be up to date. Populated by the system. Read-only. More - info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + description: |- + Most recently observed status of the ScheduledBackup. This data may not be up + to date. Populated by the system. Read-only. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status properties: lastCheckTime: description: The latest time the schedule From 866413fbf8fc31e659cb98d41fff222eeb9c8006 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Thu, 14 Mar 2024 20:01:03 +0200 Subject: [PATCH 36/87] Bug Fix: Schema list resources as object (#211) Closes #210 Signed-off-by: Itay Grudev --- charts/cluster/README.md | 2 +- charts/cluster/values.schema.json | 10 +++++----- charts/cluster/values.yaml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 0b882f2f8..ab4811962 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -158,7 +158,7 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | cluster.primaryUpdateMethod | string | `"switchover"` | Method to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated. It can be switchover (default) or in-place (restart). | | cluster.primaryUpdateStrategy | string | `"unsupervised"` | Strategy to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated: it can be automated (unsupervised - default) or manual (supervised) | | cluster.priorityClassName | string | `""` | | -| cluster.resources | string | `nil` | Resources requirements of every generated Pod. Please refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ for more information. We strongly advise you use the same setting for limits and requests so that your cluster pods are given a Guaranteed QoS. See: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ | +| cluster.resources | object | `{}` | Resources requirements of every generated Pod. Please refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ for more information. We strongly advise you use the same setting for limits and requests so that your cluster pods are given a Guaranteed QoS. See: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ | | cluster.storage.size | string | `"8Gi"` | | | cluster.storage.storageClass | string | `""` | | | cluster.superuserSecret | string | `""` | | diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 75a9551b1..2f08a58cd 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -173,14 +173,14 @@ } } }, - "postgresql": { - "type": "object" + "postgresGID": { + "type": "integer" }, "postgresUID": { "type": "integer" }, - "postgresGID": { - "type": "integer" + "postgresql": { + "type": "object" }, "primaryUpdateMethod": { "type": "string" @@ -192,7 +192,7 @@ "type": "string" }, "resources": { - "type": "null" + "type": "object" }, "storage": { "type": "object", diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index ff3c79a19..c2b46ee65 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -92,7 +92,7 @@ cluster: # -- The UID of the postgres user inside the image, defaults to 26 postgresUID: 26 - + # -- The GID of the postgres user inside the image, defaults to 26 postgresGID: 26 @@ -100,7 +100,7 @@ cluster: # Please refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ for more information. # We strongly advise you use the same setting for limits and requests so that your cluster pods are given a Guaranteed QoS. # See: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ - resources: + resources: {} # limits: # cpu: 2000m # memory: 8Gi From c35ca769e9ca044d9f5b8c114a31b09d9896d438 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 17 Mar 2024 18:57:09 +0200 Subject: [PATCH 37/87] Release cluster-v0.0.3 (#220) * Bug Fix: Schema validation of cluster.resources * Bug Fix: Correct ref to superuserSecret * Added support for the specifying user `UID` & `GID` Signed-off-by: Itay Grudev --- RELEASE.md | 9 +++++++-- charts/cluster/Chart.yaml | 2 +- charts/cluster/README.md | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 41fa2b859..984d1d550 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -12,7 +12,12 @@ release of the CloudNativePG operator. I.e. even if we have several release branches in CNPG, we will only target the most advanced point release (e.g. 1.17.1) -## How to release the `cloudnative-pg` chart +## Charts + +1. [Releasing the `cloudnative-pg` chart](#releasing-the-cloudnative-pg-chart) +2. [Releasing `cluster` chart](#releasing-the-cluster-chart) + +## Releasing the `cloudnative-pg` chart In order to create a new release of the `cloudnative-pg` chart, follow these steps: @@ -93,7 +98,7 @@ In order to create a new release of the `cloudnative-pg` chart, follow these ste ``` and be able to see the new version `X.Y.Z` as `CHART VERSION` for `cloudnative-pg` -## How to release the `cluster` chart +## Releasing the `cluster` chart In order to create a new release of the `cluster` chart, follow these steps: diff --git a/charts/cluster/Chart.yaml b/charts/cluster/Chart.yaml index dcae9cb8c..c57c3e6a9 100644 --- a/charts/cluster/Chart.yaml +++ b/charts/cluster/Chart.yaml @@ -18,7 +18,7 @@ name: cluster description: Deploys and manages a CloudNativePG cluster and its associated resources. icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: 0.0.2 +version: 0.0.3 sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cluster/README.md b/charts/cluster/README.md index ab4811962..0ad2c7973 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -1,6 +1,6 @@ # cluster -![Version: 0.0.2](https://img.shields.io/badge/Version-0.0.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![Version: 0.0.3](https://img.shields.io/badge/Version-0.0.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) > **Warning** > ### This chart is under active development. From 5a6de3a4e2045da83d36172592ecdc9339d4b30a Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sun, 17 Mar 2024 23:00:08 +0200 Subject: [PATCH 38/87] Bug Fix Attempt: Cosign confirmation prompt (#222) Signed-off-by: Itay Grudev --- .github/workflows/release-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index fb5905691..5903c9615 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -80,5 +80,5 @@ jobs: name=${file%-*} version=${file%.*} version=${version##*-} - cosign sign ghcr.io/"${GITHUB_REPOSITORY_OWNER}"/charts/"${name}":"${version}" + cosign sign --yes ghcr.io/"${GITHUB_REPOSITORY_OWNER}"/charts/"${name}":"${version}" done From 3d446576119277ab227e551cc2c7355832bd13f5 Mon Sep 17 00:00:00 2001 From: Matthias Baur Date: Tue, 19 Mar 2024 16:59:31 +0100 Subject: [PATCH 39/87] Bug Fix: CNPGClusterLowDiskSpaceCritical Severity should be critical not warning (#223) Signed-off-by: Matthias Baur --- charts/cluster/templates/prometheus-rule.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/cluster/templates/prometheus-rule.yaml b/charts/cluster/templates/prometheus-rule.yaml index 3da33025b..bc3ffde84 100644 --- a/charts/cluster/templates/prometheus-rule.yaml +++ b/charts/cluster/templates/prometheus-rule.yaml @@ -173,5 +173,5 @@ spec: ) > 0.9 for: 5m labels: - severity: warning + severity: critical {{ end }} From 98ef08a21ba8908dab716b9a9fb00a88a69d7e1c Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sun, 17 Mar 2024 19:43:51 +0200 Subject: [PATCH 40/87] feat(cluster): Exposed backup.barmanObjectStore.wal and backup.barmanObjectStore.data Signed-off-by: Itay Grudev --- charts/cluster/README.md | 6 ++++++ charts/cluster/templates/_backup.tpl | 11 ++++++----- charts/cluster/values.schema.json | 28 ++++++++++++++++++++++++++++ charts/cluster/values.yaml | 16 ++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 0ad2c7973..444c7470b 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -120,6 +120,9 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | backups.azure.storageAccount | string | `""` | | | backups.azure.storageKey | string | `""` | | | backups.azure.storageSasToken | string | `""` | | +| backups.data.compression | string | `"gzip"` | Data compression method. One of `` (for no compression), `gzip`, `bzip2` or `snappy`. | +| backups.data.encryption | string | `"AES256"` | Whether to instruct the storage provider to encrypt data files. One of `` (use the storage container default), `AES256` or `aws:kms`. | +| backups.data.jobs | int | `2` | Number of data files to be archived or restored in parallel. | | backups.destinationPath | string | `""` | Overrides the provider specific default path. Defaults to: S3: s3:// Azure: https://..core.windows.net/ Google: gs:// | | backups.enabled | bool | `false` | You need to configure backups manually, so backups are disabled by default. | | backups.endpointURL | string | `""` | Overrides the provider specific default endpoint. Defaults to: S3: https://s3..amazonaws.com" | @@ -137,6 +140,9 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | backups.scheduledBackups[0].backupOwnerReference | string | `"self"` | Backup owner reference | | backups.scheduledBackups[0].name | string | `"daily-backup"` | Scheduled backup name | | backups.scheduledBackups[0].schedule | string | `"0 0 0 * * *"` | Schedule in cron format | +| backups.wal.compression | string | `"gzip"` | WAL compression method. One of `` (for no compression), `gzip`, `bzip2` or `snappy`. | +| backups.wal.encryption | string | `"AES256"` | Whether to instruct the storage provider to encrypt WAL files. One of `` (use the storage container default), `AES256` or `aws:kms`. | +| backups.wal.maxParallel | int | `1` | Number of WAL files to be archived or restored in parallel. | | cluster.additionalLabels | object | `{}` | | | cluster.affinity | object | `{"topologyKey":"topology.kubernetes.io/zone"}` | Affinity/Anti-affinity rules for Pods See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-AffinityConfiguration | | cluster.annotations | object | `{}` | | diff --git a/charts/cluster/templates/_backup.tpl b/charts/cluster/templates/_backup.tpl index cb76d9b74..8b9a094d3 100644 --- a/charts/cluster/templates/_backup.tpl +++ b/charts/cluster/templates/_backup.tpl @@ -5,12 +5,13 @@ backup: retentionPolicy: {{ .Values.backups.retentionPolicy }} barmanObjectStore: wal: - compression: gzip - encryption: AES256 + compression: {{ .Values.backups.wal.compression }} + encryption: {{ .Values.backups.wal.encryption }} + maxParallel: {{ .Values.backups.wal.maxParallel }} data: - compression: gzip - encryption: AES256 - jobs: 2 + compression: {{ .Values.backups.data.compression }} + encryption: {{ .Values.backups.data.encryption }} + jobs: {{ .Values.backups.data.jobs }} {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.backups }} {{- include "cluster.barmanObjectStoreConfig" $d | nindent 2 }} diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 2f08a58cd..c85cced3b 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -34,6 +34,20 @@ } } }, + "data": { + "type": "object", + "properties": { + "compression": { + "type": "string" + }, + "encryption": { + "type": "string" + }, + "jobs": { + "type": "integer" + } + } + }, "destinationPath": { "type": "string" }, @@ -102,6 +116,20 @@ } } } + }, + "wal": { + "type": "object", + "properties": { + "compression": { + "type": "string" + }, + "encryption": { + "type": "string" + }, + "maxParallel": { + "type": "integer" + } + } } } }, diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index c2b46ee65..af9062b6a 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -209,6 +209,21 @@ backups: gkeEnvironment: false applicationCredentials: "" + wal: + # -- WAL compression method. One of `` (for no compression), `gzip`, `bzip2` or `snappy`. + compression: gzip + # -- Whether to instruct the storage provider to encrypt WAL files. One of `` (use the storage container default), `AES256` or `aws:kms`. + encryption: AES256 + # -- Number of WAL files to be archived or restored in parallel. + maxParallel: 1 + data: + # -- Data compression method. One of `` (for no compression), `gzip`, `bzip2` or `snappy`. + compression: gzip + # -- Whether to instruct the storage provider to encrypt data files. One of `` (use the storage container default), `AES256` or `aws:kms`. + encryption: AES256 + # -- Number of data files to be archived or restored in parallel. + jobs: 2 + scheduledBackups: - # -- Scheduled backup name @@ -221,6 +236,7 @@ backups: # -- Retention policy for backups retentionPolicy: "30d" + pooler: # -- Whether to enable PgBouncer enabled: false From 6b0e12c43ad43717362a1efaf1d52bc4e49b72ab Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sun, 17 Mar 2024 17:49:27 +0200 Subject: [PATCH 41/87] feat(cluster): Added additional PgBouncer configuration options * Added monitoring settings * Added pooler.type * Added pooler.template to allow altering the pooler deployment Signed-off-by: Itay Grudev --- .../action.yml | 4 +- .../actions/verify-pooler-ready/action.yml | 32 ++++++++++++++++ .../workflows/tests-cluster-standalone.yml | 37 ++++++++++++++++++- .github/workflows/tests-operator.yml | 2 +- charts/cluster/README.md | 22 ++++++----- charts/cluster/examples/pgbouncer.yaml | 8 ++++ charts/cluster/templates/pooler.yaml | 10 ++++- charts/cluster/values.schema.json | 22 +++++++++++ charts/cluster/values.yaml | 27 +++++++++++--- 9 files changed, 144 insertions(+), 20 deletions(-) rename .github/actions/{verify-ready-instances => verify-cluster-ready}/action.yml (81%) create mode 100644 .github/actions/verify-pooler-ready/action.yml create mode 100644 charts/cluster/examples/pgbouncer.yaml diff --git a/.github/actions/verify-ready-instances/action.yml b/.github/actions/verify-cluster-ready/action.yml similarity index 81% rename from .github/actions/verify-ready-instances/action.yml rename to .github/actions/verify-cluster-ready/action.yml index 7a848e7f3..7e3a522b8 100644 --- a/.github/actions/verify-ready-instances/action.yml +++ b/.github/actions/verify-cluster-ready/action.yml @@ -13,7 +13,7 @@ inputs: runs: using: composite steps: - - name: Wait for cluster to become ready + - name: Wait for the cluster to become ready shell: bash run: | ITER=0 @@ -22,7 +22,7 @@ runs: echo "Cluster not ready" exit 1 fi - READY_INSTANCES=$(kubectl get cluster ${INPUT_CLUSTER_NAME} -o jsonpath='{.status.readyInstances}') + READY_INSTANCES=$(kubectl get clusters.postgresql.cnpg.io ${INPUT_CLUSTER_NAME} -o jsonpath='{.status.readyInstances}') if [[ "$READY_INSTANCES" == ${INPUT_READY_INSTANCES} ]]; then echo "Cluster up and running" break diff --git a/.github/actions/verify-pooler-ready/action.yml b/.github/actions/verify-pooler-ready/action.yml new file mode 100644 index 000000000..d2c48200a --- /dev/null +++ b/.github/actions/verify-pooler-ready/action.yml @@ -0,0 +1,32 @@ +name: Verifies that a CNPG cluster has a certain amount of ready instances +description: Verifies that a CNPG cluster has a certain amount of ready instances +inputs: + pooler-name: + description: The name of the pooler to verify + required: true + default: database-cluster + ready-instances: + description: The amount of ready instances to wait for + required: true + default: "3" + +runs: + using: composite + steps: + - name: Wait for the pooler to become ready + shell: bash + run: | + ITER=0 + while true; do + if [[ $ITER -ge 300 ]]; then + echo "Pooler not ready" + exit 1 + fi + READY_INSTANCES=$(kubectl get cluster ${INPUT_POOLER_NAME} -o jsonpath='{.status.readyInstances}') + if [[ "$READY_INSTANCES" == ${INPUT_READY_INSTANCES} ]]; then + echo "Pooler up and running" + break + fi + sleep 1 + (( ++ITER )) + done diff --git a/.github/workflows/tests-cluster-standalone.yml b/.github/workflows/tests-cluster-standalone.yml index d2b6166f8..d162c84e3 100644 --- a/.github/workflows/tests-cluster-standalone.yml +++ b/.github/workflows/tests-cluster-standalone.yml @@ -30,7 +30,42 @@ jobs: database ./charts/cluster - name: Verify that the cluster is ready - uses: ./.github/actions/verify-ready-instances + uses: ./.github/actions/verify-cluster-ready with: cluster-name: database-cluster ready-instances: 1 + + test-cluster-pgbouncer: + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + + - name: Setup kind + uses: ./.github/actions/setup-kind + + - name: Deploy the operator + uses: ./.github/actions/deploy-operator + + - name: Deploy a standalone cluster + run: | + helm upgrade --install \ + --values charts/cluster/examples/pgbouncer.yaml \ + --namespace database \ + --create-namespace \ + --wait \ + database ./charts/cluster + + - name: Verify that the cluster is ready + uses: ./.github/actions/verify-cluster-ready + with: + cluster-name: database-cluster + ready-instances: 1 + + - name: Verify that the pooler is ready + uses: ./.github/actions/verify-pooler-ready + with: + pooler-name: database-cluster-pooler-rw + ready-instances: 1 diff --git a/.github/workflows/tests-operator.yml b/.github/workflows/tests-operator.yml index 1e043e43c..3177e32f5 100644 --- a/.github/workflows/tests-operator.yml +++ b/.github/workflows/tests-operator.yml @@ -35,7 +35,7 @@ jobs: EOF - name: Verify that the cluster is ready - uses: ./.github/actions/verify-ready-instances + uses: ./.github/actions/verify-cluster-ready with: cluster-name: cluster-example ready-instances: 3 diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 444c7470b..85094a2ce 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -144,23 +144,23 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | backups.wal.encryption | string | `"AES256"` | Whether to instruct the storage provider to encrypt WAL files. One of `` (use the storage container default), `AES256` or `aws:kms`. | | backups.wal.maxParallel | int | `1` | Number of WAL files to be archived or restored in parallel. | | cluster.additionalLabels | object | `{}` | | -| cluster.affinity | object | `{"topologyKey":"topology.kubernetes.io/zone"}` | Affinity/Anti-affinity rules for Pods See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-AffinityConfiguration | +| cluster.affinity | object | `{"topologyKey":"topology.kubernetes.io/zone"}` | Affinity/Anti-affinity rules for Pods. See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-AffinityConfiguration | | cluster.annotations | object | `{}` | | -| cluster.certificates | string | `nil` | The configuration for the CA and related certificates See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-CertificatesConfiguration | +| cluster.certificates | string | `nil` | The configuration for the CA and related certificates. See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-CertificatesConfiguration | | cluster.enableSuperuserAccess | bool | `true` | When this option is enabled, the operator will use the SuperuserSecret to update the postgres user password. If the secret is not present, the operator will automatically create one. When this option is disabled, the operator will ignore the SuperuserSecret content, delete it when automatically created, and then blank the password of the postgres user by setting it to NULL. | | cluster.imageName | string | `""` | Name of the container image, supporting both tags (:) and digests for deterministic and repeatable deployments: :@sha256: | | cluster.imagePullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never or IfNotPresent. If not defined, it defaults to IfNotPresent. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | -| cluster.imagePullSecrets | list | `[]` | The list of pull secrets to be used to pull the images See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-LocalObjectReference | -| cluster.initdb | object | `{}` | BootstrapInitDB is the configuration of the bootstrap process when initdb is used See: https://cloudnative-pg.io/documentation/current/bootstrap/ See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-bootstrapinitdb | +| cluster.imagePullSecrets | list | `[]` | The list of pull secrets to be used to pull the images. See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-LocalObjectReference | +| cluster.initdb | object | `{}` | BootstrapInitDB is the configuration of the bootstrap process when initdb is used. See: https://cloudnative-pg.io/documentation/current/bootstrap/ See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-bootstrapinitdb | | cluster.instances | int | `3` | Number of instances | | cluster.logLevel | string | `"info"` | The instances' log level, one of the following values: error, warning, info (default), debug, trace | -| cluster.monitoring.customQueries | list | `[]` | | -| cluster.monitoring.enabled | bool | `false` | | -| cluster.monitoring.podMonitor.enabled | bool | `true` | | -| cluster.monitoring.prometheusRule.enabled | bool | `true` | | +| cluster.monitoring.customQueries | list | `[]` | Custom Prometheus metrics | +| cluster.monitoring.enabled | bool | `false` | Whether to enable monitoring | +| cluster.monitoring.podMonitor.enabled | bool | `true` | Whether to enable the PodMonitor | +| cluster.monitoring.prometheusRule.enabled | bool | `true` | Whether to enable the PrometheusRule automated alerts | | cluster.postgresGID | int | `26` | The GID of the postgres user inside the image, defaults to 26 | | cluster.postgresUID | int | `26` | The UID of the postgres user inside the image, defaults to 26 | -| cluster.postgresql | object | `{}` | Configuration of the PostgreSQL server See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration | +| cluster.postgresql | object | `{}` | Configuration of the PostgreSQL server. See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration | | cluster.primaryUpdateMethod | string | `"switchover"` | Method to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated. It can be switchover (default) or in-place (restart). | | cluster.primaryUpdateStrategy | string | `"unsupervised"` | Strategy to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated: it can be automated (unsupervised - default) or manual (supervised) | | cluster.priorityClassName | string | `""` | | @@ -173,8 +173,12 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | nameOverride | string | `""` | Override the name of the chart | | pooler.enabled | bool | `false` | Whether to enable PgBouncer | | pooler.instances | int | `3` | Number of PgBouncer instances | +| pooler.monitoring.enabled | bool | `false` | Whether to enable monitoring | +| pooler.monitoring.podMonitor.enabled | bool | `true` | Whether to enable the PodMonitor | | pooler.parameters | object | `{"default_pool_size":"25","max_client_conn":"1000"}` | PgBouncer configuration parameters | | pooler.poolMode | string | `"transaction"` | PgBouncer pooling mode | +| pooler.template | object | `{}` | Custom PgBouncer deployment template. Use to override image, specify resources, etc. | +| pooler.type | string | `"rw"` | PgBouncer type of service to forward traffic to. | | recovery.azure.connectionString | string | `""` | | | recovery.azure.containerName | string | `""` | | | recovery.azure.inheritFromAzureAD | bool | `false` | | diff --git a/charts/cluster/examples/pgbouncer.yaml b/charts/cluster/examples/pgbouncer.yaml new file mode 100644 index 000000000..1da966275 --- /dev/null +++ b/charts/cluster/examples/pgbouncer.yaml @@ -0,0 +1,8 @@ +mode: standalone +cluster: + instances: 1 +backups: + enabled: false +pooler: + enabled: true + instances: 1 diff --git a/charts/cluster/templates/pooler.yaml b/charts/cluster/templates/pooler.yaml index 5a606d96d..5e01fe498 100644 --- a/charts/cluster/templates/pooler.yaml +++ b/charts/cluster/templates/pooler.yaml @@ -7,9 +7,15 @@ spec: cluster: name: {{ include "cluster.fullname" . }} instances: {{ .Values.pooler.instances }} - type: rw + type: {{ .Values.pooler.type }} pgbouncer: poolMode: {{ .Values.pooler.poolMode }} parameters: {{- .Values.pooler.parameters | toYaml | nindent 6 }} -{{ end }} \ No newline at end of file + monitoring: + enablePodMonitor: {{ and .Values.pooler.monitoring.enabled .Values.pooler.monitoring.podMonitor.enabled }} + {{- with .Values.pooler.template }} + template: + {{- . | toYaml | nindent 4 }} + {{- end }} +{{ end }} diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index c85cced3b..3ee174fdc 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -256,6 +256,22 @@ "instances": { "type": "integer" }, + "monitoring": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "podMonitor": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + } + } + }, "parameters": { "type": "object", "properties": { @@ -269,6 +285,12 @@ }, "poolMode": { "type": "string" + }, + "template": { + "type": "object" + }, + "type": { + "type": "string" } } }, diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index af9062b6a..07d979a4c 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -82,7 +82,7 @@ cluster: # More info: https://kubernetes.io/docs/concepts/containers/images#updating-images imagePullPolicy: IfNotPresent - # -- The list of pull secrets to be used to pull the images + # -- The list of pull secrets to be used to pull the images. # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-LocalObjectReference imagePullSecrets: [] @@ -121,12 +121,12 @@ cluster: # -- The instances' log level, one of the following values: error, warning, info (default), debug, trace logLevel: "info" - # -- Affinity/Anti-affinity rules for Pods + # -- Affinity/Anti-affinity rules for Pods. # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-AffinityConfiguration affinity: topologyKey: topology.kubernetes.io/zone - # -- The configuration for the CA and related certificates + # -- The configuration for the CA and related certificates. # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-CertificatesConfiguration certificates: @@ -138,11 +138,15 @@ cluster: superuserSecret: "" monitoring: + # -- Whether to enable monitoring enabled: false podMonitor: + # -- Whether to enable the PodMonitor enabled: true prometheusRule: + # -- Whether to enable the PrometheusRule automated alerts enabled: true + # -- Custom Prometheus metrics customQueries: [] # - name: "pg_cache_hit_ratio" # query: "SELECT current_database() as datname, sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratio FROM pg_statio_user_tables;" @@ -154,12 +158,12 @@ cluster: # usage: GAUGE # description: "Cache hit ratio" - # -- Configuration of the PostgreSQL server + # -- Configuration of the PostgreSQL server. # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration postgresql: {} # max_connections: 300 - # -- BootstrapInitDB is the configuration of the bootstrap process when initdb is used + # -- BootstrapInitDB is the configuration of the bootstrap process when initdb is used. # See: https://cloudnative-pg.io/documentation/current/bootstrap/ # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-bootstrapinitdb initdb: {} @@ -240,6 +244,8 @@ backups: pooler: # -- Whether to enable PgBouncer enabled: false + # -- PgBouncer type of service to forward traffic to. + type: rw # -- PgBouncer pooling mode poolMode: transaction # -- Number of PgBouncer instances @@ -248,3 +254,14 @@ pooler: parameters: max_client_conn: "1000" default_pool_size: "25" + + monitoring: + # -- Whether to enable monitoring + enabled: false + podMonitor: + # -- Whether to enable the PodMonitor + enabled: true + + # -- Custom PgBouncer deployment template. + # Use to override image, specify resources, etc. + template: {} From 32a3b6c2a6062b320ab5033c31464d2e47c775b0 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sun, 17 Mar 2024 18:26:26 +0200 Subject: [PATCH 42/87] Bug Fix: CI Pooler job not querying the correct information Signed-off-by: Itay Grudev --- .github/actions/verify-pooler-ready/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/verify-pooler-ready/action.yml b/.github/actions/verify-pooler-ready/action.yml index d2c48200a..38a7f493b 100644 --- a/.github/actions/verify-pooler-ready/action.yml +++ b/.github/actions/verify-pooler-ready/action.yml @@ -22,7 +22,7 @@ runs: echo "Pooler not ready" exit 1 fi - READY_INSTANCES=$(kubectl get cluster ${INPUT_POOLER_NAME} -o jsonpath='{.status.readyInstances}') + READY_INSTANCES=$(kubectl get deployments.apps ${INPUT_POOLER_NAME} -o jsonpath='{.status.readyReplicas}') if [[ "$READY_INSTANCES" == ${INPUT_READY_INSTANCES} ]]; then echo "Pooler up and running" break From 8692cbed524951669c4a59dd1d718f9219501aa4 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sat, 23 Mar 2024 15:38:03 +0200 Subject: [PATCH 43/87] Bug Fix: Release process tried to sign prov files in addition to the chart file (#227) Additionally - using variables to pull the repository name instead of hard-coding it. Signed-off-by: Itay Grudev --- .github/workflows/release-publish.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 5903c9615..571919300 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -71,14 +71,14 @@ jobs: # would be preserved, causing a non-zero exit. Set nullglob to fix this run: | shopt -s nullglob - for pkg in .cr-release-packages/*; do + for pkg in .cr-release-packages/*.tgz; do if [ -z "${pkg:-}" ]; then break fi - helm push "${pkg}" oci://ghcr.io/"${GITHUB_REPOSITORY_OWNER}"/charts + helm push "${pkg}" oci://ghcr.io/"${GITHUB_REPOSITORY}" file=${pkg##*/} name=${file%-*} version=${file%.*} version=${version##*-} - cosign sign --yes ghcr.io/"${GITHUB_REPOSITORY_OWNER}"/charts/"${name}":"${version}" + cosign sign --yes ghcr.io/"${GITHUB_REPOSITORY}"/"${name}":"${version}" done From eb9844b22599beea69e014ac332a3b258c5c299c Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sun, 24 Mar 2024 23:56:21 +0200 Subject: [PATCH 44/87] feat(charts/cloudnative-pg): Grafana dashboard as a dependency (#225) This PR switches the operator's chart to reference the Grafana dashboard from https://github.com/cloudnative-pg/grafana-dashboards, instead of embedding it here. Signed-off-by: Itay Grudev --- .github/actions/deploy-operator/action.yml | 12 +- .github/workflows/lint.yml | 2 +- .gitignore | 3 + charts/cloudnative-pg/Chart.lock | 6 + charts/cloudnative-pg/Chart.yaml | 6 + charts/cloudnative-pg/README.md | 12 +- .../monitoring/grafana-dashboard.json | 6453 +---------------- .../templates/grafana-dashboard.yaml | 12 - charts/cloudnative-pg/values.schema.json | 6 + charts/cloudnative-pg/values.yaml | 8 +- 10 files changed, 50 insertions(+), 6470 deletions(-) create mode 100644 charts/cloudnative-pg/Chart.lock delete mode 100644 charts/cloudnative-pg/templates/grafana-dashboard.yaml diff --git a/.github/actions/deploy-operator/action.yml b/.github/actions/deploy-operator/action.yml index a998f06eb..a1fea523d 100644 --- a/.github/actions/deploy-operator/action.yml +++ b/.github/actions/deploy-operator/action.yml @@ -5,6 +5,12 @@ runs: steps: - name: Deploy the operator shell: bash - run: | - helm upgrade --install cnpg --namespace cnpg-system \ - --create-namespace charts/cloudnative-pg --wait + run: + helm dependency update charts/cloudnative-pg + + helm upgrade + --install + --namespace cnpg-system + --create-namespace + --wait + cnpg charts/cloudnative-pg diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9e52d5051..2f836614a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -37,4 +37,4 @@ jobs: - name: Run chart-testing (lint) run: | - ct lint --target-branch=main --check-version-increment=false + ct lint --chart-repos cnpg-grafana=https://cloudnative-pg.github.io/grafana-dashboards --target-branch=main --check-version-increment=false diff --git a/.gitignore b/.gitignore index a3b02f43d..2be477bd4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Ignore Helm subcharts +charts/**/charts/*.tgz + # Binaries for programs and plugins *.exe *.exe~ diff --git a/charts/cloudnative-pg/Chart.lock b/charts/cloudnative-pg/Chart.lock new file mode 100644 index 000000000..16719e97b --- /dev/null +++ b/charts/cloudnative-pg/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: cluster + repository: https://cloudnative-pg.github.io/grafana-dashboards + version: 0.0.1 +digest: sha256:f2055d3b9f52be6989a8285cb736ec555f91f133791382787f4beb4387175ca0 +generated: "2024-03-23T16:27:46.965208992+02:00" diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index b5b7763b2..a0024420c 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -36,3 +36,9 @@ home: https://cloudnative-pg.io maintainers: - name: phisco email: p.scorsolini@gmail.com +dependencies: + - name: cluster + alias: monitoring + condition: monitoring.grafanaDashboard.create + version: "0.0" + repository: https://cloudnative-pg.github.io/grafana-dashboards diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index c0ecd7f4c..a58caadb3 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -16,6 +16,12 @@ CloudNativePG Operator Helm Chart * +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| https://cloudnative-pg.github.io/grafana-dashboards | monitoring(cluster) | 0.0 | + ## Values | Key | Type | Default | Description | @@ -35,11 +41,13 @@ CloudNativePG Operator Helm Chart | image.repository | string | `"ghcr.io/cloudnative-pg/cloudnative-pg"` | | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | | imagePullSecrets | list | `[]` | | +| monitoring.grafanaDashboard.annotations | object | `{}` | Annotations that ConfigMaps can have to get configured in Grafana. | | monitoring.grafanaDashboard.configMapName | string | `"cnpg-grafana-dashboard"` | The name of the ConfigMap containing the dashboard. | | monitoring.grafanaDashboard.create | bool | `false` | | +| monitoring.grafanaDashboard.labels | object | `{}` | Labels that ConfigMaps should have to get configured in Grafana. | | monitoring.grafanaDashboard.namespace | string | `""` | Allows overriding the namespace where the ConfigMap will be created, defaulting to the same one as the Release. | -| monitoring.grafanaDashboard.sidecarLabel | string | `"grafana_dashboard"` | Label that ConfigMaps should have to be loaded as dashboards. | -| monitoring.grafanaDashboard.sidecarLabelValue | string | `"1"` | Label value that ConfigMaps should have to be loaded as dashboards. | +| monitoring.grafanaDashboard.sidecarLabel | string | `"grafana_dashboard"` | Label that ConfigMaps should have to be loaded as dashboards. DEPRECATED: Use labels instead. | +| monitoring.grafanaDashboard.sidecarLabelValue | string | `"1"` | Label value that ConfigMaps should have to be loaded as dashboards. DEPRECATED: Use labels instead. | | monitoring.podMonitorEnabled | bool | `false` | Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. | | monitoringQueriesConfigMap.name | string | `"cnpg-default-monitoring"` | The name of the default monitoring configmap. | | monitoringQueriesConfigMap.queries | string | `"backends:\n query: |\n SELECT sa.datname\n , sa.usename\n , sa.application_name\n , states.state\n , COALESCE(sa.count, 0) AS total\n , COALESCE(sa.max_tx_secs, 0) AS max_tx_duration_seconds\n FROM ( VALUES ('active')\n , ('idle')\n , ('idle in transaction')\n , ('idle in transaction (aborted)')\n , ('fastpath function call')\n , ('disabled')\n ) AS states(state)\n LEFT JOIN (\n SELECT datname\n , state\n , usename\n , COALESCE(application_name, '') AS application_name\n , COUNT(*)\n , COALESCE(EXTRACT (EPOCH FROM (max(now() - xact_start))), 0) AS max_tx_secs\n FROM pg_catalog.pg_stat_activity\n GROUP BY datname, state, usename, application_name\n ) sa ON states.state = sa.state\n WHERE sa.usename IS NOT NULL\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - usename:\n usage: \"LABEL\"\n description: \"Name of the user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - state:\n usage: \"LABEL\"\n description: \"State of the backend\"\n - total:\n usage: \"GAUGE\"\n description: \"Number of backends\"\n - max_tx_duration_seconds:\n usage: \"GAUGE\"\n description: \"Maximum duration of a transaction in seconds\"\n\nbackends_waiting:\n query: |\n SELECT count(*) AS total\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks\n ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n WHERE NOT blocked_locks.granted\n metrics:\n - total:\n usage: \"GAUGE\"\n description: \"Total number of backends that are currently waiting on other queries\"\n\npg_database:\n query: |\n SELECT datname\n , pg_catalog.pg_database_size(datname) AS size_bytes\n , pg_catalog.age(datfrozenxid) AS xid_age\n , pg_catalog.mxid_age(datminmxid) AS mxid_age\n FROM pg_catalog.pg_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - size_bytes:\n usage: \"GAUGE\"\n description: \"Disk space used by the database\"\n - xid_age:\n usage: \"GAUGE\"\n description: \"Number of transactions from the frozen XID to the current one\"\n - mxid_age:\n usage: \"GAUGE\"\n description: \"Number of multiple transactions (Multixact) from the frozen XID to the current one\"\n\npg_postmaster:\n query: |\n SELECT EXTRACT(EPOCH FROM pg_postmaster_start_time) AS start_time\n FROM pg_catalog.pg_postmaster_start_time()\n metrics:\n - start_time:\n usage: \"GAUGE\"\n description: \"Time at which postgres started (based on epoch)\"\n\npg_replication:\n query: \"SELECT CASE WHEN (\n NOT pg_catalog.pg_is_in_recovery()\n OR pg_catalog.pg_last_wal_receive_lsn() = pg_catalog.pg_last_wal_replay_lsn())\n THEN 0\n ELSE GREATEST (0,\n EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp())))\n END AS lag,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up,\n (SELECT count(*) FROM pg_catalog.pg_stat_replication) AS streaming_replicas\"\n metrics:\n - lag:\n usage: \"GAUGE\"\n description: \"Replication lag behind primary in seconds\"\n - in_recovery:\n usage: \"GAUGE\"\n description: \"Whether the instance is in recovery\"\n - is_wal_receiver_up:\n usage: \"GAUGE\"\n description: \"Whether the instance wal_receiver is up\"\n - streaming_replicas:\n usage: \"GAUGE\"\n description: \"Number of streaming replicas connected to the instance\"\n\npg_replication_slots:\n query: |\n SELECT slot_name,\n slot_type,\n database,\n active,\n (CASE pg_catalog.pg_is_in_recovery()\n WHEN TRUE THEN pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_last_wal_receive_lsn(), restart_lsn)\n ELSE pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn)\n END) as pg_wal_lsn_diff\n FROM pg_catalog.pg_replication_slots\n WHERE NOT temporary\n metrics:\n - slot_name:\n usage: \"LABEL\"\n description: \"Name of the replication slot\"\n - slot_type:\n usage: \"LABEL\"\n description: \"Type of the replication slot\"\n - database:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - active:\n usage: \"GAUGE\"\n description: \"Flag indicating whether the slot is active\"\n - pg_wal_lsn_diff:\n usage: \"GAUGE\"\n description: \"Replication lag in bytes\"\n\npg_stat_archiver:\n query: |\n SELECT archived_count\n , failed_count\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_archived_time)), -1) AS seconds_since_last_archival\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_failed_time)), -1) AS seconds_since_last_failure\n , COALESCE(EXTRACT(EPOCH FROM last_archived_time), -1) AS last_archived_time\n , COALESCE(EXTRACT(EPOCH FROM last_failed_time), -1) AS last_failed_time\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_archived_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_archived_wal_start_lsn\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_failed_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_failed_wal_start_lsn\n , EXTRACT(EPOCH FROM stats_reset) AS stats_reset_time\n FROM pg_catalog.pg_stat_archiver\n metrics:\n - archived_count:\n usage: \"COUNTER\"\n description: \"Number of WAL files that have been successfully archived\"\n - failed_count:\n usage: \"COUNTER\"\n description: \"Number of failed attempts for archiving WAL files\"\n - seconds_since_last_archival:\n usage: \"GAUGE\"\n description: \"Seconds since the last successful archival operation\"\n - seconds_since_last_failure:\n usage: \"GAUGE\"\n description: \"Seconds since the last failed archival operation\"\n - last_archived_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving succeeded\"\n - last_failed_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving failed\"\n - last_archived_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Archived WAL start LSN\"\n - last_failed_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Last failed WAL LSN\"\n - stats_reset_time:\n usage: \"GAUGE\"\n description: \"Time at which these statistics were last reset\"\n\npg_stat_bgwriter:\n query: |\n SELECT checkpoints_timed\n , checkpoints_req\n , checkpoint_write_time\n , checkpoint_sync_time\n , buffers_checkpoint\n , buffers_clean\n , maxwritten_clean\n , buffers_backend\n , buffers_backend_fsync\n , buffers_alloc\n FROM pg_catalog.pg_stat_bgwriter\n metrics:\n - checkpoints_timed:\n usage: \"COUNTER\"\n description: \"Number of scheduled checkpoints that have been performed\"\n - checkpoints_req:\n usage: \"COUNTER\"\n description: \"Number of requested checkpoints that have been performed\"\n - checkpoint_write_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds\"\n - checkpoint_sync_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds\"\n - buffers_checkpoint:\n usage: \"COUNTER\"\n description: \"Number of buffers written during checkpoints\"\n - buffers_clean:\n usage: \"COUNTER\"\n description: \"Number of buffers written by the background writer\"\n - maxwritten_clean:\n usage: \"COUNTER\"\n description: \"Number of times the background writer stopped a cleaning scan because it had written too many buffers\"\n - buffers_backend:\n usage: \"COUNTER\"\n description: \"Number of buffers written directly by a backend\"\n - buffers_backend_fsync:\n usage: \"COUNTER\"\n description: \"Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)\"\n - buffers_alloc:\n usage: \"COUNTER\"\n description: \"Number of buffers allocated\"\n\npg_stat_database:\n query: |\n SELECT datname\n , xact_commit\n , xact_rollback\n , blks_read\n , blks_hit\n , tup_returned\n , tup_fetched\n , tup_inserted\n , tup_updated\n , tup_deleted\n , conflicts\n , temp_files\n , temp_bytes\n , deadlocks\n , blk_read_time\n , blk_write_time\n FROM pg_catalog.pg_stat_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of this database\"\n - xact_commit:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been committed\"\n - xact_rollback:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been rolled back\"\n - blks_read:\n usage: \"COUNTER\"\n description: \"Number of disk blocks read in this database\"\n - blks_hit:\n usage: \"COUNTER\"\n description: \"Number of times disk blocks were found already in the buffer cache, so that a read was not necessary (this only includes hits in the PostgreSQL buffer cache, not the operating system's file system cache)\"\n - tup_returned:\n usage: \"COUNTER\"\n description: \"Number of rows returned by queries in this database\"\n - tup_fetched:\n usage: \"COUNTER\"\n description: \"Number of rows fetched by queries in this database\"\n - tup_inserted:\n usage: \"COUNTER\"\n description: \"Number of rows inserted by queries in this database\"\n - tup_updated:\n usage: \"COUNTER\"\n description: \"Number of rows updated by queries in this database\"\n - tup_deleted:\n usage: \"COUNTER\"\n description: \"Number of rows deleted by queries in this database\"\n - conflicts:\n usage: \"COUNTER\"\n description: \"Number of queries canceled due to conflicts with recovery in this database\"\n - temp_files:\n usage: \"COUNTER\"\n description: \"Number of temporary files created by queries in this database\"\n - temp_bytes:\n usage: \"COUNTER\"\n description: \"Total amount of data written to temporary files by queries in this database\"\n - deadlocks:\n usage: \"COUNTER\"\n description: \"Number of deadlocks detected in this database\"\n - blk_read_time:\n usage: \"COUNTER\"\n description: \"Time spent reading data file blocks by backends in this database, in milliseconds\"\n - blk_write_time:\n usage: \"COUNTER\"\n description: \"Time spent writing data file blocks by backends in this database, in milliseconds\"\n\npg_stat_replication:\n primary: true\n query: |\n SELECT usename\n , COALESCE(application_name, '') AS application_name\n , COALESCE(client_addr::text, '') AS client_addr\n , COALESCE(client_port::text, '') AS client_port\n , EXTRACT(EPOCH FROM backend_start) AS backend_start\n , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), write_lsn) AS write_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), flush_lsn) AS flush_diff_bytes\n , COALESCE(pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), replay_lsn),0) AS replay_diff_bytes\n , COALESCE((EXTRACT(EPOCH FROM write_lag)),0)::float AS write_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM flush_lag)),0)::float AS flush_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM replay_lag)),0)::float AS replay_lag_seconds\n FROM pg_catalog.pg_stat_replication\n metrics:\n - usename:\n usage: \"LABEL\"\n description: \"Name of the replication user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - client_addr:\n usage: \"LABEL\"\n description: \"Client IP address\"\n - client_port:\n usage: \"LABEL\"\n description: \"Client TCP port\"\n - backend_start:\n usage: \"COUNTER\"\n description: \"Time when this process was started\"\n - backend_xmin_age:\n usage: \"COUNTER\"\n description: \"The age of this standby's xmin horizon\"\n - sent_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location sent on this connection\"\n - write_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location written to disk by this standby server\"\n - flush_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location flushed to disk by this standby server\"\n - replay_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location replayed into the database on this standby server\"\n - write_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written it\"\n - flush_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it\"\n - replay_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written, flushed and applied it\"\n\npg_settings:\n query: |\n SELECT name,\n CASE setting WHEN 'on' THEN '1' WHEN 'off' THEN '0' ELSE setting END AS setting\n FROM pg_catalog.pg_settings\n WHERE vartype IN ('integer', 'real', 'bool')\n ORDER BY 1\n metrics:\n - name:\n usage: \"LABEL\"\n description: \"Name of the setting\"\n - setting:\n usage: \"GAUGE\"\n description: \"Setting value\"\n"` | A string representation of a YAML defining monitoring queries. | diff --git a/charts/cloudnative-pg/monitoring/grafana-dashboard.json b/charts/cloudnative-pg/monitoring/grafana-dashboard.json index 13346f51d..8c4813056 100644 --- a/charts/cloudnative-pg/monitoring/grafana-dashboard.json +++ b/charts/cloudnative-pg/monitoring/grafana-dashboard.json @@ -1,6450 +1,3 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 1, - "links": [ - { - "asDropdown": false, - "icon": "external link", - "includeVars": false, - "keepTime": false, - "tags": [ - "cloudnativepg" - ], - "targetBlank": false, - "title": "Related Dashboards", - "tooltip": "", - "type": "dashboards", - "url": "" - } - ], - "liveNow": false, - "panels": [ - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 563, - "panels": [], - "title": "Row title", - "type": "row" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 562, - "panels": [], - "title": "Summary", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 7, - "w": 3, - "x": 0, - "y": 2 - }, - "id": 334, - "options": { - "alertInstanceLabelFilter": "{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", - "alertName": "", - "dashboardAlerts": false, - "folder": "", - "groupBy": [], - "groupMode": "default", - "maxItems": 20, - "sortOrder": 1, - "stateFilter": { - "error": true, - "firing": true, - "noData": false, - "normal": true, - "pending": true - }, - "viewMode": "list" - }, - "title": "Alerts", - "type": "alertlist" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 15, - "x": 3, - "y": 2 - }, - "id": 336, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "markdown" - }, - "pluginVersion": "10.2.2", - "title": "Overview", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 18, - "y": 2 - }, - "id": 352, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "markdown" - }, - "pluginVersion": "10.2.2", - "title": "Storage", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 21, - "y": 2 - }, - "id": 354, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "markdown" - }, - "pluginVersion": "10.2.2", - "title": "Backups", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-blue", - "value": null - } - ] - }, - "unit": "dateTimeFromNow" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 3, - "y": 3 - }, - "id": 338, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "max(cnpg_pg_postmaster_start_time{namespace=~\"$namespace\",pod=~\"$instances\"})*1000", - "format": "time_series", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A" - } - ], - "title": "Last failover", - "transformations": [], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 3, - "x": 5, - "y": 3 - }, - "id": 342, - "interval": "1m", - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "sum(rate(cnpg_pg_stat_database_xact_commit{namespace=~\"$namespace\",pod=~\"$instances\"}[$__interval])) + sum(rate(cnpg_pg_stat_database_xact_rollback{namespace=~\"$namespace\",pod=~\"$instances\"}[$__interval]))", - "interval": "", - "legendFormat": "TPS", - "range": true, - "refId": "TPS" - } - ], - "title": "TPS", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "CPU Utilisation from Requests", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "orange", - "value": 0.8 - }, - { - "color": "red", - "value": 0.9 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 8, - "y": 3 - }, - "id": 344, - "interval": "1m", - "links": [], - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{ namespace=\"$namespace\"}) / sum(kube_pod_container_resource_requests{job=\"kube-state-metrics\", namespace=\"$namespace\", resource=\"cpu\"})", - "format": "time_series", - "instant": true, - "intervalFactor": 2, - "refId": "A" - } - ], - "title": "CPU Utilisation", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "Memory Utilisation from Requests", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "orange", - "value": 0.8 - }, - { - "color": "red", - "value": 0.9 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 11, - "y": 3 - }, - "id": 348, - "interval": "1m", - "links": [], - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "sum(container_memory_working_set_bytes{job=\"kubelet\", metrics_path=\"/metrics/cadvisor\", namespace=\"$namespace\",container!=\"\", image!=\"\"}) / sum(max by(pod) (kube_pod_container_resource_requests{job=\"kube-state-metrics\", namespace=\"$namespace\", resource=\"memory\"}))", - "format": "time_series", - "instant": true, - "intervalFactor": 2, - "refId": "A" - } - ], - "title": "Memory Utilisation", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "max": 30, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "yellow", - "value": 1 - }, - { - "color": "orange", - "value": 10 - }, - { - "color": "red", - "value": 20 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 14, - "y": 3 - }, - "id": 465, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max(cnpg_pg_replication_lag{namespace=~\"$namespace\",pod=~\"$instances\"})", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Replication Lag", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "yellow", - "value": 1 - }, - { - "color": "orange", - "value": 10 - }, - { - "color": "red", - "value": 20 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 16, - "y": 3 - }, - "id": 467, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max(cnpg_pg_stat_replication_write_lag_seconds{namespace=~\"$namespace\",pod=~\"$instances\"})", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Write Lag", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 2, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 60000000000 - }, - { - "color": "red", - "value": 80000000000 - } - ] - }, - "unit": "decbytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 3, - "x": 18, - "y": 3 - }, - "id": 358, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "sum" - ], - "fields": "", - "values": false - }, - "textMode": "value", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "cnpg_pg_database_size_bytes{namespace=\"$namespace\"}", - "format": "table", - "instant": true, - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Database Size", - "transformations": [ - { - "id": "groupBy", - "options": { - "fields": { - "Value": { - "aggregations": [ - "max" - ], - "operation": "aggregate" - }, - "datname": { - "aggregations": [], - "operation": "groupby" - } - } - } - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "Elapsed time since the last successful base backup.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "from": 1, - "result": { - "color": "semi-dark-orange", - "index": 0, - "text": "Invalid date" - }, - "to": 1e+42 - }, - "type": "range" - }, - { - "options": { - "from": -2147483648, - "result": { - "color": "red", - "index": 1, - "text": "N/A" - }, - "to": -1577847600 - }, - "type": "range" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "semi-dark-red", - "value": -108000 - }, - { - "color": "semi-dark-orange", - "value": -107999 - }, - { - "color": "#EAB839", - "value": -89999 - }, - { - "color": "green", - "value": -86399 - } - ] - }, - "unit": "dtdurations" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 3, - "x": 21, - "y": 3 - }, - "id": 360, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "-(time() - max(cnpg_collector_last_available_backup_timestamp{namespace=\"$namespace\",pod=~\"$instances\"}))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Last Base Backup", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "orange", - "value": 0.8 - }, - { - "color": "red", - "value": 0.9 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 18, - "y": 5 - }, - "id": 356, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"} / kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"}))", - "format": "time_series", - "interval": "", - "legendFormat": "DATA", - "range": true, - "refId": "DATA" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"} / kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"}))", - "format": "time_series", - "interval": "", - "legendFormat": "WAL", - "range": true, - "refId": "WAL" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max(\n sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-tbs.*\"}) \n /\n sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-tbs.*\"}) \n *\n on(namespace, persistentvolumeclaim) group_left(volume)\n kube_pod_spec_volumes_persistentvolumeclaims_info{pod=~\"$instances\"}\n)", - "hide": false, - "instant": false, - "legendFormat": "Tablespaces (max)", - "range": true, - "refId": "Max Tablespace" - } - ], - "title": "Volume Space Usage", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "Computes the time since the last known WAL archival in the primary.\nWe ensure to ignore the metric in the replicas by using (1 - cnpg_pg_replication_in_recovery ) as a multiplicative factor. It will be 0 for replicas, 1 for the primary.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "color": "red", - "index": 0, - "text": "No backups" - } - }, - "type": "special" - }, - { - "options": { - "from": -1e+22, - "result": { - "color": "text", - "index": 1, - "text": "No data" - }, - "to": 0 - }, - "type": "range" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "dtdurations" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 3, - "x": 21, - "y": 5 - }, - "id": 362, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "max((1 - cnpg_pg_replication_in_recovery{namespace=~\"$namespace\",pod=~\"$instances\"}) * (time() - timestamp(cnpg_pg_stat_archiver_seconds_since_last_archival{namespace=~\"$namespace\",pod=~\"$instances\"}) +\ncnpg_pg_stat_archiver_seconds_since_last_archival{namespace=~\"$namespace\",pod=~\"$instances\"}))", - "format": "time_series", - "interval": "", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Last archived WAL", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-blue", - "value": null - } - ] - }, - "unit": "string" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 3, - "y": 6 - }, - "id": 340, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^full$/", - "values": false - }, - "text": {}, - "textMode": "value", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "cnpg_collector_postgres_version{namespace=~\"$namespace\",pod=~\"$instances\"}", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{pod}}", - "range": false, - "refId": "A" - } - ], - "title": "Version", - "transformations": [], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "yellow", - "value": 1 - }, - { - "color": "orange", - "value": 10 - }, - { - "color": "red", - "value": 20 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 14, - "y": 6 - }, - "id": 466, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max(cnpg_pg_stat_replication_flush_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"})", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Flush Lag", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "yellow", - "value": 1 - }, - { - "color": "orange", - "value": 10 - }, - { - "color": "red", - "value": 20 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 16, - "y": 6 - }, - "id": 468, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max(cnpg_pg_stat_replication_replay_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"})", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Replay Lag", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 2, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 80000000000 - }, - { - "color": "red", - "value": 90000000000 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 3, - "x": 8, - "y": 7 - }, - "id": 346, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "center", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{ namespace=\"$namespace\"})", - "hide": false, - "interval": "", - "legendFormat": "Total", - "range": true, - "refId": "B" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "Excluding cache", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 80000000000 - }, - { - "color": "red", - "value": 90000000000 - } - ] - }, - "unit": "decbytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 3, - "x": 11, - "y": 7 - }, - "id": 350, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "center", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "sum(container_memory_working_set_bytes{pod=~\"$instances\", namespace=\"$namespace\", container!=\"\", image!=\"\"})", - "hide": false, - "interval": "", - "legendFormat": "Total", - "range": true, - "refId": "B" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "0": { - "color": "red", - "index": 1, - "text": "N/A" - } - }, - "type": "value" - }, - { - "options": { - "match": "null", - "result": { - "color": "red", - "index": 0, - "text": "No backups" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "dateTimeAsIso" - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 3, - "x": 21, - "y": 7 - }, - "id": 364, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "max(cnpg_collector_first_recoverability_point{namespace=~\"$namespace\",pod=~\"$instances\"})*1000", - "format": "time_series", - "interval": "", - "legendFormat": "{{pod}}", - "range": true, - "refId": "A" - } - ], - "title": "First Recoverability Point", - "type": "stat" - }, - { - "collapsed": true, - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 9 - }, - "id": 12, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 0, - "y": 20 - }, - "id": 191, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Instance", - "transparent": true, - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 2, - "x": 3, - "y": 20 - }, - "id": 192, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Status", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 5, - "y": 20 - }, - "id": 193, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Clustering / replicas", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 2, - "x": 8, - "y": 20 - }, - "id": 384, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Zone", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 4, - "x": 10, - "y": 20 - }, - "id": 195, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Connections", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 14, - "y": 20 - }, - "id": 196, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Max Connections", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "gridPos": { - "h": 1, - "w": 3, - "x": 17, - "y": 20 - }, - "id": 197, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Wraparound", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 2, - "x": 20, - "y": 20 - }, - "id": 313, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Started", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 2, - "x": 22, - "y": 20 - }, - "id": 198, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Version", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 0, - "y": 21 - }, - "id": 61, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "\n \n

    $instances

    \n
    ", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "0": { - "index": 0, - "text": "Down" - }, - "1": { - "index": 1, - "text": "Up" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-red" - }, - { - "color": "green", - "value": 1 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 3, - "y": 21 - }, - "id": 33, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "min(kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"})", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "0": { - "color": "red", - "index": 1, - "text": "No" - }, - "1": { - "color": "green", - "index": 0, - "text": "Yes" - } - }, - "type": "value" - } - ], - "noValue": "-", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 5, - "y": 21 - }, - "id": 60, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "1 - cnpg_pg_replication_in_recovery{namespace=~\"$namespace\",pod=~\"$instances\"} + cnpg_pg_replication_is_wal_receiver_up{namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "transformations": [], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "noValue": "-", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 1, - "x": 7, - "y": 21 - }, - "id": 229, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_replication_streaming_replicas{namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "transformations": [], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "This metric depends on exporting the: `topology.kubernetes.io/zone` label through kube-state-metrics (not enabled by default). Can be added by changing its configuration with:\n\n```yaml\nmetricLabelsAllowlist:\n - nodes=[topology.kubernetes.io/zone]\n```", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "blue" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 8, - "y": 21 - }, - "id": 386, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^label_topology_kubernetes_io_zone$/", - "values": false - }, - "text": { - "valueSize": 18 - }, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "kube_pod_info{namespace=~\"$namespace\",pod=~\"$instances\"} * on(node,instance) group_left(label_topology_kubernetes_io_zone) kube_node_labels", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "transformations": [], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 10, - "y": 21 - }, - "id": 58, - "options": { - "legend": { - "calcs": [ - "last", - "mean" - ], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "8.2.1", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum by (pod) (cnpg_backends_total{namespace=~\"$namespace\",pod=~\"$instances\"})", - "instant": false, - "interval": "", - "legendFormat": "-", - "refId": "A" - } - ], - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 0, - "mappings": [], - "max": 100, - "min": 0, - "noValue": "<1%", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "#EAB839", - "value": 75 - }, - { - "color": "red", - "value": 90 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 14, - "y": 21 - }, - "id": 32, - "options": { - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "100 * sum by (pod) (cnpg_backends_total{namespace=~\"$namespace\",pod=~\"$instances\"}) / sum by (pod) (cnpg_pg_settings_setting{name=\"max_connections\",namespace=~\"$namespace\",pod=~\"$instances\"})", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "max": 2147483647, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "#EAB839", - "value": 200000000 - }, - { - "color": "red", - "value": 1000000000 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 17, - "y": 21 - }, - "id": 8, - "options": { - "displayMode": "lcd", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showUnfilled": true, - "text": {}, - "valueMode": "color" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "max by (pod) (cnpg_pg_database_xid_age{namespace=~\"$namespace\",pod=~\"$instances\"})", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "bargauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-blue" - } - ] - }, - "unit": "dateTimeFromNow" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 20, - "y": 21 - }, - "id": 314, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "cnpg_pg_postmaster_start_time{namespace=~\"$namespace\",pod=~\"$instances\"}*1000", - "format": "time_series", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A" - } - ], - "transformations": [], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-blue" - } - ] - }, - "unit": "string" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 22, - "y": 21 - }, - "id": 42, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^full$/", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": false, - "expr": "cnpg_collector_postgres_version{namespace=~\"$namespace\",pod=~\"$instances\"}", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "transformations": [], - "type": "stat" - } - ], - "targets": [ - { - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Server Health", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 10 - }, - "id": 41, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 0, - "y": 21 - }, - "id": 187, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Instance", - "transparent": true, - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 3, - "y": 21 - }, - "id": 183, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Max Connections", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 6, - "y": 21 - }, - "id": 184, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Shared Buffers", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 9, - "y": 21 - }, - "id": 185, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Effective Cache Size", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 12, - "y": 21 - }, - "id": 186, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Work Mem", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 15, - "y": 21 - }, - "id": 188, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Maintenance Work Mem", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 18, - "y": 21 - }, - "id": 189, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Random Page Cost", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 3, - "x": 21, - "y": 21 - }, - "id": 190, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Sequential Page Cost", - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 0, - "y": 22 - }, - "id": 86, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "\n \n

    $instances

    \n
    ", - "mode": "html" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "kube_pod_container_status_ready{container=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "text" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-purple" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 3, - "y": 22 - }, - "id": 30, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_settings_setting{name=\"max_connections\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-purple" - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 6, - "y": 22 - }, - "id": 24, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "max by (pod) (cnpg_pg_settings_setting{name=\"shared_buffers\",namespace=~\"$namespace\",pod=~\"$instances\"}) * max by (pod) (cnpg_pg_settings_setting{name=\"block_size\",namespace=~\"$namespace\",pod=~\"$instances\"})", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-purple" - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 9, - "y": 22 - }, - "id": 57, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "max by (pod) (cnpg_pg_settings_setting{name=\"effective_cache_size\",namespace=~\"$namespace\",pod=~\"$instances\"}) * max by (pod) (cnpg_pg_settings_setting{name=\"block_size\",namespace=~\"$namespace\",pod=~\"$instances\"})", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-purple" - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 12, - "y": 22 - }, - "id": 26, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_settings_setting{name=\"work_mem\",namespace=~\"$namespace\",pod=~\"$instances\"} * 1024", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-purple" - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 15, - "y": 22 - }, - "id": 47, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_settings_setting{name=\"maintenance_work_mem\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-purple" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 18, - "y": 22 - }, - "id": 48, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_settings_setting{name=\"random_page_cost\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-purple" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 21, - "y": 22 - }, - "id": 56, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "10.1.5", - "repeat": "instances", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_settings_setting{name=\"seq_page_cost\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "dark-purple" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 31 - }, - "id": 150, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "10.1.5", - "repeatDirection": "v", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_settings_setting{namespace=~\"$namespace\",pod=~\"$instances\"}", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Configurations", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "Time": true, - "__name__": true, - "container": true, - "endpoint": true, - "instance": true, - "job": true, - "name": false, - "namespace": true, - "pod": false - }, - "indexByName": { - "Time": 0, - "Value": 9, - "__name__": 1, - "container": 2, - "endpoint": 3, - "instance": 4, - "job": 5, - "name": 7, - "namespace": 8, - "pod": 6 - }, - "renameByName": { - "__name__": "", - "name": "parameter" - } - } - }, - { - "id": "groupingToMatrix", - "options": { - "columnField": "pod", - "rowField": "parameter", - "valueField": "Value" - } - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": {}, - "renameByName": { - "parameter\\pod": "parameter" - } - } - } - ], - "type": "table" - } - ], - "targets": [ - { - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Configuration", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 11 - }, - "id": 10, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 47 - }, - "hiddenSeries": false, - "id": 273, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 2, - "links": [], - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.4.7", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{pod=~\"$instances\", namespace=~\"$namespace\"}) by (pod)", - "format": "time_series", - "interval": "", - "intervalFactor": 2, - "legendFormat": "{{pod}}", - "refId": "A", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{pod=~\"$instances\", namespace=~\"$namespace\"})", - "hide": false, - "interval": "", - "legendFormat": "total", - "refId": "B" - } - ], - "thresholds": [], - "timeRegions": [], - "title": "CPU Usage", - "tooltip": { - "shared": true, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:189", - "format": "short", - "logBase": 1, - "min": 0, - "show": true - }, - { - "$$hashKey": "object:190", - "format": "short", - "logBase": 1, - "show": false - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fill": 2, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 47 - }, - "hiddenSeries": false, - "id": 275, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 2, - "links": [], - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.4.7", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "quota - requests", - "color": "#F2495C", - "dashes": true, - "fill": 0, - "hiddenSeries": true, - "hideTooltip": true, - "legend": true, - "linewidth": 2, - "stack": false - }, - { - "alias": "quota - limits", - "color": "#FF9830", - "dashes": true, - "fill": 0, - "hiddenSeries": true, - "hideTooltip": true, - "legend": true, - "linewidth": 2, - "stack": false - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(container_memory_working_set_bytes{pod=~\"$instances\", namespace=\"$namespace\", container!=\"\", image!=\"\"}) by (pod)", - "format": "time_series", - "interval": "", - "intervalFactor": 2, - "legendFormat": "{{pod}}", - "refId": "A", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(container_memory_working_set_bytes{pod=~\"$instances\", namespace=\"$namespace\", container!=\"\", image!=\"\"})", - "hide": false, - "interval": "", - "legendFormat": "total", - "refId": "B" - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Memory Usage (w/o cache)", - "tooltip": { - "shared": true, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:246", - "format": "bytes", - "logBase": 1, - "min": 0, - "show": true - }, - { - "$$hashKey": "object:247", - "format": "short", - "logBase": 1, - "show": false - } - ], - "yaxis": { - "align": false - } - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 54 - }, - "id": 39, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(cnpg_backends_total{namespace=~\"$namespace\",pod=~\"$instances\"}) by (pod)", - "hide": false, - "interval": "", - "legendFormat": "total ({{pod}})", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(cnpg_backends_total{namespace=~\"$namespace\",pod=~\"$instances\"}) by (state, pod)", - "interval": "", - "legendFormat": "{{state}} ({{pod}})", - "refId": "A" - } - ], - "title": "Session States", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 62 - }, - "id": 50, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(rate(cnpg_pg_stat_database_xact_commit{namespace=~\"$namespace\",pod=~\"$instances\"}[5m])) by (pod)", - "interval": "", - "legendFormat": "committed ({{pod}})", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(rate(cnpg_pg_stat_database_xact_rollback{namespace=~\"$namespace\",pod=~\"$instances\"}[5m])) by (pod)", - "hide": false, - "interval": "", - "legendFormat": "rolled back ({{pod}})", - "refId": "B" - } - ], - "title": "Transactions [5m]", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 62 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "max by (pod) (cnpg_backends_max_tx_duration_seconds{namespace=~\"$namespace\",pod=~\"$instances\"})", - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Longest Transaction", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 70 - }, - "id": 55, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "rate(cnpg_pg_stat_database_deadlocks{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "count ({{pod}})", - "refId": "B" - } - ], - "title": "Deadlocks [5m]", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 70 - }, - "id": 54, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_backends_waiting_total{namespace=~\"$namespace\",pod=~\"$instances\"}", - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Blocked Queries", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Operational Stats", - "type": "row" - }, - { - "collapsed": false, - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 12 - }, - "id": 35, - "panels": [], - "targets": [ - { - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Storage & I/O", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 0.7 - }, - { - "color": "red", - "value": 0.8 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 13 - }, - "id": 424, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"} / kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"})", - "format": "time_series", - "interval": "", - "legendFormat": "{{persistentvolumeclaim}}", - "range": true, - "refId": "FREE_SPACE" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"} / kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"})", - "format": "time_series", - "interval": "", - "legendFormat": "{{persistentvolumeclaim}}", - "range": true, - "refId": "FREE_SPACE_WAL" - } - ], - "title": "Volume Space Usage: PGDATA and WAL", - "transformations": [], - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 2, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 0.8 - }, - { - "color": "red", - "value": 0.9 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 13 - }, - "id": 426, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max by(persistentvolumeclaim) (kubelet_volume_stats_inodes_used{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"} / kubelet_volume_stats_inodes{namespace=\"$namespace\", persistentvolumeclaim=~\"$instances\"})", - "format": "time_series", - "interval": "", - "legendFormat": "{{persistentvolumeclaim}}", - "range": true, - "refId": "FREE_INODES" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "max by(persistentvolumeclaim) (kubelet_volume_stats_inodes_used{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"} / kubelet_volume_stats_inodes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-wal\"})", - "format": "time_series", - "interval": "", - "legendFormat": "{{persistentvolumeclaim}}", - "range": true, - "refId": "FREE_INODES_WAL" - } - ], - "title": "Volume Inode Usage: PGDATA and WAL", - "transformations": [], - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "#EAB839", - "value": 0.7 - }, - { - "color": "red", - "value": 0.8 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 21 - }, - "id": 564, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "10.1.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "expr": "sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-tbs.*\"}) \n/\nsum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace=\"$namespace\", persistentvolumeclaim=~\"(${instances})-tbs.*\"}) \n*\non(namespace, persistentvolumeclaim) group_left(volume,pod)\nkube_pod_spec_volumes_persistentvolumeclaims_info{pod=~\"$instances\"}", - "format": "time_series", - "interval": "", - "legendFormat": "{{volume}}-{{pod}}", - "range": true, - "refId": "FREE_SPACE" - } - ], - "title": "Volume Space Usage: Tablespaces", - "transformations": [], - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 28 - }, - "id": 44, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "sum(rate(cnpg_pg_stat_database_tup_deleted{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", - "interval": "", - "legendFormat": "deleted", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "sum(rate(cnpg_pg_stat_database_tup_inserted{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", - "hide": false, - "interval": "", - "legendFormat": "inserted", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "sum(rate(cnpg_pg_stat_database_tup_fetched{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", - "hide": false, - "interval": "", - "legendFormat": "fetched", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "sum(rate(cnpg_pg_stat_database_tup_returned{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", - "hide": false, - "interval": "", - "legendFormat": "returned", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "sum(rate(cnpg_pg_stat_database_tup_updated{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m]))", - "hide": false, - "interval": "", - "legendFormat": "updated", - "range": true, - "refId": "E" - } - ], - "title": "Tuple I/O [5m]", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 28 - }, - "id": 46, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "rate(cnpg_pg_stat_database_blks_hit{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", - "interval": "", - "legendFormat": "hit ({{pod}})", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "rate(cnpg_pg_stat_database_blks_read{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", - "hide": false, - "interval": "", - "legendFormat": "read ({{pod}})", - "range": true, - "refId": "B" - } - ], - "title": "Block I/O [5m]", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "decbytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 36 - }, - "id": 22, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "8.0.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "editorMode": "code", - "exemplar": true, - "expr": "max by (datname) (cnpg_pg_database_size_bytes{datname!~\"template.*\",datname!=\"postgres\",namespace=~\"$namespace\",pod=~\"$instances\"})", - "interval": "", - "legendFormat": " {{pod}}: {{datname}}", - "range": true, - "refId": "A" - } - ], - "title": "Database Size", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "decbytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 36 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "rate(cnpg_pg_stat_database_temp_bytes{datname=\"\",namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", - "instant": false, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Temp Bytes [5m]", - "type": "timeseries" - }, - { - "collapsed": true, - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 44 - }, - "id": 37, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 57 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_collector_pg_wal_archive_status{value=\"ready\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "interval": "", - "legendFormat": "ready ({{pod}})", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_collector_pg_wal_archive_status{value=\"done\",namespace=~\"$namespace\",pod=~\"$instances\"}", - "hide": false, - "interval": "", - "legendFormat": "done ({{pod}})", - "refId": "B" - } - ], - "title": "WAL Segment Archive Status", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 8, - "y": 57 - }, - "id": 52, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "rate(cnpg_pg_stat_archiver_archived_count{namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", - "interval": "", - "legendFormat": "archived ({{pod}})", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "rate(cnpg_pg_stat_archiver_failed_count{namespace=~\"$namespace\",pod=~\"$instances\"}[5m])", - "hide": false, - "interval": "", - "legendFormat": "failed ({{pod}})", - "refId": "B" - } - ], - "title": "Archiver Status [5m]", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 57 - }, - "id": 53, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_stat_archiver_seconds_since_last_archival{namespace=~\"$namespace\",pod=~\"$instances\"}", - "interval": "", - "legendFormat": "age ({{pod}})", - "refId": "A" - } - ], - "title": "Last Archive Age", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Write Ahead Log", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 45 - }, - "id": 18, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "line" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "#EAB839", - "value": 600 - }, - { - "color": "dark-red", - "value": 3600 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 21 - }, - "id": 16, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_replication_lag{namespace=~\"$namespace\",pod=~\"$instances\"}", - "instant": false, - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Replication Lag", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 21 - }, - "id": 14, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_stat_replication_write_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", - "instant": false, - "interval": "", - "legendFormat": "{{pod}} -> {{application_name}}", - "refId": "A" - } - ], - "title": "Write Lag", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 12, - "y": 21 - }, - "id": 59, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_stat_replication_flush_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", - "instant": false, - "interval": "", - "legendFormat": "{{pod}} -> {{application_name}}", - "refId": "A" - } - ], - "title": "Flush Lag", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 18, - "y": 21 - }, - "id": 20, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_stat_replication_replay_lag_seconds{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", - "interval": "", - "legendFormat": "{{pod}} -> {{application_name}}", - "refId": "A" - } - ], - "title": "Replay Lag", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Replication", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 46 - }, - "id": 231, - "panels": [ - { - "cards": {}, - "color": { - "cardColor": "#b4ff00", - "colorScale": "sqrt", - "colorScheme": "interpolateOranges", - "exponent": 0.5, - "mode": "spectrum" - }, - "dataFormat": "timeseries", - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "scaleDistribution": { - "type": "linear" - } - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 59 - }, - "heatmap": {}, - "hideZeroBuckets": false, - "highlightCards": true, - "id": 233, - "legend": { - "show": false - }, - "options": { - "calculate": true, - "calculation": {}, - "cellGap": 2, - "cellValues": {}, - "color": { - "exponent": 0.5, - "fill": "#b4ff00", - "mode": "scheme", - "reverse": false, - "scale": "exponential", - "scheme": "Oranges", - "steps": 128 - }, - "exemplars": { - "color": "rgba(255,0,255,0.7)" - }, - "filterValues": { - "le": 1e-9 - }, - "legend": { - "show": false - }, - "rowsFrame": { - "layout": "auto" - }, - "showValue": "never", - "tooltip": { - "show": true, - "yHistogram": false - }, - "yAxis": { - "axisPlacement": "left", - "reverse": false, - "unit": "s" - } - }, - "pluginVersion": "9.4.7", - "reverseYBuckets": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_collector_collection_duration_seconds{namespace=~\"$namespace\",pod=~\"$instances\"}", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Collection Duration", - "tooltip": { - "show": true, - "showHistogram": false - }, - "type": "heatmap", - "xAxis": { - "show": true - }, - "yAxis": { - "format": "s", - "logBase": 1, - "show": true - }, - "yBucketBound": "auto" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 59 - }, - "id": 235, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_collector_last_collection_error{namespace=~\"$namespace\",pod=~\"$instances\"}", - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "Errors", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Collector Stats", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 47 - }, - "id": 239, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "dateTimeAsIso" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 8, - "x": 0, - "y": 60 - }, - "id": 237, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_collector_first_recoverability_point{namespace=~\"$namespace\",pod=~\"$instances\"}*1000 > 0", - "format": "time_series", - "interval": "", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "title": "First Recoverability Point", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Backups", - "type": "row" - }, - { - "collapsed": true, - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 48 - }, - "id": 293, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": -1, - "drawStyle": "line", - "fillOpacity": 8, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 5, - "x": 0, - "y": 40 - }, - "id": 295, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "8.2.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_stat_bgwriter_checkpoints_req{namespace=~\"$namespace\",pod=~\"$instances\"}", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "req/{{pod}}", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_stat_bgwriter_checkpoints_timed{namespace=~\"$namespace\",pod=~\"$instances\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "timed/{{pod}}", - "refId": "A" - } - ], - "title": "Requested/Timed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": -1, - "drawStyle": "line", - "fillOpacity": 8, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 5, - "x": 5, - "y": 40 - }, - "id": 296, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "8.2.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_stat_bgwriter_checkpoint_write_time{namespace=~\"$namespace\",pod=~\"$instances\"}", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "write/{{pod}}", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "cnpg_pg_stat_bgwriter_checkpoint_sync_time{namespace=~\"$namespace\",pod=~\"$instances\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "sync/{{pod}}", - "refId": "A" - } - ], - "title": "Write/Sync time", - "type": "timeseries" - } - ], - "targets": [ - { - "datasource": { - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Checkpoints", - "type": "row" - } - ], - "refresh": "30s", - "revision": 1, - "schemaVersion": 38, - "tags": [ - "cloudnativepg" - ], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "default", - "value": "default" - }, - "hide": 0, - "includeAll": false, - "label": "Datasource", - "multi": false, - "name": "DS_PROMETHEUS", - "options": [], - "query": "prometheus", - "queryValue": "", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - }, - { - "current": { - "isNone": true, - "selected": false, - "text": "None", - "value": "" - }, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "cnpg_collector_up", - "hide": 0, - "includeAll": false, - "multi": false, - "name": "namespace", - "options": [], - "query": { - "query": "cnpg_collector_up", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "/namespace=\"(?[^\"]+)/g", - "skipUrlSync": false, - "sort": 0, - "type": "query" - }, - { - "current": { - "isNone": true, - "selected": false, - "text": "None", - "value": "" - }, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "cnpg_collector_up{namespace=~\"$namespace\"}", - "hide": 0, - "includeAll": false, - "multi": false, - "name": "cluster", - "options": [], - "query": { - "query": "cnpg_collector_up{namespace=~\"$namespace\"}", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "/\\bcluster\\b=\"(?[^\"]+)/g", - "skipUrlSync": false, - "sort": 1, - "type": "query" - }, - { - "current": { - "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] - }, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "cnpg_collector_up{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", - "hide": 0, - "includeAll": true, - "multi": true, - "name": "instances", - "options": [], - "query": { - "query": "cnpg_collector_up{namespace=~\"$namespace\",pod=~\"$cluster-[0-9]+$\"}", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "/pod=\"(?[^\"]+)/g", - "skipUrlSync": false, - "sort": 1, - "type": "query" - } - ] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "nowDelay": "" - }, - "timezone": "", - "title": "CloudNativePG", - "uid": "cloudnative-pg", - "version": 1, - "weekStart": "" -} +The JSON file has been moved to a dedicated repository for CloudNativePG dashboards located at: + +https://github.com/cloudnative-pg/grafana-dashboards/blob/main/charts/cluster/grafana-dashboard.json diff --git a/charts/cloudnative-pg/templates/grafana-dashboard.yaml b/charts/cloudnative-pg/templates/grafana-dashboard.yaml deleted file mode 100644 index 772530037..000000000 --- a/charts/cloudnative-pg/templates/grafana-dashboard.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if .Values.monitoring.grafanaDashboard.create -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ .Values.monitoring.grafanaDashboard.configMapName }} - namespace: {{ default .Release.Namespace .Values.monitoring.grafanaDashboard.namespace }} - labels: - {{ .Values.monitoring.grafanaDashboard.sidecarLabel }}: {{ .Values.monitoring.grafanaDashboard.sidecarLabelValue | quote }} -data: - cnp.json: |- -{{ .Files.Get "monitoring/grafana-dashboard.json" | indent 6 }} -{{- end -}} diff --git a/charts/cloudnative-pg/values.schema.json b/charts/cloudnative-pg/values.schema.json index 24d314406..9d380c6ea 100644 --- a/charts/cloudnative-pg/values.schema.json +++ b/charts/cloudnative-pg/values.schema.json @@ -98,12 +98,18 @@ "grafanaDashboard": { "type": "object", "properties": { + "annotations": { + "type": "object" + }, "configMapName": { "type": "string" }, "create": { "type": "boolean" }, + "labels": { + "type": "object" + }, "namespace": { "type": "string" }, diff --git a/charts/cloudnative-pg/values.yaml b/charts/cloudnative-pg/values.yaml index 8304224cf..956f0a56e 100644 --- a/charts/cloudnative-pg/values.yaml +++ b/charts/cloudnative-pg/values.yaml @@ -145,10 +145,14 @@ monitoring: namespace: "" # -- The name of the ConfigMap containing the dashboard. configMapName: "cnpg-grafana-dashboard" - # -- Label that ConfigMaps should have to be loaded as dashboards. + # -- Label that ConfigMaps should have to be loaded as dashboards. DEPRECATED: Use labels instead. sidecarLabel: "grafana_dashboard" - # -- Label value that ConfigMaps should have to be loaded as dashboards. + # -- Label value that ConfigMaps should have to be loaded as dashboards. DEPRECATED: Use labels instead. sidecarLabelValue: "1" + # -- Labels that ConfigMaps should have to get configured in Grafana. + labels: {} + # -- Annotations that ConfigMaps can have to get configured in Grafana. + annotations: {} # Default monitoring queries monitoringQueriesConfigMap: From ac6931592b01180ed77777b855beb808e59eeb87 Mon Sep 17 00:00:00 2001 From: Philippe Scorsolini Date: Mon, 25 Mar 2024 08:43:37 +0000 Subject: [PATCH 45/87] ci: properly add dependency before chart-releaser (#228) Signed-off-by: Philippe Scorsolini --- .github/workflows/release-publish.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 571919300..5f3a0d207 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -37,6 +37,10 @@ jobs: with: version: v3.14.1 + - name: Add chart dependencies + run: | + helm repo add cnpg-grafana-dashboard https://cloudnative-pg.github.io/grafana-dashboards + - name: Run chart-releaser uses: helm/chart-releaser-action@a917fd15b20e8b64b94d9158ad54cd6345335584 # v1.6.0 env: From 181e782ae7d6514f30d57c12bbce063291c86e84 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 26 Mar 2024 08:26:44 -0700 Subject: [PATCH 46/87] Add config value endpointCA for private S3 such as MinIO (#230) * Add config value endpointCA for private S3 such as MinIO Signed-off-by: PseudoResonance --- charts/cluster/README.md | 4 +++ .../templates/_barman_object_store.tpl | 6 ++++ charts/cluster/templates/ca-bundle.yaml | 9 +++++ charts/cluster/values.schema.json | 34 +++++++++++++++++++ charts/cluster/values.yaml | 14 ++++++++ 5 files changed, 67 insertions(+) create mode 100644 charts/cluster/templates/ca-bundle.yaml diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 85094a2ce..b21feedfe 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -125,6 +125,8 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | backups.data.jobs | int | `2` | Number of data files to be archived or restored in parallel. | | backups.destinationPath | string | `""` | Overrides the provider specific default path. Defaults to: S3: s3:// Azure: https://..core.windows.net/ Google: gs:// | | backups.enabled | bool | `false` | You need to configure backups manually, so backups are disabled by default. | +| backups.endpointCA | object | `{"create":false,"key":"","name":"","value":""}` | Specifies a CA bundle to validate a privately signed certificate. | +| backups.endpointCA.create | bool | `false` | Creates a secret with the given value if true, otherwise uses an existing secret. | | backups.endpointURL | string | `""` | Overrides the provider specific default endpoint. Defaults to: S3: https://s3..amazonaws.com" | | backups.google.applicationCredentials | string | `""` | | | backups.google.bucket | string | `""` | | @@ -190,6 +192,8 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | recovery.backupName | string | `""` | Backup Recovery Method | | recovery.clusterName | string | `""` | Object Store Recovery Method | | recovery.destinationPath | string | `""` | Overrides the provider specific default path. Defaults to: S3: s3:// Azure: https://..core.windows.net/ Google: gs:// | +| recovery.endpointCA | object | `{"create":false,"key":"","name":"","value":""}` | Specifies a CA bundle to validate a privately signed certificate. | +| recovery.endpointCA.create | bool | `false` | Creates a secret with the given value if true, otherwise uses an existing secret. | | recovery.endpointURL | string | `""` | Overrides the provider specific default endpoint. Defaults to: S3: https://s3..amazonaws.com" Leave empty if using the default S3 endpoint | | recovery.google.applicationCredentials | string | `""` | | | recovery.google.bucket | string | `""` | | diff --git a/charts/cluster/templates/_barman_object_store.tpl b/charts/cluster/templates/_barman_object_store.tpl index 96278f11a..f002800e5 100644 --- a/charts/cluster/templates/_barman_object_store.tpl +++ b/charts/cluster/templates/_barman_object_store.tpl @@ -4,6 +4,12 @@ endpointURL: {{ .scope.endpointURL }} {{- end }} +{{- if or (.scope.endpointCA.create) (.scope.endpointCA.name) }} + endpointCA: + name: {{ .chartFullname }}-ca-bundle + key: ca-bundle.crt +{{- end }} + {{- if .scope.destinationPath }} destinationPath: {{ .scope.destinationPath }} {{- end }} diff --git a/charts/cluster/templates/ca-bundle.yaml b/charts/cluster/templates/ca-bundle.yaml new file mode 100644 index 000000000..12991c163 --- /dev/null +++ b/charts/cluster/templates/ca-bundle.yaml @@ -0,0 +1,9 @@ +{{- if .Values.backups.endpointCA.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.backups.endpointCA.name | default (printf "%s-ca-bundle" (include "cluster.fullname" .)) | quote }} +data: + {{ .Values.backups.endpointCA.key | default "ca-bundle.crt" | quote }}: {{ .Values.backups.endpointCA.value }} + +{{- end }} diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 3ee174fdc..5a1c46e28 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -54,6 +54,23 @@ "enabled": { "type": "boolean" }, + "endpointCA": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, "endpointURL": { "type": "string" }, @@ -335,6 +352,23 @@ "destinationPath": { "type": "string" }, + "endpointCA": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, "endpointURL": { "type": "string" }, diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index 07d979a4c..d4db49e32 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -41,6 +41,13 @@ recovery: # S3: https://s3..amazonaws.com" # Leave empty if using the default S3 endpoint endpointURL: "" + # -- Specifies a CA bundle to validate a privately signed certificate. + endpointCA: + # -- Creates a secret with the given value if true, otherwise uses an existing secret. + create: false + name: "" + key: "" + value: "" # -- Overrides the provider specific default path. Defaults to: # S3: s3:// # Azure: https://..core.windows.net/ @@ -184,6 +191,13 @@ backups: # -- Overrides the provider specific default endpoint. Defaults to: # S3: https://s3..amazonaws.com" endpointURL: "" # Leave empty if using the default S3 endpoint + # -- Specifies a CA bundle to validate a privately signed certificate. + endpointCA: + # -- Creates a secret with the given value if true, otherwise uses an existing secret. + create: false + name: "" + key: "" + value: "" # -- Overrides the provider specific default path. Defaults to: # S3: s3:// From ac0a34ee73106e0df6f77394b3258920248d4b52 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 Mar 2024 20:05:44 +0200 Subject: [PATCH 47/87] chore(deps): update actions/setup-python digest to 82c7e63 (#231) * chore(deps): update actions/setup-python digest to 82c7e63 Signed-off-by: Itay Grudev Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Itay Grudev --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2f836614a..9e0fccaa5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: with: version: v3.4.0 - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: 3.7 From 741234645197be23cef35584f9bc17b6b63c7a80 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Tue, 26 Mar 2024 23:01:58 +0200 Subject: [PATCH 48/87] Added the ability to exclude specific PrometheusRules (#232) * Added the ability to exclude specific PrometheusRules Signed-off-by: Itay Grudev --- charts/cluster/README.md | 1 + .../prometheus_rules/cluster-ha-critical.yaml | 24 +++ .../prometheus_rules/cluster-ha-warning.yaml | 22 +++ .../cluster-high_connection-critical.yaml | 15 ++ .../cluster-high_connection-warning.yaml | 15 ++ .../cluster-high_replication_lag.yaml | 17 ++ .../cluster-instances_on_same_node.yaml | 17 ++ .../cluster-low_disk_space-critical.yaml | 22 +++ .../cluster-low_disk_space-warning.yaml | 22 +++ .../prometheus_rules/cluster-offline.yaml | 17 ++ .../cluster-zone_spread-warning.yaml | 16 ++ charts/cluster/templates/prometheus-rule.yaml | 174 ++---------------- charts/cluster/values.schema.json | 3 + charts/cluster/values.yaml | 5 +- 14 files changed, 206 insertions(+), 164 deletions(-) create mode 100644 charts/cluster/prometheus_rules/cluster-ha-critical.yaml create mode 100644 charts/cluster/prometheus_rules/cluster-ha-warning.yaml create mode 100644 charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml create mode 100644 charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml create mode 100644 charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml create mode 100644 charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml create mode 100644 charts/cluster/prometheus_rules/cluster-low_disk_space-critical.yaml create mode 100644 charts/cluster/prometheus_rules/cluster-low_disk_space-warning.yaml create mode 100644 charts/cluster/prometheus_rules/cluster-offline.yaml create mode 100644 charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml diff --git a/charts/cluster/README.md b/charts/cluster/README.md index b21feedfe..af047fba9 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -160,6 +160,7 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | cluster.monitoring.enabled | bool | `false` | Whether to enable monitoring | | cluster.monitoring.podMonitor.enabled | bool | `true` | Whether to enable the PodMonitor | | cluster.monitoring.prometheusRule.enabled | bool | `true` | Whether to enable the PrometheusRule automated alerts | +| cluster.monitoring.prometheusRule.excludeRules | list | `[]` | Exclude specified rules | | cluster.postgresGID | int | `26` | The GID of the postgres user inside the image, defaults to 26 | | cluster.postgresUID | int | `26` | The UID of the postgres user inside the image, defaults to 26 | | cluster.postgresql | object | `{}` | Configuration of the PostgreSQL server. See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-PostgresConfiguration | diff --git a/charts/cluster/prometheus_rules/cluster-ha-critical.yaml b/charts/cluster/prometheus_rules/cluster-ha-critical.yaml new file mode 100644 index 000000000..014e9ec44 --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-ha-critical.yaml @@ -0,0 +1,24 @@ +{{- $alert := "CNPGClusterHACritical" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster has no standby replicas! + description: |- + CloudNativePG Cluster "{{ .labels.job }}" has no ready standby replicas. Your cluster at a severe + risk of data loss and downtime if the primary instance fails. + + The primary instance is still online and able to serve queries, although connections to the `-ro` endpoint + will fail. The `-r` endpoint os operating at reduced capacity and all traffic is being served by the main. + + This can happen during a normal fail-over or automated minor version upgrades in a cluster with 2 or less + instances. The replaced instance may need some time to catch-up with the cluster primary instance. + + This alarm will be always trigger if your cluster is configured to run with only 1 instance. In this + case you may want to silence it. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHACritical.md +expr: | + max by (job) (cnpg_pg_replication_streaming_replicas{namespace="{{ .namespace }}"} - cnpg_pg_replication_is_wal_receiver_up{namespace="{{ .namespace }}"}) < 1 +for: 5m +labels: + severity: critical +{{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-ha-warning.yaml b/charts/cluster/prometheus_rules/cluster-ha-warning.yaml new file mode 100644 index 000000000..15a5d4d13 --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-ha-warning.yaml @@ -0,0 +1,22 @@ +{{- $alert := "CNPGClusterHAWarning" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster less than 2 standby replicas. + description: |- + CloudNativePG Cluster "{{ .labels.job }}" has only {{ .value }} standby replicas, putting + your cluster at risk if another instance fails. The cluster is still able to operate normally, although + the `-ro` and `-r` endpoints operate at reduced capacity. + + This can happen during a normal fail-over or automated minor version upgrades. The replaced instance may + need some time to catch-up with the cluster primary instance. + + This alarm will be constantly triggered if your cluster is configured to run with less than 3 instances. + In this case you may want to silence it. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHAWarning.md +expr: | + max by (job) (cnpg_pg_replication_streaming_replicas{namespace="{{ .namespace }}"} - cnpg_pg_replication_is_wal_receiver_up{namespace="{{ .namespace }}"}) < 2 +for: 5m +labels: + severity: warning +{{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml b/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml new file mode 100644 index 000000000..ac83376ab --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml @@ -0,0 +1,15 @@ +{{- $alert := "CNPGClusterHighConnectionsCritical" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Instance maximum number of connections critical! + description: |- + CloudNativePG Cluster "{{ .cluster }}" instance {{ .labels.pod }} is using {{ .value }}% of + the maximum number of connections. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsCritical.md +expr: | + sum by (pod) (cnpg_backends_total{namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) * 100 > 95 +for: 5m +labels: + severity: critical +{{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml b/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml new file mode 100644 index 000000000..126abd863 --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml @@ -0,0 +1,15 @@ +{{- $alert := "CNPGClusterHighConnectionsWarning" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Instance is approaching the maximum number of connections. + description: |- + CloudNativePG Cluster "{{ .cluster }}" instance {{ .labels.pod }} is using {{ .value }}% of + the maximum number of connections. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsWarning.md +expr: | + sum by (pod) (cnpg_backends_total{namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) * 100 > 80 +for: 5m +labels: + severity: warning +{{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml b/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml new file mode 100644 index 000000000..4cf1610d2 --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml @@ -0,0 +1,17 @@ +{{- $alert := "CNPGClusterHighReplicationLag" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster high replication lag + description: |- + CloudNativePG Cluster "{{ .cluster }}" is experiencing a high replication lag of + {{ .value }}ms. + + High replication lag indicates network issues, busy instances, slow queries or suboptimal configuration. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighReplicationLag.md +expr: | + max(cnpg_pg_replication_lag{namespace=~"{{ .namespace }}",pod=~"{{ .podSelector }}"}) * 1000 > 1000 +for: 5m +labels: + severity: warning +{{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml b/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml new file mode 100644 index 000000000..39900cf20 --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml @@ -0,0 +1,17 @@ +{{- $alert := "CNPGClusterInstancesOnSameNode" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster instances are located on the same node. + description: |- + CloudNativePG Cluster "{{ .cluster }}" has {{ .value }} + instances on the same node {{ .labels.node }}. + + A failure or scheduled downtime of a single node will lead to a potential service disruption and/or data loss. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterInstancesOnSameNode.md +expr: | + count by (node) (kube_pod_info{namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) > 1 +for: 5m +labels: + severity: warning +{{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-low_disk_space-critical.yaml b/charts/cluster/prometheus_rules/cluster-low_disk_space-critical.yaml new file mode 100644 index 000000000..fcacab9be --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-low_disk_space-critical.yaml @@ -0,0 +1,22 @@ +{{- $alert := "CNPGClusterLowDiskSpaceCritical" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Instance is running out of disk space! + description: |- + CloudNativePG Cluster "{{ .cluster }}" is running extremely low on disk space. Check attached PVCs! + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceCritical.md +expr: | + max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}"} / kubelet_volume_stats_capacity_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}"})) > 0.9 OR + max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}-wal"} / kubelet_volume_stats_capacity_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}-wal"})) > 0.9 OR + max(sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}-tbs.*"}) + / + sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}-tbs.*"}) + * + on(namespace, persistentvolumeclaim) group_left(volume) + kube_pod_spec_volumes_persistentvolumeclaims_info{pod=~"{{ .podSelector }}"} + ) > 0.9 +for: 5m +labels: + severity: critical +{{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-low_disk_space-warning.yaml b/charts/cluster/prometheus_rules/cluster-low_disk_space-warning.yaml new file mode 100644 index 000000000..7f36f4351 --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-low_disk_space-warning.yaml @@ -0,0 +1,22 @@ +{{- $alert := "CNPGClusterLowDiskSpaceWarning" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Instance is running out of disk space. + description: |- + CloudNativePG Cluster "{{ .cluster }}" is running low on disk space. Check attached PVCs. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceWarning.md +expr: | + max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}"} / kubelet_volume_stats_capacity_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}"})) > 0.7 OR + max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}-wal"} / kubelet_volume_stats_capacity_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}-wal"})) > 0.7 OR + max(sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}-tbs.*"}) + / + sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}-tbs.*"}) + * + on(namespace, persistentvolumeclaim) group_left(volume) + kube_pod_spec_volumes_persistentvolumeclaims_info{pod=~"{{ .podSelector }}"} + ) > 0.7 +for: 5m +labels: + severity: warning +{{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-offline.yaml b/charts/cluster/prometheus_rules/cluster-offline.yaml new file mode 100644 index 000000000..75647f7cc --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-offline.yaml @@ -0,0 +1,17 @@ +{{- $alert := "CNPGClusterOffline" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster has no running instances! + description: |- + CloudNativePG Cluster "{{ .labels.job }}" has no ready instances. + + Having an offline cluster means your applications will not be able to access the database, leading to + potential service disruption and/or data loss. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterOffline.md +expr: | + ({{ .Values.cluster.instances }} - count(cnpg_collector_up{namespace=~"{{ .namespace }}",pod=~"{{ .podSelector }}"}) OR vector(0)) > 0 +for: 5m +labels: + severity: critical +{{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml b/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml new file mode 100644 index 000000000..17183986e --- /dev/null +++ b/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml @@ -0,0 +1,16 @@ +{{- $alert := "CNPGClusterZoneSpreadWarning" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster instances in the same zone. + description: |- + CloudNativePG Cluster "{{ .cluster }}" has instances in the same availability zone. + + A disaster in one availability zone will lead to a potential service disruption and/or data loss. + runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterZoneSpreadWarning.md +expr: | + {{ .Values.cluster.instances }} > count(count by (label_topology_kubernetes_io_zone) (kube_pod_info{namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"} * on(node,instance) group_left(label_topology_kubernetes_io_zone) kube_node_labels)) < 3 +for: 5m +labels: + severity: warning +{{- end -}} diff --git a/charts/cluster/templates/prometheus-rule.yaml b/charts/cluster/templates/prometheus-rule.yaml index bc3ffde84..380f90a7d 100644 --- a/charts/cluster/templates/prometheus-rule.yaml +++ b/charts/cluster/templates/prometheus-rule.yaml @@ -1,9 +1,4 @@ {{- if and .Values.cluster.monitoring.enabled .Values.cluster.monitoring.prometheusRule.enabled -}} -{{- $value := "{{ $value }}" -}} -{{- $namespace := .Release.Namespace -}} -{{- $cluster := printf "%s/%s" $namespace (include "cluster.fullname" .)}} -{{- $labels := dict "job" "{{ $labels.job }}" "node" "{{ $labels.node }}" "pod" "{{ $labels.pod }}" -}} -{{- $podSelector := printf "%s-([1-9][0-9]*)$" (include "cluster.fullname" .) -}} apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: @@ -16,162 +11,15 @@ metadata: spec: groups: - name: cloudnative-pg/{{ include "cluster.fullname" . }} - rules: - - alert: CNPGClusterHAWarning - annotations: - summary: CNPG Cluster less than 2 standby replicas. - description: |- - CloudNativePG Cluster "{{ $labels.job }}" has only {{ $value }} standby replicas, putting - your cluster at risk if another instance fails. The cluster is still able to operate normally, although - the `-ro` and `-r` endpoints operate at reduced capacity. - - This can happen during a normal fail-over or automated minor version upgrades. The replaced instance may - need some time to catch-up with the cluster primary instance. - - This alarm will be constantly triggered if your cluster is configured to run with less than 3 instances. - In this case you may want to silence it. - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHAWarning.md - expr: | - max by (job) (cnpg_pg_replication_streaming_replicas{namespace="{{ $namespace }}"} - cnpg_pg_replication_is_wal_receiver_up{namespace="{{ $namespace }}"}) < 2 - for: 5m - labels: - severity: warning - - alert: CNPGClusterHACritical - annotations: - summary: CNPG Cluster has no standby replicas! - description: |- - CloudNativePG Cluster "{{ $labels.job }}" has no ready standby replicas. Your cluster at a severe - risk of data loss and downtime if the primary instance fails. - - The primary instance is still online and able to serve queries, although connections to the `-ro` endpoint - will fail. The `-r` endpoint os operating at reduced capacity and all traffic is being served by the main. - - This can happen during a normal fail-over or automated minor version upgrades in a cluster with 2 or less - instances. The replaced instance may need some time to catch-up with the cluster primary instance. - - This alarm will be always trigger if your cluster is configured to run with only 1 instance. In this - case you may want to silence it. - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHACritical.md - expr: | - max by (job) (cnpg_pg_replication_streaming_replicas{namespace="{{ $namespace }}"} - cnpg_pg_replication_is_wal_receiver_up{namespace="{{ $namespace }}"}) < 1 - for: 5m - labels: - severity: critical - - alert: CNPGClusterOffline - annotations: - summary: CNPG Cluster has no running instances! - description: |- - CloudNativePG Cluster "{{ $labels.job }}" has no ready instances. - - Having an offline cluster means your applications will not be able to access the database, leading to - potential service disruption and/or data loss. - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterOffline.md - expr: | - ({{ .Values.cluster.instances }} - count(cnpg_collector_up{namespace=~"{{ $namespace }}",pod=~"{{ $podSelector }}"}) OR vector(0)) > 0 - for: 5m - labels: - severity: critical - - alert: CNPGClusterZoneSpreadWarning - annotations: - summary: CNPG Cluster instances in the same zone. - description: |- - CloudNativePG Cluster "{{ $cluster }}" has instances in the same availability zone. - - A disaster in one availability zone will lead to a potential service disruption and/or data loss. - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterZoneSpreadWarning.md - expr: | - {{ .Values.cluster.instances }} > count(count by (label_topology_kubernetes_io_zone) (kube_pod_info{namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"} * on(node,instance) group_left(label_topology_kubernetes_io_zone) kube_node_labels)) < 3 - for: 5m - labels: - severity: warning - - alert: CNPGClusterInstancesOnSameNode - annotations: - summary: CNPG Cluster instances are located on the same node. - description: |- - CloudNativePG Cluster "{{ $cluster }}" has {{ $value }} - instances on the same node {{ $labels.node }}. - - A failure or scheduled downtime of a single node will lead to a potential service disruption and/or data loss. - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterInstancesOnSameNode.md - expr: | - count by (node) (kube_pod_info{namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) > 1 - for: 5m - labels: - severity: warning - - alert: CNPGClusterHighReplicationLag - annotations: - summary: CNPG Cluster high replication lag - description: |- - CloudNativePG Cluster "{{ $cluster }}" is experiencing a high replication lag of - {{ "{{ $value }}" }}ms. - - High replication lag indicates network issues, busy instances, slow queries or suboptimal configuration. - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighReplicationLag.md - expr: | - max(cnpg_pg_replication_lag{namespace=~"{{ $namespace }}",pod=~"{{ $podSelector }}"}) * 1000 > 1000 - for: 5m - labels: - severity: warning - - alert: CNPGClusterHighConnectionsWarning - annotations: - summary: CNPG Instance is approaching the maximum number of connections. - description: |- - CloudNativePG Cluster "{{ $cluster }}" instance {{ $labels.pod }} is using {{ "{{ $value }}" }}% of - the maximum number of connections. - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsWarning.md - expr: | - sum by (pod) (cnpg_backends_total{namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) * 100 > 80 - for: 5m - labels: - severity: warning - - alert: CNPGClusterHighConnectionsCritical - annotations: - summary: CNPG Instance maximum number of connections critical! - description: |- - CloudNativePG Cluster "{{ $cluster }}" instance {{ $labels.pod }} is using {{ "{{ $value }}" }}% of - the maximum number of connections. - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsCritical.md - expr: | - sum by (pod) (cnpg_backends_total{namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace=~"{{ $namespace }}", pod=~"{{ $podSelector }}"}) * 100 > 95 - for: 5m - labels: - severity: critical - - alert: CNPGClusterLowDiskSpaceWarning - annotations: - summary: CNPG Instance is running out of disk space. - description: |- - CloudNativePG Cluster "{{ $cluster }}" is running low on disk space. Check attached PVCs. - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceWarning.md - expr: | - max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}"} / kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}"})) > 0.7 OR - max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-wal"} / kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-wal"})) > 0.7 OR - max(sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-tbs.*"}) - / - sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-tbs.*"}) - * - on(namespace, persistentvolumeclaim) group_left(volume) - kube_pod_spec_volumes_persistentvolumeclaims_info{pod=~"{{ $podSelector }}"} - ) > 0.7 - for: 5m - labels: - severity: warning - - alert: CNPGClusterLowDiskSpaceCritical - annotations: - summary: CNPG Instance is running out of disk space! - description: |- - CloudNativePG Cluster "{{ $cluster }}" is running extremely low on disk space. Check attached PVCs! - runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceCritical.md - expr: | - max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}"} / kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}"})) > 0.9 OR - max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-wal"} / kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-wal"})) > 0.9 OR - max(sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_used_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-tbs.*"}) - / - sum by (namespace,persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes{namespace="{{ $namespace }}", persistentvolumeclaim=~"{{ $podSelector }}-tbs.*"}) - * - on(namespace, persistentvolumeclaim) group_left(volume) - kube_pod_spec_volumes_persistentvolumeclaims_info{pod=~"{{ $podSelector }}"} - ) > 0.9 - for: 5m - labels: - severity: critical + rules: | + {{ $dict := dict "excludeRules" .Values.cluster.monitoring.prometheusRule.excludeRules -}} + {{- $_ := set $dict "value" "{{ $value }}" -}} + {{- $_ := set $dict "namespace" .Release.Namespace -}} + {{- $_ := set $dict "cluster" (printf "%s/%s" .Release.Namespace (include "cluster.fullname" .)) -}} + {{- $_ := set $dict "labels" (dict "job" "{{ $labels.job }}" "node" "{{ $labels.node }}" "pod" "{{ $labels.pod }}") -}} + {{- $_ := set $dict "podSelector" (printf "%s-([1-9][0-9]*)$" (include "cluster.fullname" .)) -}} + {{- $_ := set $dict "Values" .Values -}} + {{- range $path, $_ := .Files.Glob "prometheus_rules/**.yaml" }} + - {{ tpl ($.Files.Get $path) $dict | nindent 10 | trim -}} + {{- end -}} {{ end }} diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 5a1c46e28..0c7c1bbe0 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -213,6 +213,9 @@ "properties": { "enabled": { "type": "boolean" + }, + "excludeRules": { + "type": "array" } } } diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index d4db49e32..bff9d2202 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -151,8 +151,11 @@ cluster: # -- Whether to enable the PodMonitor enabled: true prometheusRule: - # -- Whether to enable the PrometheusRule automated alerts + # -- Whether to enable the PrometheusRule automated alerts enabled: true + # -- Exclude specified rules + excludeRules: [] + # - CNPGClusterZoneSpreadWarning # -- Custom Prometheus metrics customQueries: [] # - name: "pg_cache_hit_ratio" From f2d6ea7effe8c7220ddcd7f2001345abf46a14a8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:04:26 +0200 Subject: [PATCH 49/87] Release cluster-v0.0.4 (#234) * Added the ability to exclude specific PrometheusRules by @itay-grudev (#232) * Added config value endpointCA for private S3 such as MinIO by @PseudoResonance (#229, #230) * Bug Fix: Severity of CNPGClusterLowDiskSpaceCritical should be critical not warning by @baurmatt (#223) * New `backup.barmanObjectStore.wal` and `backup.barmanObjectStore.data` to support disabling encryption by @itay-grudev (#198, #221) Signed-off-by: Itay Grudev Co-authored-by: Itay Grudev --- RELEASE.md | 4 ++-- charts/cluster/Chart.yaml | 2 +- charts/cluster/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 984d1d550..d328344e0 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -32,7 +32,7 @@ In order to create a new release of the `cloudnative-pg` chart, follow these ste ``` 3. Create a branch named `release/cloudnative-pg-vX.Y.Z` and switch to it: ```bash - git checkout -b release/cloudnative-pg-v$NEW_VERSION + git switch --create release/cloudnative-pg-v$NEW_VERSION ``` 4. Update the `.version` in the [Chart.yaml](./charts/cloudnative-pg/Chart.yaml) file to `"X.Y.Z"` ```bash @@ -113,7 +113,7 @@ In order to create a new release of the `cluster` chart, follow these steps: ``` 3. Create a branch: named `release/cluster-vX.Y.Z` and switch to it ```bash - git checkout -b release/cluster-v$NEW_VERSION + git switch --create release/cluster-v$NEW_VERSION ``` 4. Update the `.version` in the [Chart.yaml](./charts/cluster/Chart.yaml) file to `"X.Y.Z"` ```bash diff --git a/charts/cluster/Chart.yaml b/charts/cluster/Chart.yaml index c57c3e6a9..dccec1ead 100644 --- a/charts/cluster/Chart.yaml +++ b/charts/cluster/Chart.yaml @@ -18,7 +18,7 @@ name: cluster description: Deploys and manages a CloudNativePG cluster and its associated resources. icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: 0.0.3 +version: 0.0.4 sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cluster/README.md b/charts/cluster/README.md index af047fba9..ce4d45567 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -1,6 +1,6 @@ # cluster -![Version: 0.0.3](https://img.shields.io/badge/Version-0.0.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![Version: 0.0.4](https://img.shields.io/badge/Version-0.0.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) > **Warning** > ### This chart is under active development. From 0a85ff4cc0d73f9bd494ae641d74fe29342b77b6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:48:51 +0200 Subject: [PATCH 50/87] Release cluster-v0.0.5 (#236) Bug Fix: PrometheusRules template issue prevents upgrading the chart Signed-off-by: Itay Grudev Co-authored-by: Itay Grudev --- charts/cluster/Chart.yaml | 2 +- charts/cluster/README.md | 2 +- charts/cluster/templates/prometheus-rule.yaml | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/charts/cluster/Chart.yaml b/charts/cluster/Chart.yaml index dccec1ead..fd2be2809 100644 --- a/charts/cluster/Chart.yaml +++ b/charts/cluster/Chart.yaml @@ -18,7 +18,7 @@ name: cluster description: Deploys and manages a CloudNativePG cluster and its associated resources. icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: 0.0.4 +version: 0.0.5 sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cluster/README.md b/charts/cluster/README.md index ce4d45567..fb2fdb814 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -1,6 +1,6 @@ # cluster -![Version: 0.0.4](https://img.shields.io/badge/Version-0.0.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![Version: 0.0.5](https://img.shields.io/badge/Version-0.0.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) > **Warning** > ### This chart is under active development. diff --git a/charts/cluster/templates/prometheus-rule.yaml b/charts/cluster/templates/prometheus-rule.yaml index 380f90a7d..1836f51d9 100644 --- a/charts/cluster/templates/prometheus-rule.yaml +++ b/charts/cluster/templates/prometheus-rule.yaml @@ -11,14 +11,15 @@ metadata: spec: groups: - name: cloudnative-pg/{{ include "cluster.fullname" . }} - rules: | - {{ $dict := dict "excludeRules" .Values.cluster.monitoring.prometheusRule.excludeRules -}} + rules: + {{- $dict := dict "excludeRules" .Values.cluster.monitoring.prometheusRule.excludeRules -}} {{- $_ := set $dict "value" "{{ $value }}" -}} {{- $_ := set $dict "namespace" .Release.Namespace -}} {{- $_ := set $dict "cluster" (printf "%s/%s" .Release.Namespace (include "cluster.fullname" .)) -}} {{- $_ := set $dict "labels" (dict "job" "{{ $labels.job }}" "node" "{{ $labels.node }}" "pod" "{{ $labels.pod }}") -}} {{- $_ := set $dict "podSelector" (printf "%s-([1-9][0-9]*)$" (include "cluster.fullname" .)) -}} {{- $_ := set $dict "Values" .Values -}} + {{- $_ := set $dict "Template" .Template -}} {{- range $path, $_ := .Files.Glob "prometheus_rules/**.yaml" }} - {{ tpl ($.Files.Get $path) $dict | nindent 10 | trim -}} {{- end -}} From dac608334750da7faca45ce5b03cb3ef11292262 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Mon, 1 Apr 2024 16:02:02 +0300 Subject: [PATCH 51/87] Bug Fix: barmanObjectStore.serverName not initialized correctly preventing recovery when the cluster name is different (#240) Signed-off-by: Itay Grudev --- charts/cluster/README.md | 6 +++--- charts/cluster/templates/_bootstrap.tpl | 2 +- charts/cluster/values.yaml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/cluster/README.md b/charts/cluster/README.md index fb2fdb814..2182f8199 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -123,7 +123,7 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | backups.data.compression | string | `"gzip"` | Data compression method. One of `` (for no compression), `gzip`, `bzip2` or `snappy`. | | backups.data.encryption | string | `"AES256"` | Whether to instruct the storage provider to encrypt data files. One of `` (use the storage container default), `AES256` or `aws:kms`. | | backups.data.jobs | int | `2` | Number of data files to be archived or restored in parallel. | -| backups.destinationPath | string | `""` | Overrides the provider specific default path. Defaults to: S3: s3:// Azure: https://..core.windows.net/ Google: gs:// | +| backups.destinationPath | string | `""` | Overrides the provider specific default path. Defaults to: S3: s3:// Azure: https://..core.windows.net/ Google: gs:// | | backups.enabled | bool | `false` | You need to configure backups manually, so backups are disabled by default. | | backups.endpointCA | object | `{"create":false,"key":"","name":"","value":""}` | Specifies a CA bundle to validate a privately signed certificate. | | backups.endpointCA.create | bool | `false` | Creates a secret with the given value if true, otherwise uses an existing secret. | @@ -191,8 +191,8 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | recovery.azure.storageKey | string | `""` | | | recovery.azure.storageSasToken | string | `""` | | | recovery.backupName | string | `""` | Backup Recovery Method | -| recovery.clusterName | string | `""` | Object Store Recovery Method | -| recovery.destinationPath | string | `""` | Overrides the provider specific default path. Defaults to: S3: s3:// Azure: https://..core.windows.net/ Google: gs:// | +| recovery.clusterName | string | `""` | The original cluster name when used in backups. Also known as serverName. | +| recovery.destinationPath | string | `""` | Overrides the provider specific default path. Defaults to: S3: s3:// Azure: https://..core.windows.net/ Google: gs:// | | recovery.endpointCA | object | `{"create":false,"key":"","name":"","value":""}` | Specifies a CA bundle to validate a privately signed certificate. | | recovery.endpointCA.create | bool | `false` | Creates a secret with the given value if true, otherwise uses an existing secret. | | recovery.endpointURL | string | `""` | Overrides the provider specific default endpoint. Defaults to: S3: https://s3..amazonaws.com" Leave empty if using the default S3 endpoint | diff --git a/charts/cluster/templates/_bootstrap.tpl b/charts/cluster/templates/_bootstrap.tpl index 214ad391b..6147f3a77 100644 --- a/charts/cluster/templates/_bootstrap.tpl +++ b/charts/cluster/templates/_bootstrap.tpl @@ -37,7 +37,7 @@ bootstrap: externalClusters: - name: objectStoreRecoveryCluster barmanObjectStore: - serverName: {{ .Values.recovery.serverName }} + serverName: {{ .Values.recovery.clusterName }} {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.recovery "secretSuffix" "-recovery" -}} {{- include "cluster.barmanObjectStoreConfig" $d | nindent 4 }} {{- else }} diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index bff9d2202..325d5f3cd 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -35,7 +35,7 @@ recovery: backupName: "" # Name of the backup to recover from. Required if method is `backup`. ## - # -- Object Store Recovery Method + # -- The original cluster name when used in backups. Also known as serverName. clusterName: "" # -- Overrides the provider specific default endpoint. Defaults to: # S3: https://s3..amazonaws.com" @@ -50,7 +50,7 @@ recovery: value: "" # -- Overrides the provider specific default path. Defaults to: # S3: s3:// - # Azure: https://..core.windows.net/ + # Azure: https://..core.windows.net/ # Google: gs:// destinationPath: "" # -- One of `s3`, `azure` or `google` @@ -204,7 +204,7 @@ backups: # -- Overrides the provider specific default path. Defaults to: # S3: s3:// - # Azure: https://..core.windows.net/ + # Azure: https://..core.windows.net/ # Google: gs:// destinationPath: "" # -- One of `s3`, `azure` or `google` From 3f78003743cddc7767e9d6f0ae41259f7ccc03cd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:07:40 +0300 Subject: [PATCH 52/87] Release cluster-v0.0.6 (#241) Signed-off-by: Itay Grudev Co-authored-by: Itay Grudev --- charts/cluster/Chart.yaml | 2 +- charts/cluster/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/cluster/Chart.yaml b/charts/cluster/Chart.yaml index fd2be2809..5d61e8bb0 100644 --- a/charts/cluster/Chart.yaml +++ b/charts/cluster/Chart.yaml @@ -18,7 +18,7 @@ name: cluster description: Deploys and manages a CloudNativePG cluster and its associated resources. icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: 0.0.5 +version: 0.0.6 sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 2182f8199..93412962a 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -1,6 +1,6 @@ # cluster -![Version: 0.0.5](https://img.shields.io/badge/Version-0.0.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![Version: 0.0.6](https://img.shields.io/badge/Version-0.0.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) > **Warning** > ### This chart is under active development. From 36142ddfa538cb552f69a06f203bb50a6d1c393c Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Wed, 3 Apr 2024 12:46:39 +0300 Subject: [PATCH 53/87] Added additional PrometheusRule labels (#238) Signed-off-by: Itay Grudev --- charts/cluster/prometheus_rules/cluster-ha-critical.yaml | 2 ++ charts/cluster/prometheus_rules/cluster-ha-warning.yaml | 2 ++ .../prometheus_rules/cluster-high_connection-critical.yaml | 4 +++- .../prometheus_rules/cluster-high_connection-warning.yaml | 4 +++- .../prometheus_rules/cluster-high_replication_lag.yaml | 4 +++- .../prometheus_rules/cluster-instances_on_same_node.yaml | 4 +++- .../prometheus_rules/cluster-low_disk_space-critical.yaml | 4 +++- .../prometheus_rules/cluster-low_disk_space-warning.yaml | 4 +++- charts/cluster/prometheus_rules/cluster-offline.yaml | 2 ++ .../prometheus_rules/cluster-zone_spread-warning.yaml | 4 +++- charts/cluster/templates/prometheus-rule.yaml | 7 +++++-- 11 files changed, 32 insertions(+), 9 deletions(-) diff --git a/charts/cluster/prometheus_rules/cluster-ha-critical.yaml b/charts/cluster/prometheus_rules/cluster-ha-critical.yaml index 014e9ec44..246a5af6b 100644 --- a/charts/cluster/prometheus_rules/cluster-ha-critical.yaml +++ b/charts/cluster/prometheus_rules/cluster-ha-critical.yaml @@ -21,4 +21,6 @@ expr: | for: 5m labels: severity: critical + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-ha-warning.yaml b/charts/cluster/prometheus_rules/cluster-ha-warning.yaml index 15a5d4d13..736ddf393 100644 --- a/charts/cluster/prometheus_rules/cluster-ha-warning.yaml +++ b/charts/cluster/prometheus_rules/cluster-ha-warning.yaml @@ -19,4 +19,6 @@ expr: | for: 5m labels: severity: warning + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml b/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml index ac83376ab..e5de95225 100644 --- a/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml +++ b/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml @@ -4,7 +4,7 @@ alert: {{ $alert }} annotations: summary: CNPG Instance maximum number of connections critical! description: |- - CloudNativePG Cluster "{{ .cluster }}" instance {{ .labels.pod }} is using {{ .value }}% of + CloudNativePG Cluster "{{ .namespace }}/{{ .cluster }}" instance {{ .labels.pod }} is using {{ .value }}% of the maximum number of connections. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsCritical.md expr: | @@ -12,4 +12,6 @@ expr: | for: 5m labels: severity: critical + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml b/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml index 126abd863..ae706ee0b 100644 --- a/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml +++ b/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml @@ -4,7 +4,7 @@ alert: {{ $alert }} annotations: summary: CNPG Instance is approaching the maximum number of connections. description: |- - CloudNativePG Cluster "{{ .cluster }}" instance {{ .labels.pod }} is using {{ .value }}% of + CloudNativePG Cluster "{{ .namespace }}/{{ .cluster }}" instance {{ .labels.pod }} is using {{ .value }}% of the maximum number of connections. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsWarning.md expr: | @@ -12,4 +12,6 @@ expr: | for: 5m labels: severity: warning + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml b/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml index 4cf1610d2..ab1c175a1 100644 --- a/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml +++ b/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml @@ -4,7 +4,7 @@ alert: {{ $alert }} annotations: summary: CNPG Cluster high replication lag description: |- - CloudNativePG Cluster "{{ .cluster }}" is experiencing a high replication lag of + CloudNativePG Cluster "{{ .namespace }}/{{ .cluster }}" is experiencing a high replication lag of {{ .value }}ms. High replication lag indicates network issues, busy instances, slow queries or suboptimal configuration. @@ -14,4 +14,6 @@ expr: | for: 5m labels: severity: warning + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml b/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml index 39900cf20..b5a90742e 100644 --- a/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml +++ b/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml @@ -4,7 +4,7 @@ alert: {{ $alert }} annotations: summary: CNPG Cluster instances are located on the same node. description: |- - CloudNativePG Cluster "{{ .cluster }}" has {{ .value }} + CloudNativePG Cluster "{{ .namespace }}/{{ .cluster }}" has {{ .value }} instances on the same node {{ .labels.node }}. A failure or scheduled downtime of a single node will lead to a potential service disruption and/or data loss. @@ -14,4 +14,6 @@ expr: | for: 5m labels: severity: warning + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-low_disk_space-critical.yaml b/charts/cluster/prometheus_rules/cluster-low_disk_space-critical.yaml index fcacab9be..c211bc61a 100644 --- a/charts/cluster/prometheus_rules/cluster-low_disk_space-critical.yaml +++ b/charts/cluster/prometheus_rules/cluster-low_disk_space-critical.yaml @@ -4,7 +4,7 @@ alert: {{ $alert }} annotations: summary: CNPG Instance is running out of disk space! description: |- - CloudNativePG Cluster "{{ .cluster }}" is running extremely low on disk space. Check attached PVCs! + CloudNativePG Cluster "{{ .namespace }}/{{ .cluster }}" is running extremely low on disk space. Check attached PVCs! runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceCritical.md expr: | max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}"} / kubelet_volume_stats_capacity_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}"})) > 0.9 OR @@ -19,4 +19,6 @@ expr: | for: 5m labels: severity: critical + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-low_disk_space-warning.yaml b/charts/cluster/prometheus_rules/cluster-low_disk_space-warning.yaml index 7f36f4351..5ed3653aa 100644 --- a/charts/cluster/prometheus_rules/cluster-low_disk_space-warning.yaml +++ b/charts/cluster/prometheus_rules/cluster-low_disk_space-warning.yaml @@ -4,7 +4,7 @@ alert: {{ $alert }} annotations: summary: CNPG Instance is running out of disk space. description: |- - CloudNativePG Cluster "{{ .cluster }}" is running low on disk space. Check attached PVCs. + CloudNativePG Cluster "{{ .namespace }}/{{ .cluster }}" is running low on disk space. Check attached PVCs. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterLowDiskSpaceWarning.md expr: | max(max by(persistentvolumeclaim) (1 - kubelet_volume_stats_available_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}"} / kubelet_volume_stats_capacity_bytes{namespace="{{ .namespace }}", persistentvolumeclaim=~"{{ .podSelector }}"})) > 0.7 OR @@ -19,4 +19,6 @@ expr: | for: 5m labels: severity: warning + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-offline.yaml b/charts/cluster/prometheus_rules/cluster-offline.yaml index 75647f7cc..4ac68ce35 100644 --- a/charts/cluster/prometheus_rules/cluster-offline.yaml +++ b/charts/cluster/prometheus_rules/cluster-offline.yaml @@ -14,4 +14,6 @@ expr: | for: 5m labels: severity: critical + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml b/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml index 17183986e..0959ae87b 100644 --- a/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml +++ b/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml @@ -4,7 +4,7 @@ alert: {{ $alert }} annotations: summary: CNPG Cluster instances in the same zone. description: |- - CloudNativePG Cluster "{{ .cluster }}" has instances in the same availability zone. + CloudNativePG Cluster "{{ .namespace }}/{{ .cluster }}" has instances in the same availability zone. A disaster in one availability zone will lead to a potential service disruption and/or data loss. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterZoneSpreadWarning.md @@ -13,4 +13,6 @@ expr: | for: 5m labels: severity: warning + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} {{- end -}} diff --git a/charts/cluster/templates/prometheus-rule.yaml b/charts/cluster/templates/prometheus-rule.yaml index 1836f51d9..9b29bc692 100644 --- a/charts/cluster/templates/prometheus-rule.yaml +++ b/charts/cluster/templates/prometheus-rule.yaml @@ -15,12 +15,15 @@ spec: {{- $dict := dict "excludeRules" .Values.cluster.monitoring.prometheusRule.excludeRules -}} {{- $_ := set $dict "value" "{{ $value }}" -}} {{- $_ := set $dict "namespace" .Release.Namespace -}} - {{- $_ := set $dict "cluster" (printf "%s/%s" .Release.Namespace (include "cluster.fullname" .)) -}} + {{- $_ := set $dict "cluster" (include "cluster.fullname" .) -}} {{- $_ := set $dict "labels" (dict "job" "{{ $labels.job }}" "node" "{{ $labels.node }}" "pod" "{{ $labels.pod }}") -}} {{- $_ := set $dict "podSelector" (printf "%s-([1-9][0-9]*)$" (include "cluster.fullname" .)) -}} {{- $_ := set $dict "Values" .Values -}} {{- $_ := set $dict "Template" .Template -}} {{- range $path, $_ := .Files.Glob "prometheus_rules/**.yaml" }} - - {{ tpl ($.Files.Get $path) $dict | nindent 10 | trim -}} + {{- $tpl := tpl ($.Files.Get $path) $dict | nindent 10 | trim -}} + {{- with $tpl }} + - {{ $tpl }} + {{- end -}} {{- end -}} {{ end }} From b7e9e3e37216c4eb5a354d938774bcce1bdb2bba Mon Sep 17 00:00:00 2001 From: Rajeesh C V Date: Wed, 3 Apr 2024 15:17:57 +0530 Subject: [PATCH 54/87] feat(cloudnative-pg): Support for additional labels in PodMonitor (#196) Signed-off-by: Rajeesh Signed-off-by: Itay Grudev Signed-off-by: Itay Grudev Co-authored-by: Itay Grudev Co-authored-by: Itay Grudev --- charts/cloudnative-pg/README.md | 1 + charts/cloudnative-pg/templates/podmonitor.yaml | 5 ++++- charts/cloudnative-pg/values.schema.json | 3 +++ charts/cloudnative-pg/values.yaml | 4 ++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index a58caadb3..bc02f7165 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -49,6 +49,7 @@ CloudNativePG Operator Helm Chart | monitoring.grafanaDashboard.sidecarLabel | string | `"grafana_dashboard"` | Label that ConfigMaps should have to be loaded as dashboards. DEPRECATED: Use labels instead. | | monitoring.grafanaDashboard.sidecarLabelValue | string | `"1"` | Label value that ConfigMaps should have to be loaded as dashboards. DEPRECATED: Use labels instead. | | monitoring.podMonitorEnabled | bool | `false` | Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. | +| monitoring.podMonitorAdditionalLabels | object | `{}` | Additional labels for the podMonitor | | monitoringQueriesConfigMap.name | string | `"cnpg-default-monitoring"` | The name of the default monitoring configmap. | | monitoringQueriesConfigMap.queries | string | `"backends:\n query: |\n SELECT sa.datname\n , sa.usename\n , sa.application_name\n , states.state\n , COALESCE(sa.count, 0) AS total\n , COALESCE(sa.max_tx_secs, 0) AS max_tx_duration_seconds\n FROM ( VALUES ('active')\n , ('idle')\n , ('idle in transaction')\n , ('idle in transaction (aborted)')\n , ('fastpath function call')\n , ('disabled')\n ) AS states(state)\n LEFT JOIN (\n SELECT datname\n , state\n , usename\n , COALESCE(application_name, '') AS application_name\n , COUNT(*)\n , COALESCE(EXTRACT (EPOCH FROM (max(now() - xact_start))), 0) AS max_tx_secs\n FROM pg_catalog.pg_stat_activity\n GROUP BY datname, state, usename, application_name\n ) sa ON states.state = sa.state\n WHERE sa.usename IS NOT NULL\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - usename:\n usage: \"LABEL\"\n description: \"Name of the user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - state:\n usage: \"LABEL\"\n description: \"State of the backend\"\n - total:\n usage: \"GAUGE\"\n description: \"Number of backends\"\n - max_tx_duration_seconds:\n usage: \"GAUGE\"\n description: \"Maximum duration of a transaction in seconds\"\n\nbackends_waiting:\n query: |\n SELECT count(*) AS total\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks\n ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n WHERE NOT blocked_locks.granted\n metrics:\n - total:\n usage: \"GAUGE\"\n description: \"Total number of backends that are currently waiting on other queries\"\n\npg_database:\n query: |\n SELECT datname\n , pg_catalog.pg_database_size(datname) AS size_bytes\n , pg_catalog.age(datfrozenxid) AS xid_age\n , pg_catalog.mxid_age(datminmxid) AS mxid_age\n FROM pg_catalog.pg_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - size_bytes:\n usage: \"GAUGE\"\n description: \"Disk space used by the database\"\n - xid_age:\n usage: \"GAUGE\"\n description: \"Number of transactions from the frozen XID to the current one\"\n - mxid_age:\n usage: \"GAUGE\"\n description: \"Number of multiple transactions (Multixact) from the frozen XID to the current one\"\n\npg_postmaster:\n query: |\n SELECT EXTRACT(EPOCH FROM pg_postmaster_start_time) AS start_time\n FROM pg_catalog.pg_postmaster_start_time()\n metrics:\n - start_time:\n usage: \"GAUGE\"\n description: \"Time at which postgres started (based on epoch)\"\n\npg_replication:\n query: \"SELECT CASE WHEN (\n NOT pg_catalog.pg_is_in_recovery()\n OR pg_catalog.pg_last_wal_receive_lsn() = pg_catalog.pg_last_wal_replay_lsn())\n THEN 0\n ELSE GREATEST (0,\n EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp())))\n END AS lag,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up,\n (SELECT count(*) FROM pg_catalog.pg_stat_replication) AS streaming_replicas\"\n metrics:\n - lag:\n usage: \"GAUGE\"\n description: \"Replication lag behind primary in seconds\"\n - in_recovery:\n usage: \"GAUGE\"\n description: \"Whether the instance is in recovery\"\n - is_wal_receiver_up:\n usage: \"GAUGE\"\n description: \"Whether the instance wal_receiver is up\"\n - streaming_replicas:\n usage: \"GAUGE\"\n description: \"Number of streaming replicas connected to the instance\"\n\npg_replication_slots:\n query: |\n SELECT slot_name,\n slot_type,\n database,\n active,\n (CASE pg_catalog.pg_is_in_recovery()\n WHEN TRUE THEN pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_last_wal_receive_lsn(), restart_lsn)\n ELSE pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn)\n END) as pg_wal_lsn_diff\n FROM pg_catalog.pg_replication_slots\n WHERE NOT temporary\n metrics:\n - slot_name:\n usage: \"LABEL\"\n description: \"Name of the replication slot\"\n - slot_type:\n usage: \"LABEL\"\n description: \"Type of the replication slot\"\n - database:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - active:\n usage: \"GAUGE\"\n description: \"Flag indicating whether the slot is active\"\n - pg_wal_lsn_diff:\n usage: \"GAUGE\"\n description: \"Replication lag in bytes\"\n\npg_stat_archiver:\n query: |\n SELECT archived_count\n , failed_count\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_archived_time)), -1) AS seconds_since_last_archival\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_failed_time)), -1) AS seconds_since_last_failure\n , COALESCE(EXTRACT(EPOCH FROM last_archived_time), -1) AS last_archived_time\n , COALESCE(EXTRACT(EPOCH FROM last_failed_time), -1) AS last_failed_time\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_archived_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_archived_wal_start_lsn\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_failed_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_failed_wal_start_lsn\n , EXTRACT(EPOCH FROM stats_reset) AS stats_reset_time\n FROM pg_catalog.pg_stat_archiver\n metrics:\n - archived_count:\n usage: \"COUNTER\"\n description: \"Number of WAL files that have been successfully archived\"\n - failed_count:\n usage: \"COUNTER\"\n description: \"Number of failed attempts for archiving WAL files\"\n - seconds_since_last_archival:\n usage: \"GAUGE\"\n description: \"Seconds since the last successful archival operation\"\n - seconds_since_last_failure:\n usage: \"GAUGE\"\n description: \"Seconds since the last failed archival operation\"\n - last_archived_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving succeeded\"\n - last_failed_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving failed\"\n - last_archived_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Archived WAL start LSN\"\n - last_failed_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Last failed WAL LSN\"\n - stats_reset_time:\n usage: \"GAUGE\"\n description: \"Time at which these statistics were last reset\"\n\npg_stat_bgwriter:\n query: |\n SELECT checkpoints_timed\n , checkpoints_req\n , checkpoint_write_time\n , checkpoint_sync_time\n , buffers_checkpoint\n , buffers_clean\n , maxwritten_clean\n , buffers_backend\n , buffers_backend_fsync\n , buffers_alloc\n FROM pg_catalog.pg_stat_bgwriter\n metrics:\n - checkpoints_timed:\n usage: \"COUNTER\"\n description: \"Number of scheduled checkpoints that have been performed\"\n - checkpoints_req:\n usage: \"COUNTER\"\n description: \"Number of requested checkpoints that have been performed\"\n - checkpoint_write_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds\"\n - checkpoint_sync_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds\"\n - buffers_checkpoint:\n usage: \"COUNTER\"\n description: \"Number of buffers written during checkpoints\"\n - buffers_clean:\n usage: \"COUNTER\"\n description: \"Number of buffers written by the background writer\"\n - maxwritten_clean:\n usage: \"COUNTER\"\n description: \"Number of times the background writer stopped a cleaning scan because it had written too many buffers\"\n - buffers_backend:\n usage: \"COUNTER\"\n description: \"Number of buffers written directly by a backend\"\n - buffers_backend_fsync:\n usage: \"COUNTER\"\n description: \"Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)\"\n - buffers_alloc:\n usage: \"COUNTER\"\n description: \"Number of buffers allocated\"\n\npg_stat_database:\n query: |\n SELECT datname\n , xact_commit\n , xact_rollback\n , blks_read\n , blks_hit\n , tup_returned\n , tup_fetched\n , tup_inserted\n , tup_updated\n , tup_deleted\n , conflicts\n , temp_files\n , temp_bytes\n , deadlocks\n , blk_read_time\n , blk_write_time\n FROM pg_catalog.pg_stat_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of this database\"\n - xact_commit:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been committed\"\n - xact_rollback:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been rolled back\"\n - blks_read:\n usage: \"COUNTER\"\n description: \"Number of disk blocks read in this database\"\n - blks_hit:\n usage: \"COUNTER\"\n description: \"Number of times disk blocks were found already in the buffer cache, so that a read was not necessary (this only includes hits in the PostgreSQL buffer cache, not the operating system's file system cache)\"\n - tup_returned:\n usage: \"COUNTER\"\n description: \"Number of rows returned by queries in this database\"\n - tup_fetched:\n usage: \"COUNTER\"\n description: \"Number of rows fetched by queries in this database\"\n - tup_inserted:\n usage: \"COUNTER\"\n description: \"Number of rows inserted by queries in this database\"\n - tup_updated:\n usage: \"COUNTER\"\n description: \"Number of rows updated by queries in this database\"\n - tup_deleted:\n usage: \"COUNTER\"\n description: \"Number of rows deleted by queries in this database\"\n - conflicts:\n usage: \"COUNTER\"\n description: \"Number of queries canceled due to conflicts with recovery in this database\"\n - temp_files:\n usage: \"COUNTER\"\n description: \"Number of temporary files created by queries in this database\"\n - temp_bytes:\n usage: \"COUNTER\"\n description: \"Total amount of data written to temporary files by queries in this database\"\n - deadlocks:\n usage: \"COUNTER\"\n description: \"Number of deadlocks detected in this database\"\n - blk_read_time:\n usage: \"COUNTER\"\n description: \"Time spent reading data file blocks by backends in this database, in milliseconds\"\n - blk_write_time:\n usage: \"COUNTER\"\n description: \"Time spent writing data file blocks by backends in this database, in milliseconds\"\n\npg_stat_replication:\n primary: true\n query: |\n SELECT usename\n , COALESCE(application_name, '') AS application_name\n , COALESCE(client_addr::text, '') AS client_addr\n , COALESCE(client_port::text, '') AS client_port\n , EXTRACT(EPOCH FROM backend_start) AS backend_start\n , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), write_lsn) AS write_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), flush_lsn) AS flush_diff_bytes\n , COALESCE(pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), replay_lsn),0) AS replay_diff_bytes\n , COALESCE((EXTRACT(EPOCH FROM write_lag)),0)::float AS write_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM flush_lag)),0)::float AS flush_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM replay_lag)),0)::float AS replay_lag_seconds\n FROM pg_catalog.pg_stat_replication\n metrics:\n - usename:\n usage: \"LABEL\"\n description: \"Name of the replication user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - client_addr:\n usage: \"LABEL\"\n description: \"Client IP address\"\n - client_port:\n usage: \"LABEL\"\n description: \"Client TCP port\"\n - backend_start:\n usage: \"COUNTER\"\n description: \"Time when this process was started\"\n - backend_xmin_age:\n usage: \"COUNTER\"\n description: \"The age of this standby's xmin horizon\"\n - sent_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location sent on this connection\"\n - write_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location written to disk by this standby server\"\n - flush_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location flushed to disk by this standby server\"\n - replay_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location replayed into the database on this standby server\"\n - write_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written it\"\n - flush_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it\"\n - replay_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written, flushed and applied it\"\n\npg_settings:\n query: |\n SELECT name,\n CASE setting WHEN 'on' THEN '1' WHEN 'off' THEN '0' ELSE setting END AS setting\n FROM pg_catalog.pg_settings\n WHERE vartype IN ('integer', 'real', 'bool')\n ORDER BY 1\n metrics:\n - name:\n usage: \"LABEL\"\n description: \"Name of the setting\"\n - setting:\n usage: \"GAUGE\"\n description: \"Setting value\"\n"` | A string representation of a YAML defining monitoring queries. | | nameOverride | string | `""` | | diff --git a/charts/cloudnative-pg/templates/podmonitor.yaml b/charts/cloudnative-pg/templates/podmonitor.yaml index 89789127d..bae86ca8d 100644 --- a/charts/cloudnative-pg/templates/podmonitor.yaml +++ b/charts/cloudnative-pg/templates/podmonitor.yaml @@ -5,6 +5,9 @@ metadata: name: {{ include "cloudnative-pg.fullname" . }} labels: {{- include "cloudnative-pg.labels" . | nindent 4 }} + {{- with .Values.monitoring.podMonitorAdditionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end}} {{- with .Values.commonAnnotations }} annotations: {{- toYaml . | nindent 4 }} @@ -15,4 +18,4 @@ spec: {{- include "cloudnative-pg.selectorLabels" . | nindent 6 }} podMetricsEndpoints: - port: metrics -{{- end }} \ No newline at end of file +{{- end }} diff --git a/charts/cloudnative-pg/values.schema.json b/charts/cloudnative-pg/values.schema.json index 9d380c6ea..efce45658 100644 --- a/charts/cloudnative-pg/values.schema.json +++ b/charts/cloudnative-pg/values.schema.json @@ -123,6 +123,9 @@ }, "podMonitorEnabled": { "type": "boolean" + }, + "podMonitorAdditionalLabels": { + "type": "object" } } }, diff --git a/charts/cloudnative-pg/values.yaml b/charts/cloudnative-pg/values.yaml index 956f0a56e..17314ed7a 100644 --- a/charts/cloudnative-pg/values.yaml +++ b/charts/cloudnative-pg/values.yaml @@ -137,8 +137,12 @@ tolerations: [] affinity: {} monitoring: + # -- Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. podMonitorEnabled: false + # -- Additional labels for the podMonitor + podMonitorAdditionalLabels: {} + grafanaDashboard: create: false # -- Allows overriding the namespace where the ConfigMap will be created, defaulting to the same one as the Release. From 0179d31d0784fa41a04bdf54fd47c9fe0691ea2c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:23:35 +0300 Subject: [PATCH 55/87] Release cluster-v0.0.7 (#244) Signed-off-by: Itay Grudev Co-authored-by: Itay Grudev --- charts/cloudnative-pg/README.md | 2 +- charts/cloudnative-pg/values.schema.json | 6 +++--- charts/cluster/Chart.yaml | 2 +- charts/cluster/README.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index bc02f7165..c1da68bb7 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -48,8 +48,8 @@ CloudNativePG Operator Helm Chart | monitoring.grafanaDashboard.namespace | string | `""` | Allows overriding the namespace where the ConfigMap will be created, defaulting to the same one as the Release. | | monitoring.grafanaDashboard.sidecarLabel | string | `"grafana_dashboard"` | Label that ConfigMaps should have to be loaded as dashboards. DEPRECATED: Use labels instead. | | monitoring.grafanaDashboard.sidecarLabelValue | string | `"1"` | Label value that ConfigMaps should have to be loaded as dashboards. DEPRECATED: Use labels instead. | -| monitoring.podMonitorEnabled | bool | `false` | Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. | | monitoring.podMonitorAdditionalLabels | object | `{}` | Additional labels for the podMonitor | +| monitoring.podMonitorEnabled | bool | `false` | Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. | | monitoringQueriesConfigMap.name | string | `"cnpg-default-monitoring"` | The name of the default monitoring configmap. | | monitoringQueriesConfigMap.queries | string | `"backends:\n query: |\n SELECT sa.datname\n , sa.usename\n , sa.application_name\n , states.state\n , COALESCE(sa.count, 0) AS total\n , COALESCE(sa.max_tx_secs, 0) AS max_tx_duration_seconds\n FROM ( VALUES ('active')\n , ('idle')\n , ('idle in transaction')\n , ('idle in transaction (aborted)')\n , ('fastpath function call')\n , ('disabled')\n ) AS states(state)\n LEFT JOIN (\n SELECT datname\n , state\n , usename\n , COALESCE(application_name, '') AS application_name\n , COUNT(*)\n , COALESCE(EXTRACT (EPOCH FROM (max(now() - xact_start))), 0) AS max_tx_secs\n FROM pg_catalog.pg_stat_activity\n GROUP BY datname, state, usename, application_name\n ) sa ON states.state = sa.state\n WHERE sa.usename IS NOT NULL\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - usename:\n usage: \"LABEL\"\n description: \"Name of the user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - state:\n usage: \"LABEL\"\n description: \"State of the backend\"\n - total:\n usage: \"GAUGE\"\n description: \"Number of backends\"\n - max_tx_duration_seconds:\n usage: \"GAUGE\"\n description: \"Maximum duration of a transaction in seconds\"\n\nbackends_waiting:\n query: |\n SELECT count(*) AS total\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks\n ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n WHERE NOT blocked_locks.granted\n metrics:\n - total:\n usage: \"GAUGE\"\n description: \"Total number of backends that are currently waiting on other queries\"\n\npg_database:\n query: |\n SELECT datname\n , pg_catalog.pg_database_size(datname) AS size_bytes\n , pg_catalog.age(datfrozenxid) AS xid_age\n , pg_catalog.mxid_age(datminmxid) AS mxid_age\n FROM pg_catalog.pg_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - size_bytes:\n usage: \"GAUGE\"\n description: \"Disk space used by the database\"\n - xid_age:\n usage: \"GAUGE\"\n description: \"Number of transactions from the frozen XID to the current one\"\n - mxid_age:\n usage: \"GAUGE\"\n description: \"Number of multiple transactions (Multixact) from the frozen XID to the current one\"\n\npg_postmaster:\n query: |\n SELECT EXTRACT(EPOCH FROM pg_postmaster_start_time) AS start_time\n FROM pg_catalog.pg_postmaster_start_time()\n metrics:\n - start_time:\n usage: \"GAUGE\"\n description: \"Time at which postgres started (based on epoch)\"\n\npg_replication:\n query: \"SELECT CASE WHEN (\n NOT pg_catalog.pg_is_in_recovery()\n OR pg_catalog.pg_last_wal_receive_lsn() = pg_catalog.pg_last_wal_replay_lsn())\n THEN 0\n ELSE GREATEST (0,\n EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp())))\n END AS lag,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up,\n (SELECT count(*) FROM pg_catalog.pg_stat_replication) AS streaming_replicas\"\n metrics:\n - lag:\n usage: \"GAUGE\"\n description: \"Replication lag behind primary in seconds\"\n - in_recovery:\n usage: \"GAUGE\"\n description: \"Whether the instance is in recovery\"\n - is_wal_receiver_up:\n usage: \"GAUGE\"\n description: \"Whether the instance wal_receiver is up\"\n - streaming_replicas:\n usage: \"GAUGE\"\n description: \"Number of streaming replicas connected to the instance\"\n\npg_replication_slots:\n query: |\n SELECT slot_name,\n slot_type,\n database,\n active,\n (CASE pg_catalog.pg_is_in_recovery()\n WHEN TRUE THEN pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_last_wal_receive_lsn(), restart_lsn)\n ELSE pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn)\n END) as pg_wal_lsn_diff\n FROM pg_catalog.pg_replication_slots\n WHERE NOT temporary\n metrics:\n - slot_name:\n usage: \"LABEL\"\n description: \"Name of the replication slot\"\n - slot_type:\n usage: \"LABEL\"\n description: \"Type of the replication slot\"\n - database:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - active:\n usage: \"GAUGE\"\n description: \"Flag indicating whether the slot is active\"\n - pg_wal_lsn_diff:\n usage: \"GAUGE\"\n description: \"Replication lag in bytes\"\n\npg_stat_archiver:\n query: |\n SELECT archived_count\n , failed_count\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_archived_time)), -1) AS seconds_since_last_archival\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_failed_time)), -1) AS seconds_since_last_failure\n , COALESCE(EXTRACT(EPOCH FROM last_archived_time), -1) AS last_archived_time\n , COALESCE(EXTRACT(EPOCH FROM last_failed_time), -1) AS last_failed_time\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_archived_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_archived_wal_start_lsn\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_failed_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_failed_wal_start_lsn\n , EXTRACT(EPOCH FROM stats_reset) AS stats_reset_time\n FROM pg_catalog.pg_stat_archiver\n metrics:\n - archived_count:\n usage: \"COUNTER\"\n description: \"Number of WAL files that have been successfully archived\"\n - failed_count:\n usage: \"COUNTER\"\n description: \"Number of failed attempts for archiving WAL files\"\n - seconds_since_last_archival:\n usage: \"GAUGE\"\n description: \"Seconds since the last successful archival operation\"\n - seconds_since_last_failure:\n usage: \"GAUGE\"\n description: \"Seconds since the last failed archival operation\"\n - last_archived_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving succeeded\"\n - last_failed_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving failed\"\n - last_archived_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Archived WAL start LSN\"\n - last_failed_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Last failed WAL LSN\"\n - stats_reset_time:\n usage: \"GAUGE\"\n description: \"Time at which these statistics were last reset\"\n\npg_stat_bgwriter:\n query: |\n SELECT checkpoints_timed\n , checkpoints_req\n , checkpoint_write_time\n , checkpoint_sync_time\n , buffers_checkpoint\n , buffers_clean\n , maxwritten_clean\n , buffers_backend\n , buffers_backend_fsync\n , buffers_alloc\n FROM pg_catalog.pg_stat_bgwriter\n metrics:\n - checkpoints_timed:\n usage: \"COUNTER\"\n description: \"Number of scheduled checkpoints that have been performed\"\n - checkpoints_req:\n usage: \"COUNTER\"\n description: \"Number of requested checkpoints that have been performed\"\n - checkpoint_write_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds\"\n - checkpoint_sync_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds\"\n - buffers_checkpoint:\n usage: \"COUNTER\"\n description: \"Number of buffers written during checkpoints\"\n - buffers_clean:\n usage: \"COUNTER\"\n description: \"Number of buffers written by the background writer\"\n - maxwritten_clean:\n usage: \"COUNTER\"\n description: \"Number of times the background writer stopped a cleaning scan because it had written too many buffers\"\n - buffers_backend:\n usage: \"COUNTER\"\n description: \"Number of buffers written directly by a backend\"\n - buffers_backend_fsync:\n usage: \"COUNTER\"\n description: \"Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)\"\n - buffers_alloc:\n usage: \"COUNTER\"\n description: \"Number of buffers allocated\"\n\npg_stat_database:\n query: |\n SELECT datname\n , xact_commit\n , xact_rollback\n , blks_read\n , blks_hit\n , tup_returned\n , tup_fetched\n , tup_inserted\n , tup_updated\n , tup_deleted\n , conflicts\n , temp_files\n , temp_bytes\n , deadlocks\n , blk_read_time\n , blk_write_time\n FROM pg_catalog.pg_stat_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of this database\"\n - xact_commit:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been committed\"\n - xact_rollback:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been rolled back\"\n - blks_read:\n usage: \"COUNTER\"\n description: \"Number of disk blocks read in this database\"\n - blks_hit:\n usage: \"COUNTER\"\n description: \"Number of times disk blocks were found already in the buffer cache, so that a read was not necessary (this only includes hits in the PostgreSQL buffer cache, not the operating system's file system cache)\"\n - tup_returned:\n usage: \"COUNTER\"\n description: \"Number of rows returned by queries in this database\"\n - tup_fetched:\n usage: \"COUNTER\"\n description: \"Number of rows fetched by queries in this database\"\n - tup_inserted:\n usage: \"COUNTER\"\n description: \"Number of rows inserted by queries in this database\"\n - tup_updated:\n usage: \"COUNTER\"\n description: \"Number of rows updated by queries in this database\"\n - tup_deleted:\n usage: \"COUNTER\"\n description: \"Number of rows deleted by queries in this database\"\n - conflicts:\n usage: \"COUNTER\"\n description: \"Number of queries canceled due to conflicts with recovery in this database\"\n - temp_files:\n usage: \"COUNTER\"\n description: \"Number of temporary files created by queries in this database\"\n - temp_bytes:\n usage: \"COUNTER\"\n description: \"Total amount of data written to temporary files by queries in this database\"\n - deadlocks:\n usage: \"COUNTER\"\n description: \"Number of deadlocks detected in this database\"\n - blk_read_time:\n usage: \"COUNTER\"\n description: \"Time spent reading data file blocks by backends in this database, in milliseconds\"\n - blk_write_time:\n usage: \"COUNTER\"\n description: \"Time spent writing data file blocks by backends in this database, in milliseconds\"\n\npg_stat_replication:\n primary: true\n query: |\n SELECT usename\n , COALESCE(application_name, '') AS application_name\n , COALESCE(client_addr::text, '') AS client_addr\n , COALESCE(client_port::text, '') AS client_port\n , EXTRACT(EPOCH FROM backend_start) AS backend_start\n , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), write_lsn) AS write_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), flush_lsn) AS flush_diff_bytes\n , COALESCE(pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), replay_lsn),0) AS replay_diff_bytes\n , COALESCE((EXTRACT(EPOCH FROM write_lag)),0)::float AS write_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM flush_lag)),0)::float AS flush_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM replay_lag)),0)::float AS replay_lag_seconds\n FROM pg_catalog.pg_stat_replication\n metrics:\n - usename:\n usage: \"LABEL\"\n description: \"Name of the replication user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - client_addr:\n usage: \"LABEL\"\n description: \"Client IP address\"\n - client_port:\n usage: \"LABEL\"\n description: \"Client TCP port\"\n - backend_start:\n usage: \"COUNTER\"\n description: \"Time when this process was started\"\n - backend_xmin_age:\n usage: \"COUNTER\"\n description: \"The age of this standby's xmin horizon\"\n - sent_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location sent on this connection\"\n - write_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location written to disk by this standby server\"\n - flush_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location flushed to disk by this standby server\"\n - replay_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location replayed into the database on this standby server\"\n - write_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written it\"\n - flush_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it\"\n - replay_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written, flushed and applied it\"\n\npg_settings:\n query: |\n SELECT name,\n CASE setting WHEN 'on' THEN '1' WHEN 'off' THEN '0' ELSE setting END AS setting\n FROM pg_catalog.pg_settings\n WHERE vartype IN ('integer', 'real', 'bool')\n ORDER BY 1\n metrics:\n - name:\n usage: \"LABEL\"\n description: \"Name of the setting\"\n - setting:\n usage: \"GAUGE\"\n description: \"Setting value\"\n"` | A string representation of a YAML defining monitoring queries. | | nameOverride | string | `""` | | diff --git a/charts/cloudnative-pg/values.schema.json b/charts/cloudnative-pg/values.schema.json index efce45658..68434c745 100644 --- a/charts/cloudnative-pg/values.schema.json +++ b/charts/cloudnative-pg/values.schema.json @@ -121,11 +121,11 @@ } } }, - "podMonitorEnabled": { - "type": "boolean" - }, "podMonitorAdditionalLabels": { "type": "object" + }, + "podMonitorEnabled": { + "type": "boolean" } } }, diff --git a/charts/cluster/Chart.yaml b/charts/cluster/Chart.yaml index 5d61e8bb0..e9aca8321 100644 --- a/charts/cluster/Chart.yaml +++ b/charts/cluster/Chart.yaml @@ -18,7 +18,7 @@ name: cluster description: Deploys and manages a CloudNativePG cluster and its associated resources. icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: 0.0.6 +version: 0.0.7 sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 93412962a..745779e66 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -1,6 +1,6 @@ # cluster -![Version: 0.0.6](https://img.shields.io/badge/Version-0.0.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![Version: 0.0.7](https://img.shields.io/badge/Version-0.0.7-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) > **Warning** > ### This chart is under active development. From 7347bce3cb72d5197f5d6b7f442d82d40b3f690b Mon Sep 17 00:00:00 2001 From: Pieter <110168856+Pionerd@users.noreply.github.com> Date: Mon, 8 Apr 2024 23:14:57 +0200 Subject: [PATCH 56/87] bugfix(cluster): Propagate inheritFromAzureAD to the barman configuration (#251) Signed-off-by: Pieter <110168856+Pionerd@users.noreply.github.com> --- charts/cluster/templates/_barman_object_store.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/charts/cluster/templates/_barman_object_store.tpl b/charts/cluster/templates/_barman_object_store.tpl index f002800e5..4269ad951 100644 --- a/charts/cluster/templates/_barman_object_store.tpl +++ b/charts/cluster/templates/_barman_object_store.tpl @@ -33,7 +33,9 @@ destinationPath: "https://{{ required "You need to specify Azure storageAccount if destinationPath is not specified." .scope.azure.storageAccount }}.{{ .scope.azure.serviceName }}.core.windows.net/{{ .scope.azure.containerName }}{{ .scope.azure.path }}" {{- end }} azureCredentials: - {{- if .scope.azure.connectionString }} + {{- if .scope.azure.inheritFromAzureAD }} + inheritFromAzureAD: true + {{- else if .scope.azure.connectionString }} connectionString: name: {{ .chartFullname }}-backup-azure{{ .secretSuffix }}-creds key: AZURE_CONNECTION_STRING From 5f75eb4985ed75560b92d50e338f59b3e997ec78 Mon Sep 17 00:00:00 2001 From: JesseBot Date: Wed, 10 Apr 2024 21:44:42 +0200 Subject: [PATCH 57/87] Update charts/cluster/README.md - fix Getting Started doc markdown link by escaping space (#253) Signed-off-by: JesseBot --- charts/cluster/README.md | 2 +- charts/cluster/README.md.gotmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 745779e66..014a68cfc 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -51,7 +51,7 @@ helm upgrade --install cnpg \ cnpg/cluster ``` -A more detailed guide can be found here: [Getting Started](docs/Getting Started.md) +A more detailed guide can be found in the [Getting Started docs](<./docs/Getting Started.md>). Cluster Configuration --------------------- diff --git a/charts/cluster/README.md.gotmpl b/charts/cluster/README.md.gotmpl index 956e1431e..e1a4d2f05 100644 --- a/charts/cluster/README.md.gotmpl +++ b/charts/cluster/README.md.gotmpl @@ -58,7 +58,7 @@ helm upgrade --install cnpg \ cnpg/cluster ``` -A more detailed guide can be found here: [Getting Started](docs/Getting Started.md) +A more detailed guide can be found in the [Getting Started docs](<./docs/Getting Started.md>). Cluster Configuration From 59d85d961414a812077d305cfc1e53ad1d329c73 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 09:55:12 +0300 Subject: [PATCH 58/87] chore(deps): update sigstore/cosign-installer action to v3.5.0 (#254) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 5f3a0d207..aec13cdee 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -65,7 +65,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Install sigstore/cosign - uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0 + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 - name: Push charts to GHCR env: From e9e521bf66ef5fff4ee5a3f986a8ff1abef94871 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 00:53:46 +0300 Subject: [PATCH 59/87] chore(deps): update azure/setup-helm action to v4.2.0 (#255) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index aec13cdee..60c70c45f 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -33,7 +33,7 @@ jobs: echo "${{ secrets.PGP_KEY_PASSPHRASE }}" > /tmp/passphrase-file.txt - name: Set up Helm - uses: azure/setup-helm@b7246b12e77f7134dc2d460a3d5bad15bbe29390 # v4.1.0 + uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0 with: version: v3.14.1 From d1b8601f0fe69b732a36b4b02c941f5560ce380a Mon Sep 17 00:00:00 2001 From: crmendes Date: Fri, 19 Apr 2024 13:13:14 +0200 Subject: [PATCH 60/87] fix(cluster): fix cluster.certificates on values schema (#258) Co-authored-by: crmendes --- charts/cluster/values.schema.json | 2 +- charts/cluster/values.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 0c7c1bbe0..4f99fb242 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -168,7 +168,7 @@ "type": "object" }, "certificates": { - "type": "null" + "type": "object" }, "enableSuperuserAccess": { "type": "boolean" diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index 325d5f3cd..b7a5fd5c0 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -135,7 +135,7 @@ cluster: # -- The configuration for the CA and related certificates. # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-CertificatesConfiguration - certificates: + certificates: {} # -- When this option is enabled, the operator will use the SuperuserSecret to update the postgres user password. # If the secret is not present, the operator will automatically create one. From a6fa96741fa7b8777f1e8be03264c20ec8be9597 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:20:18 +0300 Subject: [PATCH 61/87] Release cluster-v0.0.8 (#259) Signed-off-by: Itay Grudev Co-authored-by: Itay Grudev --- charts/cluster/Chart.yaml | 2 +- charts/cluster/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/cluster/Chart.yaml b/charts/cluster/Chart.yaml index e9aca8321..b41a8aa5b 100644 --- a/charts/cluster/Chart.yaml +++ b/charts/cluster/Chart.yaml @@ -18,7 +18,7 @@ name: cluster description: Deploys and manages a CloudNativePG cluster and its associated resources. icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: 0.0.7 +version: 0.0.8 sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 014a68cfc..233649791 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -1,6 +1,6 @@ # cluster -![Version: 0.0.7](https://img.shields.io/badge/Version-0.0.7-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![Version: 0.0.8](https://img.shields.io/badge/Version-0.0.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) > **Warning** > ### This chart is under active development. @@ -148,7 +148,7 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | cluster.additionalLabels | object | `{}` | | | cluster.affinity | object | `{"topologyKey":"topology.kubernetes.io/zone"}` | Affinity/Anti-affinity rules for Pods. See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-AffinityConfiguration | | cluster.annotations | object | `{}` | | -| cluster.certificates | string | `nil` | The configuration for the CA and related certificates. See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-CertificatesConfiguration | +| cluster.certificates | object | `{}` | The configuration for the CA and related certificates. See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-CertificatesConfiguration | | cluster.enableSuperuserAccess | bool | `true` | When this option is enabled, the operator will use the SuperuserSecret to update the postgres user password. If the secret is not present, the operator will automatically create one. When this option is disabled, the operator will ignore the SuperuserSecret content, delete it when automatically created, and then blank the password of the postgres user by setting it to NULL. | | cluster.imageName | string | `""` | Name of the container image, supporting both tags (:) and digests for deterministic and repeatable deployments: :@sha256: | | cluster.imagePullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never or IfNotPresent. If not defined, it defaults to IfNotPresent. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | From 2728793a700452ebd0779d2c97c68e793bef3aa0 Mon Sep 17 00:00:00 2001 From: gpothier Date: Sat, 20 Apr 2024 14:56:59 -0400 Subject: [PATCH 62/87] feat(cluster): Make roles configurable (#247) Signed-off-by: Guillaume Pothier Co-authored-by: Itay Grudev --- charts/cluster/README.md | 1 + charts/cluster/templates/cluster.yaml | 6 ++++++ charts/cluster/values.schema.json | 3 +++ charts/cluster/values.yaml | 13 +++++++++++++ 4 files changed, 23 insertions(+) diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 233649791..747f9de61 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -168,6 +168,7 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | cluster.primaryUpdateStrategy | string | `"unsupervised"` | Strategy to follow to upgrade the primary server during a rolling update procedure, after all replicas have been successfully updated: it can be automated (unsupervised - default) or manual (supervised) | | cluster.priorityClassName | string | `""` | | | cluster.resources | object | `{}` | Resources requirements of every generated Pod. Please refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ for more information. We strongly advise you use the same setting for limits and requests so that your cluster pods are given a Guaranteed QoS. See: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ | +| cluster.roles | list | `[]` | This feature enables declarative management of existing roles, as well as the creation of new roles if they are not already present in the database. See: https://cloudnative-pg.io/documentation/current/declarative_role_management/ | | cluster.storage.size | string | `"8Gi"` | | | cluster.storage.storageClass | string | `""` | | | cluster.superuserSecret | string | `""` | | diff --git a/charts/cluster/templates/cluster.yaml b/charts/cluster/templates/cluster.yaml index 9634dc8f0..5ff0bb2fa 100644 --- a/charts/cluster/templates/cluster.yaml +++ b/charts/cluster/templates/cluster.yaml @@ -57,6 +57,12 @@ spec: {{- toYaml . | nindent 6 }} {{ end }} + managed: + {{- with .Values.cluster.roles }} + roles: + {{- toYaml . | nindent 6 }} + {{ end }} + monitoring: enablePodMonitor: {{ and .Values.cluster.monitoring.enabled .Values.cluster.monitoring.podMonitor.enabled }} {{- if not (empty .Values.cluster.monitoring.customQueries) }} diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 4f99fb242..7bab0a443 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -242,6 +242,9 @@ "resources": { "type": "object" }, + "roles": { + "type": "array" + }, "storage": { "type": "object", "properties": { diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index b7a5fd5c0..f2651018b 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -144,6 +144,19 @@ cluster: enableSuperuserAccess: true superuserSecret: "" + # -- This feature enables declarative management of existing roles, as well as the creation of new roles if they are not + # already present in the database. + # See: https://cloudnative-pg.io/documentation/current/declarative_role_management/ + roles: [] + # - name: dante + # ensure: present + # comment: Dante Alighieri + # login: true + # superuser: false + # inRoles: + # - pg_monitor + # - pg_signal_backend + monitoring: # -- Whether to enable monitoring enabled: false From 4636878bdbe2c632a32468dad1d4dde41aee1180 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 20:10:56 +0300 Subject: [PATCH 63/87] chore(deps): update actions/checkout action to v4.1.3 (#260) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- .github/workflows/release-pr.yml | 2 +- .github/workflows/release-publish.yml | 2 +- .github/workflows/tests-cluster-standalone.yml | 4 ++-- .github/workflows/tests-operator.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9e0fccaa5..50489d5ad 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: 0 diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 53608c6f7..9baae79f3 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Create Pull Request id: create-pr env: diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 60c70c45f..1b9a015a9 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: 0 # important for fetching all history to run comparison against diff --git a/.github/workflows/tests-cluster-standalone.yml b/.github/workflows/tests-cluster-standalone.yml index d162c84e3..5c17704d0 100644 --- a/.github/workflows/tests-cluster-standalone.yml +++ b/.github/workflows/tests-cluster-standalone.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: 0 @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: 0 diff --git a/.github/workflows/tests-operator.yml b/.github/workflows/tests-operator.yml index 3177e32f5..064306806 100644 --- a/.github/workflows/tests-operator.yml +++ b/.github/workflows/tests-operator.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: 0 From 15d5bd66ce8f81f3300642a3ab302501ad7a644b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:56:56 +0300 Subject: [PATCH 64/87] chore(deps): update helm/kind-action action to v1.10.0 (#261) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/actions/setup-kind/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-kind/action.yml b/.github/actions/setup-kind/action.yml index 347864b17..8d312e9e6 100644 --- a/.github/actions/setup-kind/action.yml +++ b/.github/actions/setup-kind/action.yml @@ -21,4 +21,4 @@ runs: uses: azure/setup-kubectl@901a10e89ea615cf61f57ac05cecdf23e7de06d8 # v3.2 - name: Create kind cluster - uses: helm/kind-action@99576bfa6ddf9a8e612d83b513da5a75875caced # v1.9.0 + uses: helm/kind-action@0025e74a8c7512023d06dc019c617aa3cf561fde # v1.10.0 From 9b9cdd86e14688af5e3e8344b44ee59f24441c7d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 13:45:12 +0200 Subject: [PATCH 65/87] Release cloudnative-pg-v0.21.0 (#266) Signed-off-by: Jaime Silvela Co-authored-by: Jaime Silvela --- charts/cloudnative-pg/Chart.yaml | 4 +- charts/cloudnative-pg/README.md | 4 +- .../cloudnative-pg/templates/crds/crds.yaml | 715 +++++++++++++++++- charts/cloudnative-pg/templates/rbac.yaml | 16 + charts/cloudnative-pg/values.yaml | 1 + 5 files changed, 734 insertions(+), 6 deletions(-) diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index a0024420c..fac787158 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,12 +18,12 @@ name: cloudnative-pg description: CloudNativePG Operator Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.20.2" +version: "0.21.0" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.22.2" +appVersion: "1.23.0" sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index c1da68bb7..4b31d7f17 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.20.2](https://img.shields.io/badge/Version-0.20.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.22.2](https://img.shields.io/badge/AppVersion-1.22.2-informational?style=flat-square) +![Version: 0.21.0](https://img.shields.io/badge/Version-0.21.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.0](https://img.shields.io/badge/AppVersion-1.23.0-informational?style=flat-square) CloudNativePG Operator Helm Chart @@ -51,7 +51,7 @@ CloudNativePG Operator Helm Chart | monitoring.podMonitorAdditionalLabels | object | `{}` | Additional labels for the podMonitor | | monitoring.podMonitorEnabled | bool | `false` | Specifies whether the monitoring should be enabled. Requires Prometheus Operator CRDs. | | monitoringQueriesConfigMap.name | string | `"cnpg-default-monitoring"` | The name of the default monitoring configmap. | -| monitoringQueriesConfigMap.queries | string | `"backends:\n query: |\n SELECT sa.datname\n , sa.usename\n , sa.application_name\n , states.state\n , COALESCE(sa.count, 0) AS total\n , COALESCE(sa.max_tx_secs, 0) AS max_tx_duration_seconds\n FROM ( VALUES ('active')\n , ('idle')\n , ('idle in transaction')\n , ('idle in transaction (aborted)')\n , ('fastpath function call')\n , ('disabled')\n ) AS states(state)\n LEFT JOIN (\n SELECT datname\n , state\n , usename\n , COALESCE(application_name, '') AS application_name\n , COUNT(*)\n , COALESCE(EXTRACT (EPOCH FROM (max(now() - xact_start))), 0) AS max_tx_secs\n FROM pg_catalog.pg_stat_activity\n GROUP BY datname, state, usename, application_name\n ) sa ON states.state = sa.state\n WHERE sa.usename IS NOT NULL\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - usename:\n usage: \"LABEL\"\n description: \"Name of the user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - state:\n usage: \"LABEL\"\n description: \"State of the backend\"\n - total:\n usage: \"GAUGE\"\n description: \"Number of backends\"\n - max_tx_duration_seconds:\n usage: \"GAUGE\"\n description: \"Maximum duration of a transaction in seconds\"\n\nbackends_waiting:\n query: |\n SELECT count(*) AS total\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks\n ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n WHERE NOT blocked_locks.granted\n metrics:\n - total:\n usage: \"GAUGE\"\n description: \"Total number of backends that are currently waiting on other queries\"\n\npg_database:\n query: |\n SELECT datname\n , pg_catalog.pg_database_size(datname) AS size_bytes\n , pg_catalog.age(datfrozenxid) AS xid_age\n , pg_catalog.mxid_age(datminmxid) AS mxid_age\n FROM pg_catalog.pg_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - size_bytes:\n usage: \"GAUGE\"\n description: \"Disk space used by the database\"\n - xid_age:\n usage: \"GAUGE\"\n description: \"Number of transactions from the frozen XID to the current one\"\n - mxid_age:\n usage: \"GAUGE\"\n description: \"Number of multiple transactions (Multixact) from the frozen XID to the current one\"\n\npg_postmaster:\n query: |\n SELECT EXTRACT(EPOCH FROM pg_postmaster_start_time) AS start_time\n FROM pg_catalog.pg_postmaster_start_time()\n metrics:\n - start_time:\n usage: \"GAUGE\"\n description: \"Time at which postgres started (based on epoch)\"\n\npg_replication:\n query: \"SELECT CASE WHEN (\n NOT pg_catalog.pg_is_in_recovery()\n OR pg_catalog.pg_last_wal_receive_lsn() = pg_catalog.pg_last_wal_replay_lsn())\n THEN 0\n ELSE GREATEST (0,\n EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp())))\n END AS lag,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up,\n (SELECT count(*) FROM pg_catalog.pg_stat_replication) AS streaming_replicas\"\n metrics:\n - lag:\n usage: \"GAUGE\"\n description: \"Replication lag behind primary in seconds\"\n - in_recovery:\n usage: \"GAUGE\"\n description: \"Whether the instance is in recovery\"\n - is_wal_receiver_up:\n usage: \"GAUGE\"\n description: \"Whether the instance wal_receiver is up\"\n - streaming_replicas:\n usage: \"GAUGE\"\n description: \"Number of streaming replicas connected to the instance\"\n\npg_replication_slots:\n query: |\n SELECT slot_name,\n slot_type,\n database,\n active,\n (CASE pg_catalog.pg_is_in_recovery()\n WHEN TRUE THEN pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_last_wal_receive_lsn(), restart_lsn)\n ELSE pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn)\n END) as pg_wal_lsn_diff\n FROM pg_catalog.pg_replication_slots\n WHERE NOT temporary\n metrics:\n - slot_name:\n usage: \"LABEL\"\n description: \"Name of the replication slot\"\n - slot_type:\n usage: \"LABEL\"\n description: \"Type of the replication slot\"\n - database:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - active:\n usage: \"GAUGE\"\n description: \"Flag indicating whether the slot is active\"\n - pg_wal_lsn_diff:\n usage: \"GAUGE\"\n description: \"Replication lag in bytes\"\n\npg_stat_archiver:\n query: |\n SELECT archived_count\n , failed_count\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_archived_time)), -1) AS seconds_since_last_archival\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_failed_time)), -1) AS seconds_since_last_failure\n , COALESCE(EXTRACT(EPOCH FROM last_archived_time), -1) AS last_archived_time\n , COALESCE(EXTRACT(EPOCH FROM last_failed_time), -1) AS last_failed_time\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_archived_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_archived_wal_start_lsn\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_failed_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_failed_wal_start_lsn\n , EXTRACT(EPOCH FROM stats_reset) AS stats_reset_time\n FROM pg_catalog.pg_stat_archiver\n metrics:\n - archived_count:\n usage: \"COUNTER\"\n description: \"Number of WAL files that have been successfully archived\"\n - failed_count:\n usage: \"COUNTER\"\n description: \"Number of failed attempts for archiving WAL files\"\n - seconds_since_last_archival:\n usage: \"GAUGE\"\n description: \"Seconds since the last successful archival operation\"\n - seconds_since_last_failure:\n usage: \"GAUGE\"\n description: \"Seconds since the last failed archival operation\"\n - last_archived_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving succeeded\"\n - last_failed_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving failed\"\n - last_archived_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Archived WAL start LSN\"\n - last_failed_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Last failed WAL LSN\"\n - stats_reset_time:\n usage: \"GAUGE\"\n description: \"Time at which these statistics were last reset\"\n\npg_stat_bgwriter:\n query: |\n SELECT checkpoints_timed\n , checkpoints_req\n , checkpoint_write_time\n , checkpoint_sync_time\n , buffers_checkpoint\n , buffers_clean\n , maxwritten_clean\n , buffers_backend\n , buffers_backend_fsync\n , buffers_alloc\n FROM pg_catalog.pg_stat_bgwriter\n metrics:\n - checkpoints_timed:\n usage: \"COUNTER\"\n description: \"Number of scheduled checkpoints that have been performed\"\n - checkpoints_req:\n usage: \"COUNTER\"\n description: \"Number of requested checkpoints that have been performed\"\n - checkpoint_write_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds\"\n - checkpoint_sync_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds\"\n - buffers_checkpoint:\n usage: \"COUNTER\"\n description: \"Number of buffers written during checkpoints\"\n - buffers_clean:\n usage: \"COUNTER\"\n description: \"Number of buffers written by the background writer\"\n - maxwritten_clean:\n usage: \"COUNTER\"\n description: \"Number of times the background writer stopped a cleaning scan because it had written too many buffers\"\n - buffers_backend:\n usage: \"COUNTER\"\n description: \"Number of buffers written directly by a backend\"\n - buffers_backend_fsync:\n usage: \"COUNTER\"\n description: \"Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)\"\n - buffers_alloc:\n usage: \"COUNTER\"\n description: \"Number of buffers allocated\"\n\npg_stat_database:\n query: |\n SELECT datname\n , xact_commit\n , xact_rollback\n , blks_read\n , blks_hit\n , tup_returned\n , tup_fetched\n , tup_inserted\n , tup_updated\n , tup_deleted\n , conflicts\n , temp_files\n , temp_bytes\n , deadlocks\n , blk_read_time\n , blk_write_time\n FROM pg_catalog.pg_stat_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of this database\"\n - xact_commit:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been committed\"\n - xact_rollback:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been rolled back\"\n - blks_read:\n usage: \"COUNTER\"\n description: \"Number of disk blocks read in this database\"\n - blks_hit:\n usage: \"COUNTER\"\n description: \"Number of times disk blocks were found already in the buffer cache, so that a read was not necessary (this only includes hits in the PostgreSQL buffer cache, not the operating system's file system cache)\"\n - tup_returned:\n usage: \"COUNTER\"\n description: \"Number of rows returned by queries in this database\"\n - tup_fetched:\n usage: \"COUNTER\"\n description: \"Number of rows fetched by queries in this database\"\n - tup_inserted:\n usage: \"COUNTER\"\n description: \"Number of rows inserted by queries in this database\"\n - tup_updated:\n usage: \"COUNTER\"\n description: \"Number of rows updated by queries in this database\"\n - tup_deleted:\n usage: \"COUNTER\"\n description: \"Number of rows deleted by queries in this database\"\n - conflicts:\n usage: \"COUNTER\"\n description: \"Number of queries canceled due to conflicts with recovery in this database\"\n - temp_files:\n usage: \"COUNTER\"\n description: \"Number of temporary files created by queries in this database\"\n - temp_bytes:\n usage: \"COUNTER\"\n description: \"Total amount of data written to temporary files by queries in this database\"\n - deadlocks:\n usage: \"COUNTER\"\n description: \"Number of deadlocks detected in this database\"\n - blk_read_time:\n usage: \"COUNTER\"\n description: \"Time spent reading data file blocks by backends in this database, in milliseconds\"\n - blk_write_time:\n usage: \"COUNTER\"\n description: \"Time spent writing data file blocks by backends in this database, in milliseconds\"\n\npg_stat_replication:\n primary: true\n query: |\n SELECT usename\n , COALESCE(application_name, '') AS application_name\n , COALESCE(client_addr::text, '') AS client_addr\n , COALESCE(client_port::text, '') AS client_port\n , EXTRACT(EPOCH FROM backend_start) AS backend_start\n , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), write_lsn) AS write_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), flush_lsn) AS flush_diff_bytes\n , COALESCE(pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), replay_lsn),0) AS replay_diff_bytes\n , COALESCE((EXTRACT(EPOCH FROM write_lag)),0)::float AS write_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM flush_lag)),0)::float AS flush_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM replay_lag)),0)::float AS replay_lag_seconds\n FROM pg_catalog.pg_stat_replication\n metrics:\n - usename:\n usage: \"LABEL\"\n description: \"Name of the replication user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - client_addr:\n usage: \"LABEL\"\n description: \"Client IP address\"\n - client_port:\n usage: \"LABEL\"\n description: \"Client TCP port\"\n - backend_start:\n usage: \"COUNTER\"\n description: \"Time when this process was started\"\n - backend_xmin_age:\n usage: \"COUNTER\"\n description: \"The age of this standby's xmin horizon\"\n - sent_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location sent on this connection\"\n - write_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location written to disk by this standby server\"\n - flush_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location flushed to disk by this standby server\"\n - replay_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location replayed into the database on this standby server\"\n - write_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written it\"\n - flush_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it\"\n - replay_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written, flushed and applied it\"\n\npg_settings:\n query: |\n SELECT name,\n CASE setting WHEN 'on' THEN '1' WHEN 'off' THEN '0' ELSE setting END AS setting\n FROM pg_catalog.pg_settings\n WHERE vartype IN ('integer', 'real', 'bool')\n ORDER BY 1\n metrics:\n - name:\n usage: \"LABEL\"\n description: \"Name of the setting\"\n - setting:\n usage: \"GAUGE\"\n description: \"Setting value\"\n"` | A string representation of a YAML defining monitoring queries. | +| monitoringQueriesConfigMap.queries | string | `"backends:\n query: |\n SELECT sa.datname\n , sa.usename\n , sa.application_name\n , states.state\n , COALESCE(sa.count, 0) AS total\n , COALESCE(sa.max_tx_secs, 0) AS max_tx_duration_seconds\n FROM ( VALUES ('active')\n , ('idle')\n , ('idle in transaction')\n , ('idle in transaction (aborted)')\n , ('fastpath function call')\n , ('disabled')\n ) AS states(state)\n LEFT JOIN (\n SELECT datname\n , state\n , usename\n , COALESCE(application_name, '') AS application_name\n , COUNT(*)\n , COALESCE(EXTRACT (EPOCH FROM (max(now() - xact_start))), 0) AS max_tx_secs\n FROM pg_catalog.pg_stat_activity\n GROUP BY datname, state, usename, application_name\n ) sa ON states.state = sa.state\n WHERE sa.usename IS NOT NULL\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - usename:\n usage: \"LABEL\"\n description: \"Name of the user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - state:\n usage: \"LABEL\"\n description: \"State of the backend\"\n - total:\n usage: \"GAUGE\"\n description: \"Number of backends\"\n - max_tx_duration_seconds:\n usage: \"GAUGE\"\n description: \"Maximum duration of a transaction in seconds\"\n\nbackends_waiting:\n query: |\n SELECT count(*) AS total\n FROM pg_catalog.pg_locks blocked_locks\n JOIN pg_catalog.pg_locks blocking_locks\n ON blocking_locks.locktype = blocked_locks.locktype\n AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database\n AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n AND blocking_locks.pid != blocked_locks.pid\n JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n WHERE NOT blocked_locks.granted\n metrics:\n - total:\n usage: \"GAUGE\"\n description: \"Total number of backends that are currently waiting on other queries\"\n\npg_database:\n query: |\n SELECT datname\n , pg_catalog.pg_database_size(datname) AS size_bytes\n , pg_catalog.age(datfrozenxid) AS xid_age\n , pg_catalog.mxid_age(datminmxid) AS mxid_age\n FROM pg_catalog.pg_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - size_bytes:\n usage: \"GAUGE\"\n description: \"Disk space used by the database\"\n - xid_age:\n usage: \"GAUGE\"\n description: \"Number of transactions from the frozen XID to the current one\"\n - mxid_age:\n usage: \"GAUGE\"\n description: \"Number of multiple transactions (Multixact) from the frozen XID to the current one\"\n\npg_postmaster:\n query: |\n SELECT EXTRACT(EPOCH FROM pg_postmaster_start_time) AS start_time\n FROM pg_catalog.pg_postmaster_start_time()\n metrics:\n - start_time:\n usage: \"GAUGE\"\n description: \"Time at which postgres started (based on epoch)\"\n\npg_replication:\n query: \"SELECT CASE WHEN (\n NOT pg_catalog.pg_is_in_recovery()\n OR pg_catalog.pg_last_wal_receive_lsn() = pg_catalog.pg_last_wal_replay_lsn())\n THEN 0\n ELSE GREATEST (0,\n EXTRACT(EPOCH FROM (now() - pg_catalog.pg_last_xact_replay_timestamp())))\n END AS lag,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n EXISTS (TABLE pg_stat_wal_receiver) AS is_wal_receiver_up,\n (SELECT count(*) FROM pg_catalog.pg_stat_replication) AS streaming_replicas\"\n metrics:\n - lag:\n usage: \"GAUGE\"\n description: \"Replication lag behind primary in seconds\"\n - in_recovery:\n usage: \"GAUGE\"\n description: \"Whether the instance is in recovery\"\n - is_wal_receiver_up:\n usage: \"GAUGE\"\n description: \"Whether the instance wal_receiver is up\"\n - streaming_replicas:\n usage: \"GAUGE\"\n description: \"Number of streaming replicas connected to the instance\"\n\npg_replication_slots:\n query: |\n SELECT slot_name,\n slot_type,\n database,\n active,\n (CASE pg_catalog.pg_is_in_recovery()\n WHEN TRUE THEN pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_last_wal_receive_lsn(), restart_lsn)\n ELSE pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), restart_lsn)\n END) as pg_wal_lsn_diff\n FROM pg_catalog.pg_replication_slots\n WHERE NOT temporary\n metrics:\n - slot_name:\n usage: \"LABEL\"\n description: \"Name of the replication slot\"\n - slot_type:\n usage: \"LABEL\"\n description: \"Type of the replication slot\"\n - database:\n usage: \"LABEL\"\n description: \"Name of the database\"\n - active:\n usage: \"GAUGE\"\n description: \"Flag indicating whether the slot is active\"\n - pg_wal_lsn_diff:\n usage: \"GAUGE\"\n description: \"Replication lag in bytes\"\n\npg_stat_archiver:\n query: |\n SELECT archived_count\n , failed_count\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_archived_time)), -1) AS seconds_since_last_archival\n , COALESCE(EXTRACT(EPOCH FROM (now() - last_failed_time)), -1) AS seconds_since_last_failure\n , COALESCE(EXTRACT(EPOCH FROM last_archived_time), -1) AS last_archived_time\n , COALESCE(EXTRACT(EPOCH FROM last_failed_time), -1) AS last_failed_time\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_archived_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_archived_wal_start_lsn\n , COALESCE(CAST(CAST('x'||pg_catalog.right(pg_catalog.split_part(last_failed_wal, '.', 1), 16) AS pg_catalog.bit(64)) AS pg_catalog.int8), -1) AS last_failed_wal_start_lsn\n , EXTRACT(EPOCH FROM stats_reset) AS stats_reset_time\n FROM pg_catalog.pg_stat_archiver\n metrics:\n - archived_count:\n usage: \"COUNTER\"\n description: \"Number of WAL files that have been successfully archived\"\n - failed_count:\n usage: \"COUNTER\"\n description: \"Number of failed attempts for archiving WAL files\"\n - seconds_since_last_archival:\n usage: \"GAUGE\"\n description: \"Seconds since the last successful archival operation\"\n - seconds_since_last_failure:\n usage: \"GAUGE\"\n description: \"Seconds since the last failed archival operation\"\n - last_archived_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving succeeded\"\n - last_failed_time:\n usage: \"GAUGE\"\n description: \"Epoch of the last time WAL archiving failed\"\n - last_archived_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Archived WAL start LSN\"\n - last_failed_wal_start_lsn:\n usage: \"GAUGE\"\n description: \"Last failed WAL LSN\"\n - stats_reset_time:\n usage: \"GAUGE\"\n description: \"Time at which these statistics were last reset\"\n\npg_stat_bgwriter:\n runonserver: \"<17.0.0\"\n query: |\n SELECT checkpoints_timed\n , checkpoints_req\n , checkpoint_write_time\n , checkpoint_sync_time\n , buffers_checkpoint\n , buffers_clean\n , maxwritten_clean\n , buffers_backend\n , buffers_backend_fsync\n , buffers_alloc\n FROM pg_catalog.pg_stat_bgwriter\n metrics:\n - checkpoints_timed:\n usage: \"COUNTER\"\n description: \"Number of scheduled checkpoints that have been performed\"\n - checkpoints_req:\n usage: \"COUNTER\"\n description: \"Number of requested checkpoints that have been performed\"\n - checkpoint_write_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds\"\n - checkpoint_sync_time:\n usage: \"COUNTER\"\n description: \"Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds\"\n - buffers_checkpoint:\n usage: \"COUNTER\"\n description: \"Number of buffers written during checkpoints\"\n - buffers_clean:\n usage: \"COUNTER\"\n description: \"Number of buffers written by the background writer\"\n - maxwritten_clean:\n usage: \"COUNTER\"\n description: \"Number of times the background writer stopped a cleaning scan because it had written too many buffers\"\n - buffers_backend:\n usage: \"COUNTER\"\n description: \"Number of buffers written directly by a backend\"\n - buffers_backend_fsync:\n usage: \"COUNTER\"\n description: \"Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)\"\n - buffers_alloc:\n usage: \"COUNTER\"\n description: \"Number of buffers allocated\"\n\npg_stat_database:\n query: |\n SELECT datname\n , xact_commit\n , xact_rollback\n , blks_read\n , blks_hit\n , tup_returned\n , tup_fetched\n , tup_inserted\n , tup_updated\n , tup_deleted\n , conflicts\n , temp_files\n , temp_bytes\n , deadlocks\n , blk_read_time\n , blk_write_time\n FROM pg_catalog.pg_stat_database\n metrics:\n - datname:\n usage: \"LABEL\"\n description: \"Name of this database\"\n - xact_commit:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been committed\"\n - xact_rollback:\n usage: \"COUNTER\"\n description: \"Number of transactions in this database that have been rolled back\"\n - blks_read:\n usage: \"COUNTER\"\n description: \"Number of disk blocks read in this database\"\n - blks_hit:\n usage: \"COUNTER\"\n description: \"Number of times disk blocks were found already in the buffer cache, so that a read was not necessary (this only includes hits in the PostgreSQL buffer cache, not the operating system's file system cache)\"\n - tup_returned:\n usage: \"COUNTER\"\n description: \"Number of rows returned by queries in this database\"\n - tup_fetched:\n usage: \"COUNTER\"\n description: \"Number of rows fetched by queries in this database\"\n - tup_inserted:\n usage: \"COUNTER\"\n description: \"Number of rows inserted by queries in this database\"\n - tup_updated:\n usage: \"COUNTER\"\n description: \"Number of rows updated by queries in this database\"\n - tup_deleted:\n usage: \"COUNTER\"\n description: \"Number of rows deleted by queries in this database\"\n - conflicts:\n usage: \"COUNTER\"\n description: \"Number of queries canceled due to conflicts with recovery in this database\"\n - temp_files:\n usage: \"COUNTER\"\n description: \"Number of temporary files created by queries in this database\"\n - temp_bytes:\n usage: \"COUNTER\"\n description: \"Total amount of data written to temporary files by queries in this database\"\n - deadlocks:\n usage: \"COUNTER\"\n description: \"Number of deadlocks detected in this database\"\n - blk_read_time:\n usage: \"COUNTER\"\n description: \"Time spent reading data file blocks by backends in this database, in milliseconds\"\n - blk_write_time:\n usage: \"COUNTER\"\n description: \"Time spent writing data file blocks by backends in this database, in milliseconds\"\n\npg_stat_replication:\n primary: true\n query: |\n SELECT usename\n , COALESCE(application_name, '') AS application_name\n , COALESCE(client_addr::text, '') AS client_addr\n , COALESCE(client_port::text, '') AS client_port\n , EXTRACT(EPOCH FROM backend_start) AS backend_start\n , COALESCE(pg_catalog.age(backend_xmin), 0) AS backend_xmin_age\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), sent_lsn) AS sent_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), write_lsn) AS write_diff_bytes\n , pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), flush_lsn) AS flush_diff_bytes\n , COALESCE(pg_catalog.pg_wal_lsn_diff(pg_catalog.pg_current_wal_lsn(), replay_lsn),0) AS replay_diff_bytes\n , COALESCE((EXTRACT(EPOCH FROM write_lag)),0)::float AS write_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM flush_lag)),0)::float AS flush_lag_seconds\n , COALESCE((EXTRACT(EPOCH FROM replay_lag)),0)::float AS replay_lag_seconds\n FROM pg_catalog.pg_stat_replication\n metrics:\n - usename:\n usage: \"LABEL\"\n description: \"Name of the replication user\"\n - application_name:\n usage: \"LABEL\"\n description: \"Name of the application\"\n - client_addr:\n usage: \"LABEL\"\n description: \"Client IP address\"\n - client_port:\n usage: \"LABEL\"\n description: \"Client TCP port\"\n - backend_start:\n usage: \"COUNTER\"\n description: \"Time when this process was started\"\n - backend_xmin_age:\n usage: \"COUNTER\"\n description: \"The age of this standby's xmin horizon\"\n - sent_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location sent on this connection\"\n - write_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location written to disk by this standby server\"\n - flush_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location flushed to disk by this standby server\"\n - replay_diff_bytes:\n usage: \"GAUGE\"\n description: \"Difference in bytes from the last write-ahead log location replayed into the database on this standby server\"\n - write_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written it\"\n - flush_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it\"\n - replay_lag_seconds:\n usage: \"GAUGE\"\n description: \"Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written, flushed and applied it\"\n\npg_settings:\n query: |\n SELECT name,\n CASE setting WHEN 'on' THEN '1' WHEN 'off' THEN '0' ELSE setting END AS setting\n FROM pg_catalog.pg_settings\n WHERE vartype IN ('integer', 'real', 'bool')\n ORDER BY 1\n metrics:\n - name:\n usage: \"LABEL\"\n description: \"Name of the setting\"\n - setting:\n usage: \"GAUGE\"\n description: \"Setting value\"\n"` | A string representation of a YAML defining monitoring queries. | | nameOverride | string | `""` | | | nodeSelector | object | `{}` | Nodeselector for the operator to be installed. | | podAnnotations | object | `{}` | Annotations to be added to the pod. | diff --git a/charts/cloudnative-pg/templates/crds/crds.yaml b/charts/cloudnative-pg/templates/crds/crds.yaml index 9607beb4e..0d4ea03f0 100644 --- a/charts/cloudnative-pg/templates/crds/crds.yaml +++ b/charts/cloudnative-pg/templates/crds/crds.yaml @@ -70,11 +70,12 @@ spec: method: default: barmanObjectStore description: |- - The backup method to be used, possible options are `barmanObjectStore` - and `volumeSnapshot`. Defaults to: `barmanObjectStore`. + The backup method to be used, possible options are `barmanObjectStore`, + `volumeSnapshot` or `plugin`. Defaults to: `barmanObjectStore`. enum: - barmanObjectStore - volumeSnapshot + - plugin type: string online: description: |- @@ -109,6 +110,23 @@ spec: an immediate segment switch. type: boolean type: object + pluginConfiguration: + description: Configuration parameters passed to the plugin managing + this backup + properties: + name: + description: Name is the name of the plugin managing this backup + type: string + parameters: + additionalProperties: + type: string + description: |- + Parameters are the configuration parameters passed to the backup + plugin for this backup + type: object + required: + - name + type: object target: description: |- The policy to decide which instance should perform this backup. If empty, @@ -420,6 +438,88 @@ spec: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + helm.sh/resource-policy: keep + name: clusterimagecatalogs.postgresql.cnpg.io +spec: + group: postgresql.cnpg.io + names: + kind: ClusterImageCatalog + listKind: ClusterImageCatalogList + plural: clusterimagecatalogs + singular: clusterimagecatalog + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ClusterImageCatalog is the Schema for the clusterimagecatalogs + API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the desired behavior of the ClusterImageCatalog. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + images: + description: List of CatalogImages available in the catalog + items: + description: CatalogImage defines the image and major version + properties: + image: + description: The image reference + type: string + major: + description: The PostgreSQL major version of the image. Must + be unique within the catalog. + minimum: 10 + type: integer + required: + - image + - major + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-validations: + - message: Images must have unique major versions + rule: self.all(e, self.filter(f, f.major==e.major).size() == 1) + required: + - images + type: object + required: + - metadata + - spec + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.14.0 @@ -2297,6 +2397,18 @@ spec: description: description: Description of this PostgreSQL cluster type: string + enablePDB: + default: true + description: |- + Manage the `PodDisruptionBudget` resources within the cluster. When + configured as `true` (default setting), the pod disruption budgets + will safeguard the primary node from being terminated. Conversely, + setting it to `false` will result in the absence of any + `PodDisruptionBudget` resource, permitting the shutdown of all nodes + hosting the PostgreSQL cluster. This latter configuration is + advisable for any PostgreSQL cluster employed for + development/staging purposes. + type: boolean enableSuperuserAccess: default: false description: |- @@ -3150,6 +3262,40 @@ spec: to be unhealthy format: int32 type: integer + imageCatalogRef: + description: Defines the major PostgreSQL version we want to use within + an ImageCatalog + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + major: + description: The major version of PostgreSQL we want to use from + the ImageCatalog + type: integer + x-kubernetes-validations: + - message: Major is immutable + rule: self == oldSelf + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - major + - name + type: object + x-kubernetes-map-type: atomic + x-kubernetes-validations: + - message: Only image catalogs are supported + rule: self.kind == 'ImageCatalog' || self.kind == 'ClusterImageCatalog' + - message: Only image catalogs are supported + rule: self.apiGroup == 'postgresql.cnpg.io' imageName: description: |- Name of the container image, supporting both tags (`:`) @@ -3596,6 +3742,27 @@ spec: up again) or not (recreate it elsewhere - when `instances` >1) type: boolean type: object + plugins: + description: |- + The plugins configuration, containing + any plugin to be loaded with the corresponding configuration + items: + description: |- + PluginConfiguration specifies a plugin that need to be loaded for this + cluster to be reconciled + properties: + name: + description: Name is the plugin name + type: string + parameters: + additionalProperties: + type: string + description: Parameters is the configuration of the plugin + type: object + required: + - name + type: object + type: array postgresGID: default: 26 description: The GID of the `postgres` user inside the image, defaults @@ -4142,6 +4309,25 @@ spec: pattern: ^[0-9a-z_]*$ type: string type: object + synchronizeReplicas: + description: Configures the synchronization of the user defined + physical replication slots + properties: + enabled: + default: true + description: When set to true, every replication slot that + is on the primary is synchronized on each standby + type: boolean + excludePatterns: + description: List of regular expression patterns to match + the names of replication slots to be excluded (by default + empty) + items: + type: string + type: array + required: + - enabled + type: object updateInterval: default: 30 description: |- @@ -5182,6 +5368,9 @@ spec: required: - instances type: object + x-kubernetes-validations: + - message: imageName and imageCatalogRef are mutually exclusive + rule: '!(has(self.imageCatalogRef) && has(self.imageName))' status: description: |- Most recently observed status of the cluster. This data may not be up @@ -5391,6 +5580,9 @@ spec: items: type: string type: array + image: + description: Image contains the image name used by the pods + type: string initializingPVC: description: List of all the PVCs that are being initialized by this cluster @@ -5502,6 +5694,52 @@ spec: phaseReason: description: Reason for the current phase type: string + pluginStatus: + description: PluginStatus is the status of the loaded plugins + items: + description: PluginStatus is the status of a loaded plugin + properties: + backupCapabilities: + description: |- + BackupCapabilities are the list of capabilities of the + plugin regarding the Backup management + items: + type: string + type: array + capabilities: + description: |- + Capabilities are the list of capabilities of the + plugin + items: + type: string + type: array + name: + description: Name is the name of the plugin + type: string + operatorCapabilities: + description: |- + OperatorCapabilities are the list of capabilities of the + plugin regarding the reconciler + items: + type: string + type: array + version: + description: |- + Version is the version of the plugin loaded by the + latest reconciliation loop + type: string + walCapabilities: + description: |- + WALCapabilities are the list of capabilities of the + plugin regarding the WAL management + items: + type: string + type: array + required: + - name + - version + type: object + type: array poolerIntegrations: description: The integration needed by poolers referencing the cluster properties: @@ -5585,6 +5823,15 @@ spec: description: The resource version of the "postgres" user secret type: string type: object + switchReplicaClusterStatus: + description: SwitchReplicaClusterStatus is the status of the switch + to replica cluster + properties: + inProgress: + description: InProgress indicates if there is an ongoing procedure + of switching a cluster to a replica cluster. + type: boolean + type: object tablespacesStatus: description: TablespacesStatus reports the state of the declarative tablespaces in the cluster @@ -5672,6 +5919,87 @@ spec: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + helm.sh/resource-policy: keep + name: imagecatalogs.postgresql.cnpg.io +spec: + group: postgresql.cnpg.io + names: + kind: ImageCatalog + listKind: ImageCatalogList + plural: imagecatalogs + singular: imagecatalog + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ImageCatalog is the Schema for the imagecatalogs API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the desired behavior of the ImageCatalog. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + images: + description: List of CatalogImages available in the catalog + items: + description: CatalogImage defines the image and major version + properties: + image: + description: The image reference + type: string + major: + description: The PostgreSQL major version of the image. Must + be unique within the catalog. + minimum: 10 + type: integer + required: + - image + - major + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-validations: + - message: Images must have unique major versions + rule: self.all(e, self.filter(f, f.major==e.major).size() == 1) + required: + - images + type: object + required: + - metadata + - spec + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.14.0 @@ -6042,6 +6370,372 @@ spec: - transaction type: string type: object + serviceTemplate: + description: Template for the Service to be created + properties: + metadata: + description: |- + Standard object's metadata. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations + type: object + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: http://kubernetes.io/docs/user-guide/labels + type: object + type: object + spec: + description: |- + Specification of the desired behavior of the service. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + allocateLoadBalancerNodePorts: + description: |- + allocateLoadBalancerNodePorts defines if NodePorts will be automatically + allocated for services with type LoadBalancer. Default is "true". It + may be set to "false" if the cluster load-balancer does not rely on + NodePorts. If the caller requests specific NodePorts (by specifying a + value), those requests will be respected, regardless of this field. + This field may only be set for services with type LoadBalancer and will + be cleared if the type is changed to any other type. + type: boolean + clusterIP: + description: |- + clusterIP is the IP address of the service and is usually assigned + randomly. If an address is specified manually, is in-range (as per + system configuration), and is not in use, it will be allocated to the + service; otherwise creation of the service will fail. This field may not + be changed through updates unless the type field is also being changed + to ExternalName (which requires this field to be blank) or the type + field is being changed from ExternalName (in which case this field may + optionally be specified, as describe above). Valid values are "None", + empty string (""), or a valid IP address. Setting this to "None" makes a + "headless service" (no virtual IP), which is useful when direct endpoint + connections are preferred and proxying is not required. Only applies to + types ClusterIP, NodePort, and LoadBalancer. If this field is specified + when creating a Service of type ExternalName, creation will fail. This + field will be wiped when updating a Service to type ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + type: string + clusterIPs: + description: |- + ClusterIPs is a list of IP addresses assigned to this service, and are + usually assigned randomly. If an address is specified manually, is + in-range (as per system configuration), and is not in use, it will be + allocated to the service; otherwise creation of the service will fail. + This field may not be changed through updates unless the type field is + also being changed to ExternalName (which requires this field to be + empty) or the type field is being changed from ExternalName (in which + case this field may optionally be specified, as describe above). Valid + values are "None", empty string (""), or a valid IP address. Setting + this to "None" makes a "headless service" (no virtual IP), which is + useful when direct endpoint connections are preferred and proxying is + not required. Only applies to types ClusterIP, NodePort, and + LoadBalancer. If this field is specified when creating a Service of type + ExternalName, creation will fail. This field will be wiped when updating + a Service to type ExternalName. If this field is not specified, it will + be initialized from the clusterIP field. If this field is specified, + clients must ensure that clusterIPs[0] and clusterIP have the same + value. + + + This field may hold a maximum of two entries (dual-stack IPs, in either order). + These IPs must correspond to the values of the ipFamilies field. Both + clusterIPs and ipFamilies are governed by the ipFamilyPolicy field. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + description: |- + externalIPs is a list of IP addresses for which nodes in the cluster + will also accept traffic for this service. These IPs are not managed by + Kubernetes. The user is responsible for ensuring that traffic arrives + at a node with this IP. A common example is external load-balancers + that are not part of the Kubernetes system. + items: + type: string + type: array + externalName: + description: |- + externalName is the external reference that discovery mechanisms will + return as an alias for this service (e.g. a DNS CNAME record). No + proxying will be involved. Must be a lowercase RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires `type` to be "ExternalName". + type: string + externalTrafficPolicy: + description: |- + externalTrafficPolicy describes how nodes distribute service traffic they + receive on one of the Service's "externally-facing" addresses (NodePorts, + ExternalIPs, and LoadBalancer IPs). If set to "Local", the proxy will configure + the service in a way that assumes that external load balancers will take care + of balancing the service traffic between nodes, and so each node will deliver + traffic only to the node-local endpoints of the service, without masquerading + the client source IP. (Traffic mistakenly sent to a node with no endpoints will + be dropped.) The default value, "Cluster", uses the standard behavior of + routing to all endpoints evenly (possibly modified by topology and other + features). Note that traffic sent to an External IP or LoadBalancer IP from + within the cluster will always get "Cluster" semantics, but clients sending to + a NodePort from within the cluster may need to take traffic policy into account + when picking a node. + type: string + healthCheckNodePort: + description: |- + healthCheckNodePort specifies the healthcheck nodePort for the service. + This only applies when type is set to LoadBalancer and + externalTrafficPolicy is set to Local. If a value is specified, is + in-range, and is not in use, it will be used. If not specified, a value + will be automatically allocated. External systems (e.g. load-balancers) + can use this port to determine if a given node holds endpoints for this + service or not. If this field is specified when creating a Service + which does not need it, creation will fail. This field will be wiped + when updating a Service to no longer need it (e.g. changing type). + This field cannot be updated once set. + format: int32 + type: integer + internalTrafficPolicy: + description: |- + InternalTrafficPolicy describes how nodes distribute service traffic they + receive on the ClusterIP. If set to "Local", the proxy will assume that pods + only want to talk to endpoints of the service on the same node as the pod, + dropping the traffic if there are no local endpoints. The default value, + "Cluster", uses the standard behavior of routing to all endpoints evenly + (possibly modified by topology and other features). + type: string + ipFamilies: + description: |- + IPFamilies is a list of IP families (e.g. IPv4, IPv6) assigned to this + service. This field is usually assigned automatically based on cluster + configuration and the ipFamilyPolicy field. If this field is specified + manually, the requested family is available in the cluster, + and ipFamilyPolicy allows it, it will be used; otherwise creation of + the service will fail. This field is conditionally mutable: it allows + for adding or removing a secondary IP family, but it does not allow + changing the primary IP family of the Service. Valid values are "IPv4" + and "IPv6". This field only applies to Services of types ClusterIP, + NodePort, and LoadBalancer, and does apply to "headless" services. + This field will be wiped when updating a Service to type ExternalName. + + + This field may hold a maximum of two entries (dual-stack families, in + either order). These families must correspond to the values of the + clusterIPs field, if specified. Both clusterIPs and ipFamilies are + governed by the ipFamilyPolicy field. + items: + description: |- + IPFamily represents the IP Family (IPv4 or IPv6). This type is used + to express the family of an IP expressed by a type (e.g. service.spec.ipFamilies). + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + description: |- + IPFamilyPolicy represents the dual-stack-ness requested or required by + this Service. If there is no value provided, then this field will be set + to SingleStack. Services can be "SingleStack" (a single IP family), + "PreferDualStack" (two IP families on dual-stack configured clusters or + a single IP family on single-stack clusters), or "RequireDualStack" + (two IP families on dual-stack configured clusters, otherwise fail). The + ipFamilies and clusterIPs fields depend on the value of this field. This + field will be wiped when updating a service to type ExternalName. + type: string + loadBalancerClass: + description: |- + loadBalancerClass is the class of the load balancer implementation this Service belongs to. + If specified, the value of this field must be a label-style identifier, with an optional prefix, + e.g. "internal-vip" or "example.com/internal-vip". Unprefixed names are reserved for end-users. + This field can only be set when the Service type is 'LoadBalancer'. If not set, the default load + balancer implementation is used, today this is typically done through the cloud provider integration, + but should apply for any default implementation. If set, it is assumed that a load balancer + implementation is watching for Services with a matching class. Any default load balancer + implementation (e.g. cloud providers) should ignore Services that set this field. + This field can only be set when creating or updating a Service to type 'LoadBalancer'. + Once set, it can not be changed. This field will be wiped when a service is updated to a non 'LoadBalancer' type. + type: string + loadBalancerIP: + description: |- + Only applies to Service Type: LoadBalancer. + This feature depends on whether the underlying cloud-provider supports specifying + the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not support the feature. + Deprecated: This field was under-specified and its meaning varies across implementations. + Using it is non-portable and it may not support dual-stack. + Users are encouraged to use implementation-specific annotations when available. + type: string + loadBalancerSourceRanges: + description: |- + If specified and supported by the platform, this will restrict traffic through the cloud-provider + load-balancer will be restricted to the specified client IPs. This field will be ignored if the + cloud-provider does not support the feature." + More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/ + items: + type: string + type: array + ports: + description: |- + The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + items: + description: ServicePort contains information on service's + port. + properties: + appProtocol: + description: |- + The application protocol for this port. + This is used as a hint for implementations to offer richer behavior for protocols that they understand. + This field follows standard Kubernetes label syntax. + Valid values are either: + + + * Un-prefixed protocol names - reserved for IANA standard service names (as per + RFC-6335 and https://www.iana.org/assignments/service-names). + + + * Kubernetes-defined prefixed names: + * 'kubernetes.io/h2c' - HTTP/2 prior knowledge over cleartext as described in https://www.rfc-editor.org/rfc/rfc9113.html#name-starting-http-2-with-prior- + * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455 + * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455 + + + * Other protocols should use implementation-defined prefixed names such as + mycompany.com/my-custom-protocol. + type: string + name: + description: |- + The name of this port within the service. This must be a DNS_LABEL. + All ports within a ServiceSpec must have unique names. When considering + the endpoints for a Service, this must match the 'name' field in the + EndpointPort. + Optional if only one ServicePort is defined on this service. + type: string + nodePort: + description: |- + The port on each node on which this service is exposed when type is + NodePort or LoadBalancer. Usually assigned by the system. If a value is + specified, in-range, and not in use it will be used, otherwise the + operation will fail. If not specified, a port will be allocated if this + Service requires one. If this field is specified when creating a + Service which does not need it, creation will fail. This field will be + wiped when updating a Service to no longer need it (e.g. changing type + from NodePort to ClusterIP). + More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP + description: |- + The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". + Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the pods targeted by the service. + Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named port in the + target Pod's container ports. If this is not specified, the value + of the 'port' field is used (an identity map). + This field is ignored for services with clusterIP=None, and should be + omitted or set equal to the 'port' field. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + description: |- + publishNotReadyAddresses indicates that any agent which deals with endpoints for this + Service should disregard any indications of ready/not-ready. + The primary use case for setting this field is for a StatefulSet's Headless Service to + propagate SRV DNS records for its Pods for the purpose of peer discovery. + The Kubernetes controllers that generate Endpoints and EndpointSlice resources for + Services interpret this to mean that all endpoints are considered "ready" even if the + Pods themselves are not. Agents which consume only Kubernetes generated endpoints + through the Endpoints or EndpointSlice resources can safely assume this behavior. + type: boolean + selector: + additionalProperties: + type: string + description: |- + Route service traffic to pods with label keys and values matching this + selector. If empty or not present, the service is assumed to have an + external process managing its endpoints, which Kubernetes will not + modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. + Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/ + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + description: |- + Supports "ClientIP" and "None". Used to maintain session affinity. + Enable client IP based session affinity. + Must be ClientIP or None. + Defaults to None. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: |- + timeoutSeconds specifies the seconds of ClientIP type session sticky time. + The value must be >0 && <=86400(for 1 day) if ServiceAffinity == "ClientIP". + Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: |- + type determines how the Service is exposed. Defaults to ClusterIP. Valid + options are ExternalName, ClusterIP, NodePort, and LoadBalancer. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or if that is not + specified, by manual construction of an Endpoints object or + EndpointSlice objects. If clusterIP is "None", no virtual IP is + allocated and the endpoints are published as a set of endpoints rather + than a virtual IP. + "NodePort" builds on ClusterIP and allocates a port on every node which + routes to the same endpoints as the clusterIP. + "LoadBalancer" builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to the same endpoints + as the clusterIP. + "ExternalName" aliases this service to the specified externalName. + Several other fields do not apply to ExternalName services. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: string + type: object + type: object template: description: The template of the Pod to be created properties: @@ -13741,6 +14435,23 @@ spec: an immediate segment switch. type: boolean type: object + pluginConfiguration: + description: Configuration parameters passed to the plugin managing + this backup + properties: + name: + description: Name is the name of the plugin managing this backup + type: string + parameters: + additionalProperties: + type: string + description: |- + Parameters are the configuration parameters passed to the backup + plugin for this backup + type: object + required: + - name + type: object schedule: description: |- The schedule does not follow the same format used in Kubernetes CronJobs diff --git a/charts/cloudnative-pg/templates/rbac.yaml b/charts/cloudnative-pg/templates/rbac.yaml index cf7982939..1865bccab 100644 --- a/charts/cloudnative-pg/templates/rbac.yaml +++ b/charts/cloudnative-pg/templates/rbac.yaml @@ -265,6 +265,14 @@ rules: - get - patch - update +- apiGroups: + - postgresql.cnpg.io + resources: + - clusterimagecatalogs + verbs: + - get + - list + - watch - apiGroups: - postgresql.cnpg.io resources: @@ -292,6 +300,14 @@ rules: - patch - update - watch +- apiGroups: + - postgresql.cnpg.io + resources: + - imagecatalogs + verbs: + - get + - list + - watch - apiGroups: - postgresql.cnpg.io resources: diff --git a/charts/cloudnative-pg/values.yaml b/charts/cloudnative-pg/values.yaml index 17314ed7a..2715399ab 100644 --- a/charts/cloudnative-pg/values.yaml +++ b/charts/cloudnative-pg/values.yaml @@ -359,6 +359,7 @@ monitoringQueriesConfigMap: description: "Time at which these statistics were last reset" pg_stat_bgwriter: + runonserver: "<17.0.0" query: | SELECT checkpoints_timed , checkpoints_req From ee3a3f2d5c5c2e0367b49964bb2e647ab8625c46 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:42:55 +0300 Subject: [PATCH 66/87] chore(deps): update actions/checkout action to v4.1.4 (#269) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- .github/workflows/release-pr.yml | 2 +- .github/workflows/release-publish.yml | 2 +- .github/workflows/tests-cluster-standalone.yml | 4 ++-- .github/workflows/tests-operator.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 50489d5ad..121b0dfcc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 0 diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 9baae79f3..47f34194c 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Create Pull Request id: create-pr env: diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 1b9a015a9..016450973 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 0 # important for fetching all history to run comparison against diff --git a/.github/workflows/tests-cluster-standalone.yml b/.github/workflows/tests-cluster-standalone.yml index 5c17704d0..160ef5ac4 100644 --- a/.github/workflows/tests-cluster-standalone.yml +++ b/.github/workflows/tests-cluster-standalone.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 0 @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 0 diff --git a/.github/workflows/tests-operator.yml b/.github/workflows/tests-operator.yml index 064306806..36aa66a8e 100644 --- a/.github/workflows/tests-operator.yml +++ b/.github/workflows/tests-operator.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 0 From ec214c41a99d5f8a529fccc3b769963234c56462 Mon Sep 17 00:00:00 2001 From: gpothier Date: Fri, 26 Apr 2024 03:18:18 -0400 Subject: [PATCH 67/87] Add support for the `method` parameter in `scheduledBackups` (#265) * Add support for the `method` parameter in `scheduledBackups` Fixes #264 Signed-off-by: Guillaume Pothier --- charts/cluster/README.md | 1 + charts/cluster/templates/scheduled-backups.yaml | 1 + charts/cluster/values.schema.json | 3 +++ charts/cluster/values.yaml | 2 ++ 4 files changed, 7 insertions(+) diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 747f9de61..ff59e34ef 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -140,6 +140,7 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | backups.s3.region | string | `""` | | | backups.s3.secretKey | string | `""` | | | backups.scheduledBackups[0].backupOwnerReference | string | `"self"` | Backup owner reference | +| backups.scheduledBackups[0].method | string | `"barmanObjectStore"` | Backup method, can be `barmanObjectStore` (default) or `volumeSnapshot` | | backups.scheduledBackups[0].name | string | `"daily-backup"` | Scheduled backup name | | backups.scheduledBackups[0].schedule | string | `"0 0 0 * * *"` | Schedule in cron format | | backups.wal.compression | string | `"gzip"` | WAL compression method. One of `` (for no compression), `gzip`, `bzip2` or `snappy`. | diff --git a/charts/cluster/templates/scheduled-backups.yaml b/charts/cluster/templates/scheduled-backups.yaml index 36fbc4471..23f8c4ca4 100644 --- a/charts/cluster/templates/scheduled-backups.yaml +++ b/charts/cluster/templates/scheduled-backups.yaml @@ -10,6 +10,7 @@ metadata: spec: immediate: true schedule: {{ .schedule }} + method: {{ .method }} backupOwnerReference: {{ .backupOwnerReference }} cluster: name: {{ include "cluster.fullname" $context }} diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 7bab0a443..46c874b1d 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -125,6 +125,9 @@ "backupOwnerReference": { "type": "string" }, + "method": { + "type": "string" + }, "name": { "type": "string" }, diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index f2651018b..7dad93eea 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -266,6 +266,8 @@ backups: schedule: "0 0 0 * * *" # -- Backup owner reference backupOwnerReference: self + # -- Backup method, can be `barmanObjectStore` (default) or `volumeSnapshot` + method: barmanObjectStore # -- Retention policy for backups retentionPolicy: "30d" From f285416410361254d49bab083626f24666669992 Mon Sep 17 00:00:00 2001 From: Aisling McGinn Date: Fri, 26 Apr 2024 08:57:43 -0400 Subject: [PATCH 68/87] fix: Bump chart dependency grafana-dashboards to version 0.0.2 (#270) * update chart dependency grafana-dashboard to 0.2 Signed-off-by: Aisling McGinn Signed-off-by: Aisling McGinn * Update Chart.lock Signed-off-by: Aisling McGinn * Don't specify patch version Signed-off-by: Aisling McGinn --------- Signed-off-by: Aisling McGinn Signed-off-by: Aisling McGinn Co-authored-by: Itay Grudev --- charts/cloudnative-pg/Chart.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/cloudnative-pg/Chart.lock b/charts/cloudnative-pg/Chart.lock index 16719e97b..610070fb7 100644 --- a/charts/cloudnative-pg/Chart.lock +++ b/charts/cloudnative-pg/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: cluster repository: https://cloudnative-pg.github.io/grafana-dashboards - version: 0.0.1 -digest: sha256:f2055d3b9f52be6989a8285cb736ec555f91f133791382787f4beb4387175ca0 -generated: "2024-03-23T16:27:46.965208992+02:00" + version: 0.0.2 +digest: sha256:fcf16ad357c17be3dd79c138723e78e9e101fecc5d07d9371299c32b9f85dbd9 +generated: "2024-04-25T12:32:36.61779032-04:00" From ceb84ca2b607193a8fc19ad24130bc44e75f69dc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 27 Apr 2024 15:36:46 +0100 Subject: [PATCH 69/87] Release cloudnative-pg-v0.21.1 (#273) Signed-off-by: Philippe Scorsolini Co-authored-by: Philippe Scorsolini --- charts/cloudnative-pg/Chart.yaml | 2 +- charts/cloudnative-pg/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index fac787158..dfdb33bc0 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,7 +18,7 @@ name: cloudnative-pg description: CloudNativePG Operator Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.21.0" +version: "0.21.1" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 4b31d7f17..9beea2676 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.21.0](https://img.shields.io/badge/Version-0.21.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.0](https://img.shields.io/badge/AppVersion-1.23.0-informational?style=flat-square) +![Version: 0.21.1](https://img.shields.io/badge/Version-0.21.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.0](https://img.shields.io/badge/AppVersion-1.23.0-informational?style=flat-square) CloudNativePG Operator Helm Chart From 9c668bcd2b00f1f820cfbe7bb76f4b8865a7dcfd Mon Sep 17 00:00:00 2001 From: Leonardo Cecchi Date: Tue, 30 Apr 2024 15:27:18 +0200 Subject: [PATCH 70/87] Release cloudnative-pg-v0.21.2 (#274) Signed-off-by: Leonardo Cecchi --- RELEASE.md | 2 +- charts/cloudnative-pg/Chart.yaml | 4 ++-- charts/cloudnative-pg/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index d328344e0..118f2ac8c 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -36,7 +36,7 @@ In order to create a new release of the `cloudnative-pg` chart, follow these ste ``` 4. Update the `.version` in the [Chart.yaml](./charts/cloudnative-pg/Chart.yaml) file to `"X.Y.Z"` ```bash - sed -i -E "s/^version: ([0-9]+.?)+/version: $NEW_VERSION/" charts/cloudnative-pg/Chart.yaml + sed -i -E "s/^version: \"([0-9]+.?)+\"/version: \"$NEW_VERSION\"/" charts/cloudnative-pg/Chart.yaml ``` 5. Update everything else as required, e.g. if releasing due to a new `cloudnative-pg` version being released, you might want to update the following: diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index dfdb33bc0..0a8c12a07 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,12 +18,12 @@ name: cloudnative-pg description: CloudNativePG Operator Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.21.1" +version: "0.21.2" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.23.0" +appVersion: "1.23.1" sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 9beea2676..7c3470cd3 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.21.1](https://img.shields.io/badge/Version-0.21.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.0](https://img.shields.io/badge/AppVersion-1.23.0-informational?style=flat-square) +![Version: 0.21.2](https://img.shields.io/badge/Version-0.21.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.1](https://img.shields.io/badge/AppVersion-1.23.1-informational?style=flat-square) CloudNativePG Operator Helm Chart From 7c1557ecba5c7f9930923915077089c3ab7e0711 Mon Sep 17 00:00:00 2001 From: walterbaidal Date: Fri, 24 May 2024 17:10:36 +0200 Subject: [PATCH 71/87] fix(cluster): Add quote to schedule parameter (#289) Fixes Issue #288 - schedule parameter not quoted Signed-off-by: walterbaidal --- charts/cluster/templates/scheduled-backups.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/cluster/templates/scheduled-backups.yaml b/charts/cluster/templates/scheduled-backups.yaml index 23f8c4ca4..850c27940 100644 --- a/charts/cluster/templates/scheduled-backups.yaml +++ b/charts/cluster/templates/scheduled-backups.yaml @@ -9,7 +9,7 @@ metadata: labels: {{ include "cluster.labels" $context | nindent 4 }} spec: immediate: true - schedule: {{ .schedule }} + schedule: {{ .schedule | quote }} method: {{ .method }} backupOwnerReference: {{ .backupOwnerReference }} cluster: From c664085cee5355596b4b18ebfceb71ec1a48fba1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 18:15:52 +0300 Subject: [PATCH 72/87] chore(deps): update actions/checkout action to v4.1.6 (#281) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- .github/workflows/release-pr.yml | 2 +- .github/workflows/release-publish.yml | 2 +- .github/workflows/tests-cluster-standalone.yml | 4 ++-- .github/workflows/tests-operator.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 121b0dfcc..9cf1e3624 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 47f34194c..448808a59 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Create Pull Request id: create-pr env: diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 016450973..e15206600 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 # important for fetching all history to run comparison against diff --git a/.github/workflows/tests-cluster-standalone.yml b/.github/workflows/tests-cluster-standalone.yml index 160ef5ac4..70b4c6f83 100644 --- a/.github/workflows/tests-cluster-standalone.yml +++ b/.github/workflows/tests-cluster-standalone.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 diff --git a/.github/workflows/tests-operator.yml b/.github/workflows/tests-operator.yml index 36aa66a8e..87474c18d 100644 --- a/.github/workflows/tests-operator.yml +++ b/.github/workflows/tests-operator.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 From 917b767e4f792b8ebcef6205a9453374354b4253 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sat, 25 May 2024 19:40:25 +0300 Subject: [PATCH 73/87] Forcing serverName to default to cluster.fullname to ensure consistency (#292) Closes: #243 Signed-off-by: Itay Grudev --- charts/cluster/templates/_bootstrap.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/cluster/templates/_bootstrap.tpl b/charts/cluster/templates/_bootstrap.tpl index 6147f3a77..35797ec1b 100644 --- a/charts/cluster/templates/_bootstrap.tpl +++ b/charts/cluster/templates/_bootstrap.tpl @@ -37,7 +37,7 @@ bootstrap: externalClusters: - name: objectStoreRecoveryCluster barmanObjectStore: - serverName: {{ .Values.recovery.clusterName }} + serverName: {{ default (include "cluster.fullname" .) .Values.recovery.clusterName }} {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.recovery "secretSuffix" "-recovery" -}} {{- include "cluster.barmanObjectStoreConfig" $d | nindent 4 }} {{- else }} From 7da043f55c6c9e56684ad589ea69ff63c45467c9 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sat, 25 May 2024 19:41:12 +0300 Subject: [PATCH 74/87] fix(cluster): Prometheus Rule CNPGClusterOffline false positives (#291) * Bug Fix: Direct matching instead of pattern matching for namespace in PrometheusRules * Bug Fix: CNPGClusterOffline false positives Signed-off-by: Itay Grudev --- .../prometheus_rules/cluster-high_connection-critical.yaml | 2 +- .../prometheus_rules/cluster-high_connection-warning.yaml | 2 +- .../cluster/prometheus_rules/cluster-high_replication_lag.yaml | 2 +- .../prometheus_rules/cluster-instances_on_same_node.yaml | 2 +- charts/cluster/prometheus_rules/cluster-offline.yaml | 2 +- .../cluster/prometheus_rules/cluster-zone_spread-warning.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml b/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml index e5de95225..df13ce3b3 100644 --- a/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml +++ b/charts/cluster/prometheus_rules/cluster-high_connection-critical.yaml @@ -8,7 +8,7 @@ annotations: the maximum number of connections. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsCritical.md expr: | - sum by (pod) (cnpg_backends_total{namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) * 100 > 95 + sum by (pod) (cnpg_backends_total{namespace="{{ .namespace }}", pod=~"{{ .podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace="{{ .namespace }}", pod=~"{{ .podSelector }}"}) * 100 > 95 for: 5m labels: severity: critical diff --git a/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml b/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml index ae706ee0b..73cc78392 100644 --- a/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml +++ b/charts/cluster/prometheus_rules/cluster-high_connection-warning.yaml @@ -8,7 +8,7 @@ annotations: the maximum number of connections. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighConnectionsWarning.md expr: | - sum by (pod) (cnpg_backends_total{namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) * 100 > 80 + sum by (pod) (cnpg_backends_total{namespace="{{ .namespace }}", pod=~"{{ .podSelector }}"}) / max by (pod) (cnpg_pg_settings_setting{name="max_connections", namespace="{{ .namespace }}", pod=~"{{ .podSelector }}"}) * 100 > 80 for: 5m labels: severity: warning diff --git a/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml b/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml index ab1c175a1..660db254f 100644 --- a/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml +++ b/charts/cluster/prometheus_rules/cluster-high_replication_lag.yaml @@ -10,7 +10,7 @@ annotations: High replication lag indicates network issues, busy instances, slow queries or suboptimal configuration. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterHighReplicationLag.md expr: | - max(cnpg_pg_replication_lag{namespace=~"{{ .namespace }}",pod=~"{{ .podSelector }}"}) * 1000 > 1000 + max(cnpg_pg_replication_lag{namespace="{{ .namespace }}",pod=~"{{ .podSelector }}"}) * 1000 > 1000 for: 5m labels: severity: warning diff --git a/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml b/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml index b5a90742e..aafcfab1e 100644 --- a/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml +++ b/charts/cluster/prometheus_rules/cluster-instances_on_same_node.yaml @@ -10,7 +10,7 @@ annotations: A failure or scheduled downtime of a single node will lead to a potential service disruption and/or data loss. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterInstancesOnSameNode.md expr: | - count by (node) (kube_pod_info{namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"}) > 1 + count by (node) (kube_pod_info{namespace="{{ .namespace }}", pod=~"{{ .podSelector }}"}) > 1 for: 5m labels: severity: warning diff --git a/charts/cluster/prometheus_rules/cluster-offline.yaml b/charts/cluster/prometheus_rules/cluster-offline.yaml index 4ac68ce35..aa4dc12ee 100644 --- a/charts/cluster/prometheus_rules/cluster-offline.yaml +++ b/charts/cluster/prometheus_rules/cluster-offline.yaml @@ -10,7 +10,7 @@ annotations: potential service disruption and/or data loss. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterOffline.md expr: | - ({{ .Values.cluster.instances }} - count(cnpg_collector_up{namespace=~"{{ .namespace }}",pod=~"{{ .podSelector }}"}) OR vector(0)) > 0 + (count(cnpg_collector_up{namespace="{{ .namespace }}",pod=~"{{ .podSelector }}"}) OR on() vector(0)) == 0 for: 5m labels: severity: critical diff --git a/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml b/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml index 0959ae87b..41fa4002a 100644 --- a/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml +++ b/charts/cluster/prometheus_rules/cluster-zone_spread-warning.yaml @@ -9,7 +9,7 @@ annotations: A disaster in one availability zone will lead to a potential service disruption and/or data loss. runbook_url: https://github.com/cloudnative-pg/charts/blob/main/charts/cluster/docs/runbooks/CNPGClusterZoneSpreadWarning.md expr: | - {{ .Values.cluster.instances }} > count(count by (label_topology_kubernetes_io_zone) (kube_pod_info{namespace=~"{{ .namespace }}", pod=~"{{ .podSelector }}"} * on(node,instance) group_left(label_topology_kubernetes_io_zone) kube_node_labels)) < 3 + {{ .Values.cluster.instances }} > count(count by (label_topology_kubernetes_io_zone) (kube_pod_info{namespace="{{ .namespace }}", pod=~"{{ .podSelector }}"} * on(node,instance) group_left(label_topology_kubernetes_io_zone) kube_node_labels)) < 3 for: 5m labels: severity: warning From a123fb45f19cec9695eacdb20a6adaa787992c30 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Sat, 25 May 2024 19:45:40 +0300 Subject: [PATCH 75/87] feat(cluster): allow using existing secret for backup and restore (#239) * Renamed barmanObjectStore secrets for consistency * feat(cluster): allow using existing secret for backup and restore Signed-off-by: Itay Grudev Signed-off-by: Itay Grudev Co-authored-by: Ben Scholzen (DASPRiD) Co-authored-by: Cr4mble --- charts/cluster/templates/_backup.tpl | 4 ++-- .../templates/_barman_object_store.tpl | 19 +++++++++++-------- charts/cluster/templates/_bootstrap.tpl | 7 ++++--- .../cluster/templates/backup-azure-creds.yaml | 6 +++--- .../templates/backup-google-creds.yaml | 6 +++--- .../backup-google-recovery-creds.yaml | 8 -------- charts/cluster/templates/backup-s3-creds.yaml | 4 ++-- ...y-creds.yaml => recovery-azure-creds.yaml} | 6 +++--- .../templates/recovery-google-creds.yaml | 8 ++++++++ ...very-creds.yaml => recovery-s3-creds.yaml} | 6 +++--- charts/cluster/values.yaml | 10 ++++++++++ 11 files changed, 49 insertions(+), 35 deletions(-) delete mode 100644 charts/cluster/templates/backup-google-recovery-creds.yaml rename charts/cluster/templates/{backup-azure-recovery-creds.yaml => recovery-azure-creds.yaml} (67%) create mode 100644 charts/cluster/templates/recovery-google-creds.yaml rename charts/cluster/templates/{backup-s3-recovery-creds.yaml => recovery-s3-creds.yaml} (64%) diff --git a/charts/cluster/templates/_backup.tpl b/charts/cluster/templates/_backup.tpl index 8b9a094d3..8059618c4 100644 --- a/charts/cluster/templates/_backup.tpl +++ b/charts/cluster/templates/_backup.tpl @@ -1,6 +1,6 @@ {{- define "cluster.backup" -}} -backup: {{- if .Values.backups.enabled }} +backup: target: "prefer-standby" retentionPolicy: {{ .Values.backups.retentionPolicy }} barmanObjectStore: @@ -13,7 +13,7 @@ backup: encryption: {{ .Values.backups.data.encryption }} jobs: {{ .Values.backups.data.jobs }} - {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.backups }} + {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.backups "secretPrefix" "backup" }} {{- include "cluster.barmanObjectStoreConfig" $d | nindent 2 }} {{- end }} {{- end }} diff --git a/charts/cluster/templates/_barman_object_store.tpl b/charts/cluster/templates/_barman_object_store.tpl index 4269ad951..c685bbec1 100644 --- a/charts/cluster/templates/_barman_object_store.tpl +++ b/charts/cluster/templates/_barman_object_store.tpl @@ -1,7 +1,7 @@ {{- define "cluster.barmanObjectStoreConfig" -}} {{- if .scope.endpointURL }} - endpointURL: {{ .scope.endpointURL }} + endpointURL: {{ .scope.endpointURL | quote }} {{- end }} {{- if or (.scope.endpointCA.create) (.scope.endpointCA.name) }} @@ -21,35 +21,37 @@ {{- if empty .scope.destinationPath }} destinationPath: "s3://{{ required "You need to specify S3 bucket if destinationPath is not specified." .scope.s3.bucket }}{{ .scope.s3.path }}" {{- end }} + {{- $secretName := coalesce .scope.secret.name (printf "%s-%s-s3-creds" .chartFullname .secretPrefix) }} s3Credentials: accessKeyId: - name: {{ .chartFullname }}-backup-s3{{ .secretSuffix }}-creds + name: {{ $secretName }} key: ACCESS_KEY_ID secretAccessKey: - name: {{ .chartFullname }}-backup-s3{{ .secretSuffix }}-creds + name: {{ $secretName }} key: ACCESS_SECRET_KEY {{- else if eq .scope.provider "azure" }} {{- if empty .scope.destinationPath }} destinationPath: "https://{{ required "You need to specify Azure storageAccount if destinationPath is not specified." .scope.azure.storageAccount }}.{{ .scope.azure.serviceName }}.core.windows.net/{{ .scope.azure.containerName }}{{ .scope.azure.path }}" {{- end }} + {{- $secretName := coalesce .scope.secret.name (printf "%s-%s-azure-creds" .chartFullname .secretPrefix) }} azureCredentials: {{- if .scope.azure.inheritFromAzureAD }} inheritFromAzureAD: true {{- else if .scope.azure.connectionString }} connectionString: - name: {{ .chartFullname }}-backup-azure{{ .secretSuffix }}-creds + name: {{ $secretName }} key: AZURE_CONNECTION_STRING {{- else }} storageAccount: - name: {{ .chartFullname }}-backup-azure{{ .secretSuffix }}-creds + name: {{ $secretName }} key: AZURE_STORAGE_ACCOUNT {{- if .scope.azure.storageKey }} storageKey: - name: {{ .chartFullname }}-backup-azure{{ .secretSuffix }}-creds + name: {{ $secretName }} key: AZURE_STORAGE_KEY {{- else }} storageSasToken: - name: {{ .chartFullname }}-backup-azure{{ .secretSuffix }}-creds + name: {{ $secretName }} key: AZURE_STORAGE_SAS_TOKEN {{- end }} {{- end }} @@ -57,10 +59,11 @@ {{- if empty .scope.destinationPath }} destinationPath: "gs://{{ required "You need to specify Google storage bucket if destinationPath is not specified." .scope.google.bucket }}{{ .scope.google.path }}" {{- end }} + {{- $secretName := coalesce .scope.secret.name (printf "%s-%s-google-creds" .chartFullname .secretPrefix) }} googleCredentials: gkeEnvironment: {{ .scope.google.gkeEnvironment }} applicationCredentials: - name: {{ .chartFullname }}-backup-google{{ .secretSuffix }}-creds + name: {{ $secretName }} key: APPLICATION_CREDENTIALS {{- end -}} {{- end -}} diff --git a/charts/cluster/templates/_bootstrap.tpl b/charts/cluster/templates/_bootstrap.tpl index 35797ec1b..cd800bd3b 100644 --- a/charts/cluster/templates/_bootstrap.tpl +++ b/charts/cluster/templates/_bootstrap.tpl @@ -1,6 +1,6 @@ {{- define "cluster.bootstrap" -}} -bootstrap: {{- if eq .Values.mode "standalone" }} +bootstrap: initdb: {{- with .Values.cluster.initdb }} {{- with (omit . "postInitApplicationSQL") }} @@ -21,7 +21,8 @@ bootstrap: {{- printf "- %s" . | nindent 6 }} {{- end -}} {{- end -}} -{{- else if eq .Values.mode "recovery" }} +{{- else if eq .Values.mode "recovery" -}} +bootstrap: recovery: {{- with .Values.recovery.pitrTarget.time }} recoveryTarget: @@ -38,7 +39,7 @@ externalClusters: - name: objectStoreRecoveryCluster barmanObjectStore: serverName: {{ default (include "cluster.fullname" .) .Values.recovery.clusterName }} - {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.recovery "secretSuffix" "-recovery" -}} + {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.recovery "secretPrefix" "recovery" -}} {{- include "cluster.barmanObjectStoreConfig" $d | nindent 4 }} {{- else }} {{ fail "Invalid cluster mode!" }} diff --git a/charts/cluster/templates/backup-azure-creds.yaml b/charts/cluster/templates/backup-azure-creds.yaml index 19a651eb3..6c84308dd 100644 --- a/charts/cluster/templates/backup-azure-creds.yaml +++ b/charts/cluster/templates/backup-azure-creds.yaml @@ -1,11 +1,11 @@ -{{- if and .Values.backups.enabled (eq .Values.backups.provider "azure") }} +{{- if and .Values.backups.enabled (eq .Values.backups.provider "azure") .Values.backups.secret.create }} apiVersion: v1 kind: Secret metadata: - name: {{ include "cluster.fullname" . }}-backup-azure-creds + name: {{ default (printf "%s-backup-azure-creds" (include "cluster.fullname" .)) .Values.backups.secret.name }} data: AZURE_CONNECTION_STRING: {{ .Values.backups.azure.connectionString | b64enc | quote }} AZURE_STORAGE_ACCOUNT: {{ .Values.backups.azure.storageAccount | b64enc | quote }} AZURE_STORAGE_KEY: {{ .Values.backups.azure.storageKey | b64enc | quote }} AZURE_STORAGE_SAS_TOKEN: {{ .Values.backups.azure.storageSasToken | b64enc | quote }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/charts/cluster/templates/backup-google-creds.yaml b/charts/cluster/templates/backup-google-creds.yaml index 252a27064..cc05c4c59 100644 --- a/charts/cluster/templates/backup-google-creds.yaml +++ b/charts/cluster/templates/backup-google-creds.yaml @@ -1,8 +1,8 @@ -{{- if and .Values.backups.enabled (eq .Values.backups.provider "google") }} +{{- if and .Values.backups.enabled (eq .Values.backups.provider "google") .Values.backups.secret.create }} apiVersion: v1 kind: Secret metadata: - name: {{ include "cluster.fullname" . }}-backup-google-creds + name: {{ default (printf "%s-backup-google-creds" (include "cluster.fullname" .)) .Values.backups.secret.name }} data: APPLICATION_CREDENTIALS: {{ .Values.backups.google.applicationCredentials | b64enc | quote }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/charts/cluster/templates/backup-google-recovery-creds.yaml b/charts/cluster/templates/backup-google-recovery-creds.yaml deleted file mode 100644 index 942bb897b..000000000 --- a/charts/cluster/templates/backup-google-recovery-creds.yaml +++ /dev/null @@ -1,8 +0,0 @@ -{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "google") }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "cluster.fullname" . }}-backup-google-recovery-creds -data: - APPLICATION_CREDENTIALS: {{ .Values.recovery.google.applicationCredentials | b64enc | quote }} -{{- end }} \ No newline at end of file diff --git a/charts/cluster/templates/backup-s3-creds.yaml b/charts/cluster/templates/backup-s3-creds.yaml index b906d2453..ddd8e2717 100644 --- a/charts/cluster/templates/backup-s3-creds.yaml +++ b/charts/cluster/templates/backup-s3-creds.yaml @@ -1,8 +1,8 @@ -{{- if and .Values.backups.enabled (eq .Values.backups.provider "s3") }} +{{- if and .Values.backups.enabled (eq .Values.backups.provider "s3") .Values.backups.secret.create }} apiVersion: v1 kind: Secret metadata: - name: {{ include "cluster.fullname" . }}-backup-s3-creds + name: {{ default (printf "%s-backup-s3-creds" (include "cluster.fullname" .)) .Values.backups.secret.name }} data: ACCESS_KEY_ID: {{ required ".Values.backups.s3.accessKey is required, but not specified." .Values.backups.s3.accessKey | b64enc | quote }} ACCESS_SECRET_KEY: {{ required ".Values.backups.s3.secretKey is required, but not specified." .Values.backups.s3.secretKey | b64enc | quote }} diff --git a/charts/cluster/templates/backup-azure-recovery-creds.yaml b/charts/cluster/templates/recovery-azure-creds.yaml similarity index 67% rename from charts/cluster/templates/backup-azure-recovery-creds.yaml rename to charts/cluster/templates/recovery-azure-creds.yaml index b4aecb558..9fb707651 100644 --- a/charts/cluster/templates/backup-azure-recovery-creds.yaml +++ b/charts/cluster/templates/recovery-azure-creds.yaml @@ -1,11 +1,11 @@ -{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "azure") }} +{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "azure") .Values.recovery.secret.create }} apiVersion: v1 kind: Secret metadata: - name: {{ include "cluster.fullname" . }}-backup-azure-recovery-creds + name: {{ default (printf "%s-recovery-azure-creds" (include "cluster.fullname" .)) .Values.recovery.secret.name }} data: AZURE_CONNECTION_STRING: {{ .Values.recovery.azure.connectionString | b64enc | quote }} AZURE_STORAGE_ACCOUNT: {{ .Values.recovery.azure.storageAccount | b64enc | quote }} AZURE_STORAGE_KEY: {{ .Values.recovery.azure.storageKey | b64enc | quote }} AZURE_STORAGE_SAS_TOKEN: {{ .Values.recovery.azure.storageSasToken | b64enc | quote }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/charts/cluster/templates/recovery-google-creds.yaml b/charts/cluster/templates/recovery-google-creds.yaml new file mode 100644 index 000000000..e7366ec4c --- /dev/null +++ b/charts/cluster/templates/recovery-google-creds.yaml @@ -0,0 +1,8 @@ +{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "google") .Values.recovery.secret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ default (printf "%s-recovery-google-creds" (include "cluster.fullname" .)) .Values.recovery.secret.name }} +data: + APPLICATION_CREDENTIALS: {{ .Values.recovery.google.applicationCredentials | b64enc | quote }} +{{- end }} diff --git a/charts/cluster/templates/backup-s3-recovery-creds.yaml b/charts/cluster/templates/recovery-s3-creds.yaml similarity index 64% rename from charts/cluster/templates/backup-s3-recovery-creds.yaml rename to charts/cluster/templates/recovery-s3-creds.yaml index 9cc615fcd..950c74c4b 100644 --- a/charts/cluster/templates/backup-s3-recovery-creds.yaml +++ b/charts/cluster/templates/recovery-s3-creds.yaml @@ -1,9 +1,9 @@ -{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "s3") }} +{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "s3") .Values.recovery.secret.create }} apiVersion: v1 kind: Secret metadata: - name: {{ include "cluster.fullname" . }}-backup-s3-recovery-creds + name: {{ default (printf "%s-recovery-s3-creds" (include "cluster.fullname" .)) .Values.recovery.secret.name }} data: ACCESS_KEY_ID: {{ required ".Values.recovery.s3.accessKey is required, but not specified." .Values.recovery.s3.accessKey | b64enc | quote }} ACCESS_SECRET_KEY: {{ required ".Values.recovery.s3.secretKey is required, but not specified." .Values.recovery.s3.secretKey | b64enc | quote }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index 7dad93eea..c0c10c512 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -75,6 +75,11 @@ recovery: bucket: "" gkeEnvironment: false applicationCredentials: "" + secret: + # -- Whether to create a secret for the backup credentials + create: true + # -- Name of the backup credentials secret + name: "" cluster: @@ -242,6 +247,11 @@ backups: bucket: "" gkeEnvironment: false applicationCredentials: "" + secret: + # -- Whether to create a secret for the backup credentials + create: true + # -- Name of the backup credentials secret + name: "" wal: # -- WAL compression method. One of `` (for no compression), `gzip`, `bzip2` or `snappy`. From 6f07f62a0360c00ce88ee3921b248d5705ee5e1b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 May 2024 20:08:28 +0300 Subject: [PATCH 76/87] Release cluster-v0.0.9 (#293) Signed-off-by: Itay Grudev Co-authored-by: Itay Grudev --- charts/cluster/Chart.yaml | 2 +- charts/cluster/README.md | 6 +++++- charts/cluster/values.schema.json | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/charts/cluster/Chart.yaml b/charts/cluster/Chart.yaml index b41a8aa5b..d69a5f0b7 100644 --- a/charts/cluster/Chart.yaml +++ b/charts/cluster/Chart.yaml @@ -18,7 +18,7 @@ name: cluster description: Deploys and manages a CloudNativePG cluster and its associated resources. icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: 0.0.8 +version: 0.0.9 sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cluster/README.md b/charts/cluster/README.md index ff59e34ef..621a8442c 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -1,6 +1,6 @@ # cluster -![Version: 0.0.8](https://img.shields.io/badge/Version-0.0.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![Version: 0.0.9](https://img.shields.io/badge/Version-0.0.9-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) > **Warning** > ### This chart is under active development. @@ -143,6 +143,8 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | backups.scheduledBackups[0].method | string | `"barmanObjectStore"` | Backup method, can be `barmanObjectStore` (default) or `volumeSnapshot` | | backups.scheduledBackups[0].name | string | `"daily-backup"` | Scheduled backup name | | backups.scheduledBackups[0].schedule | string | `"0 0 0 * * *"` | Schedule in cron format | +| backups.secret.create | bool | `true` | Whether to create a secret for the backup credentials | +| backups.secret.name | string | `""` | Name of the backup credentials secret | | backups.wal.compression | string | `"gzip"` | WAL compression method. One of `` (for no compression), `gzip`, `bzip2` or `snappy`. | | backups.wal.encryption | string | `"AES256"` | Whether to instruct the storage provider to encrypt WAL files. One of `` (use the storage container default), `AES256` or `aws:kms`. | | backups.wal.maxParallel | int | `1` | Number of WAL files to be archived or restored in parallel. | @@ -210,6 +212,8 @@ refer to the [CloudNativePG Documentation](https://cloudnative-pg.io/documentat | recovery.s3.path | string | `"/"` | | | recovery.s3.region | string | `""` | | | recovery.s3.secretKey | string | `""` | | +| recovery.secret.create | bool | `true` | Whether to create a secret for the backup credentials | +| recovery.secret.name | string | `""` | Name of the backup credentials secret | | type | string | `"postgresql"` | Type of the CNPG database. Available types: * `postgresql` * `postgis` | ## Maintainers diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 46c874b1d..1ceac4e19 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -137,6 +137,17 @@ } } }, + "secret": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, "wal": { "type": "object", "properties": { @@ -431,6 +442,17 @@ "type": "string" } } + }, + "secret": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } } } }, From f7e6ddc2b3a1211588d07d723e9e3dfcb4f66485 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 17:13:18 +0300 Subject: [PATCH 77/87] chore(deps): update docker/login-action action to v3.2.0 (#298) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index e15206600..f7aa0ee9e 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -58,7 +58,7 @@ jobs: run: shred --remove=wipesync /tmp/keyring.gpg /tmp/passphrase-file.txt - name: Login to GitHub Container Registry - uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 with: registry: ghcr.io username: ${{ github.actor }} From e364ff6cf6f05e74f511ce593471b73ed9538693 Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 30 May 2024 05:40:33 -0400 Subject: [PATCH 78/87] Add 'additionalEnvVars' to the chart (#303) * Add 'additionalEnv' to the chart Signed-off-by: Steven --- charts/cloudnative-pg/README.md | 1 + charts/cloudnative-pg/templates/deployment.yaml | 3 +++ charts/cloudnative-pg/values.yaml | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 7c3470cd3..40cbd6e1a 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -27,6 +27,7 @@ CloudNativePG Operator Helm Chart | Key | Type | Default | Description | |-----|------|---------|-------------| | additionalArgs | list | `[]` | Additinal arguments to be added to the operator's args list. | +| additionalEnv | list | `[]` | Array containing extra environment variables which can be templated. See [values.yaml](./values.yaml) for examples. | affinity | object | `{}` | Affinity for the operator to be installed. | | commonAnnotations | object | `{}` | Annotations to be added to all other resources. | | config | object | `{"create":true,"data":{},"name":"cnpg-controller-manager-config","secret":false}` | Operator configuration. | diff --git a/charts/cloudnative-pg/templates/deployment.yaml b/charts/cloudnative-pg/templates/deployment.yaml index 858248a66..80a656d5c 100644 --- a/charts/cloudnative-pg/templates/deployment.yaml +++ b/charts/cloudnative-pg/templates/deployment.yaml @@ -72,6 +72,9 @@ spec: fieldPath: metadata.namespace - name: MONITORING_QUERIES_CONFIGMAP value: "{{ .Values.monitoringQueriesConfigMap.name }}" + {{- if .Values.additionalEnv }} + {{- tpl (.Values.additionalEnvVars | toYaml) . | nindent 8 }} + {{- end }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} livenessProbe: diff --git a/charts/cloudnative-pg/values.yaml b/charts/cloudnative-pg/values.yaml index 2715399ab..f240cb359 100644 --- a/charts/cloudnative-pg/values.yaml +++ b/charts/cloudnative-pg/values.yaml @@ -66,6 +66,14 @@ config: # -- Additinal arguments to be added to the operator's args list. additionalArgs: [] +# -- Array containing extra environment variables which can be templated. +# For example: +# - name: RELEASE_NAME +# value: "{{ .Release.Name }}" +# - name: MY_VAR +# value: "mySpecialKey" +additionalEnv: [] + serviceAccount: # -- Specifies whether the service account should be created. create: true From 75fb6032640b3f690682ed395ddce0b3f47eeac3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 12:45:45 +0300 Subject: [PATCH 79/87] Release cloudnative-pg-v0.21.3 (#304) Signed-off-by: Itay Grudev Co-authored-by: Itay Grudev --- charts/cloudnative-pg/Chart.yaml | 2 +- charts/cloudnative-pg/README.md | 4 ++-- charts/cloudnative-pg/values.schema.json | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index 0a8c12a07..4744fd073 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,7 +18,7 @@ name: cloudnative-pg description: CloudNativePG Operator Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.21.2" +version: "0.21.3" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 40cbd6e1a..4cacea8ce 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.21.2](https://img.shields.io/badge/Version-0.21.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.1](https://img.shields.io/badge/AppVersion-1.23.1-informational?style=flat-square) +![Version: 0.21.3](https://img.shields.io/badge/Version-0.21.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.1](https://img.shields.io/badge/AppVersion-1.23.1-informational?style=flat-square) CloudNativePG Operator Helm Chart @@ -27,7 +27,7 @@ CloudNativePG Operator Helm Chart | Key | Type | Default | Description | |-----|------|---------|-------------| | additionalArgs | list | `[]` | Additinal arguments to be added to the operator's args list. | -| additionalEnv | list | `[]` | Array containing extra environment variables which can be templated. See [values.yaml](./values.yaml) for examples. +| additionalEnv | list | `[]` | Array containing extra environment variables which can be templated. For example: - name: RELEASE_NAME value: "{{ .Release.Name }}" - name: MY_VAR value: "mySpecialKey" | | affinity | object | `{}` | Affinity for the operator to be installed. | | commonAnnotations | object | `{}` | Annotations to be added to all other resources. | | config | object | `{"create":true,"data":{},"name":"cnpg-controller-manager-config","secret":false}` | Operator configuration. | diff --git a/charts/cloudnative-pg/values.schema.json b/charts/cloudnative-pg/values.schema.json index 68434c745..6c3779ac2 100644 --- a/charts/cloudnative-pg/values.schema.json +++ b/charts/cloudnative-pg/values.schema.json @@ -5,6 +5,9 @@ "additionalArgs": { "type": "array" }, + "additionalEnv": { + "type": "array" + }, "affinity": { "type": "object" }, From 5fd71cf969ef7f09405c129f4eb984d7d59c6519 Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 30 May 2024 11:28:17 -0400 Subject: [PATCH 80/87] fix additionalEnv typo in deployment.yaml (#305) Signed-off-by: Steven --- charts/cloudnative-pg/templates/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/cloudnative-pg/templates/deployment.yaml b/charts/cloudnative-pg/templates/deployment.yaml index 80a656d5c..515d52ecc 100644 --- a/charts/cloudnative-pg/templates/deployment.yaml +++ b/charts/cloudnative-pg/templates/deployment.yaml @@ -73,7 +73,7 @@ spec: - name: MONITORING_QUERIES_CONFIGMAP value: "{{ .Values.monitoringQueriesConfigMap.name }}" {{- if .Values.additionalEnv }} - {{- tpl (.Values.additionalEnvVars | toYaml) . | nindent 8 }} + {{- tpl (.Values.additionalEnv | toYaml) . | nindent 8 }} {{- end }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} From b378ad06aee0e9902eaaf8413784e5e3bb724861 Mon Sep 17 00:00:00 2001 From: Itay Grudev Date: Thu, 30 May 2024 20:21:17 +0300 Subject: [PATCH 81/87] Release cloudnative-pg-v0.21.4 (#307) Signed-off-by: Itay Grudev --- charts/cloudnative-pg/Chart.yaml | 2 +- charts/cloudnative-pg/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index 4744fd073..c71537116 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,7 +18,7 @@ name: cloudnative-pg description: CloudNativePG Operator Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.21.3" +version: "0.21.4" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index 4cacea8ce..e193a55fe 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.21.3](https://img.shields.io/badge/Version-0.21.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.1](https://img.shields.io/badge/AppVersion-1.23.1-informational?style=flat-square) +![Version: 0.21.4](https://img.shields.io/badge/Version-0.21.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.1](https://img.shields.io/badge/AppVersion-1.23.1-informational?style=flat-square) CloudNativePG Operator Helm Chart From ce57b3d3a40df702f113029a0bad9452d6e4d045 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 22:20:00 +0300 Subject: [PATCH 82/87] chore(deps): update actions/checkout action to v4.1.7 (#318) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- .github/workflows/release-pr.yml | 2 +- .github/workflows/release-publish.yml | 2 +- .github/workflows/tests-cluster-standalone.yml | 4 ++-- .github/workflows/tests-operator.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9cf1e3624..ccd1dbd2a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 448808a59..350caf243 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Create Pull Request id: create-pr env: diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index f7aa0ee9e..a2dcd3537 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 # important for fetching all history to run comparison against diff --git a/.github/workflows/tests-cluster-standalone.yml b/.github/workflows/tests-cluster-standalone.yml index 70b4c6f83..99488d3f2 100644 --- a/.github/workflows/tests-cluster-standalone.yml +++ b/.github/workflows/tests-cluster-standalone.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 diff --git a/.github/workflows/tests-operator.yml b/.github/workflows/tests-operator.yml index 87474c18d..a8fe41d20 100644 --- a/.github/workflows/tests-operator.yml +++ b/.github/workflows/tests-operator.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 From 50666bd57f8a63b8b28eeca70ab2c0b2404ab1c4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 16:00:56 +0200 Subject: [PATCH 83/87] Release cloudnative-pg-v0.21.5 (#315) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Niccolò Fei Co-authored-by: Niccolò Fei --- CONTRIBUTING.md | 2 +- charts/cloudnative-pg/Chart.yaml | 4 +- charts/cloudnative-pg/README.md | 2 +- .../cloudnative-pg/templates/crds/crds.yaml | 787 +++++++++++++++--- charts/cloudnative-pg/templates/rbac.yaml | 8 - 5 files changed, 680 insertions(+), 123 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0c8321019..ae6f50c48 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ Before reporting a new issue, please make sure you have: ## How to contribute -You can contribute to cnp-bench by submitting a new pull request. +You can contribute to charts by submitting a new pull request. Please: diff --git a/charts/cloudnative-pg/Chart.yaml b/charts/cloudnative-pg/Chart.yaml index c71537116..a689af2b5 100644 --- a/charts/cloudnative-pg/Chart.yaml +++ b/charts/cloudnative-pg/Chart.yaml @@ -18,12 +18,12 @@ name: cloudnative-pg description: CloudNativePG Operator Helm Chart icon: https://raw.githubusercontent.com/cloudnative-pg/artwork/main/cloudnativepg-logo.svg type: application -version: "0.21.4" +version: "0.21.5" # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning, they should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.23.1" +appVersion: "1.23.2" sources: - https://github.com/cloudnative-pg/charts keywords: diff --git a/charts/cloudnative-pg/README.md b/charts/cloudnative-pg/README.md index e193a55fe..2aa7853ab 100644 --- a/charts/cloudnative-pg/README.md +++ b/charts/cloudnative-pg/README.md @@ -1,6 +1,6 @@ # cloudnative-pg -![Version: 0.21.4](https://img.shields.io/badge/Version-0.21.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.1](https://img.shields.io/badge/AppVersion-1.23.1-informational?style=flat-square) +![Version: 0.21.5](https://img.shields.io/badge/Version-0.21.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.23.2](https://img.shields.io/badge/AppVersion-1.23.2-informational?style=flat-square) CloudNativePG Operator Helm Chart diff --git a/charts/cloudnative-pg/templates/crds/crds.yaml b/charts/cloudnative-pg/templates/crds/crds.yaml index 0d4ea03f0..72377c3e5 100644 --- a/charts/cloudnative-pg/templates/crds/crds.yaml +++ b/charts/cloudnative-pg/templates/crds/crds.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 helm.sh/resource-policy: keep name: backups.postgresql.cnpg.io spec: @@ -440,7 +440,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 helm.sh/resource-policy: keep name: clusterimagecatalogs.postgresql.cnpg.io spec: @@ -522,7 +522,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 helm.sh/resource-policy: keep name: clusters.postgresql.cnpg.io spec: @@ -639,11 +639,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -658,12 +660,12 @@ spec: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. - Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -673,12 +675,12 @@ spec: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. - Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -719,11 +721,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -743,6 +747,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -765,6 +770,7 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the affinity requirements specified by this field are not met at @@ -814,11 +820,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -833,12 +841,12 @@ spec: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. - Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -848,12 +856,12 @@ spec: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. - Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -893,11 +901,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -917,6 +927,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -929,6 +940,7 @@ spec: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object additionalPodAntiAffinity: description: |- @@ -986,11 +998,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -1005,12 +1019,12 @@ spec: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. - Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -1020,12 +1034,12 @@ spec: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. - Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -1066,11 +1080,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -1090,6 +1106,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -1112,6 +1129,7 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the anti-affinity requirements specified by this field are not met at @@ -1161,11 +1179,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -1180,12 +1200,12 @@ spec: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. - Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -1195,12 +1215,12 @@ spec: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. - Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -1240,11 +1260,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -1264,6 +1286,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -1276,6 +1299,7 @@ spec: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object enablePodAntiAffinity: description: |- @@ -1334,11 +1358,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. @@ -1366,11 +1392,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic weight: @@ -1383,6 +1411,7 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the affinity requirements specified by this field are not met at @@ -1427,11 +1456,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. @@ -1459,14 +1490,17 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic type: array + x-kubernetes-list-type: atomic required: - nodeSelectorTerms type: object @@ -2453,10 +2487,15 @@ spec: description: The key to select. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the ConfigMap or its key @@ -2515,10 +2554,15 @@ spec: be a valid secret key. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret or its key must @@ -2544,10 +2588,15 @@ spec: description: The ConfigMap to select from properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the ConfigMap must be defined @@ -2562,10 +2611,15 @@ spec: description: The Secret to select from properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret must be defined @@ -2625,6 +2679,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -2764,11 +2819,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -2796,7 +2853,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -3168,10 +3225,15 @@ spec: be a valid secret key. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret or its key must @@ -3191,10 +3253,15 @@ spec: be a valid secret key. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret or its key must @@ -3214,10 +3281,15 @@ spec: be a valid secret key. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret or its key must @@ -3237,10 +3309,15 @@ spec: be a valid secret key. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret or its key must @@ -3342,6 +3419,14 @@ spec: description: Number of instances required in the cluster minimum: 1 type: integer + livenessProbeTimeout: + description: |- + LivenessProbeTimeout is the time (in seconds) that is allowed for a PostgreSQL instance + to successfully respond to the liveness probe (default 30). + The Liveness probe failure threshold is derived from this value using the formula: + ceiling(livenessProbe / 10). + format: int32 + type: integer logLevel: default: info description: 'The instances'' log level, one of the following values: @@ -3816,10 +3901,15 @@ spec: be a valid secret key. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret or its key @@ -4005,11 +4095,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -4088,11 +4180,17 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: optional specify whether the ConfigMap @@ -4112,8 +4210,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + pod: only annotations, labels, name, namespace + and uid are supported.' properties: apiVersion: description: Version of the schema the FieldPath @@ -4172,6 +4270,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the secret data to @@ -4215,11 +4314,17 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: optional field specify whether the Secret @@ -4258,6 +4363,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object replica: description: Replica cluster configuration @@ -4494,6 +4600,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -4633,11 +4740,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -4665,7 +4774,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -4745,6 +4854,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -4888,11 +4998,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -4920,7 +5032,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -5005,11 +5117,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -5080,9 +5194,6 @@ spec: In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew. - - - This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default). format: int32 type: integer nodeAffinityPolicy: @@ -5163,6 +5274,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -5302,11 +5414,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -5334,7 +5448,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -5921,7 +6035,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 helm.sh/resource-policy: keep name: imagecatalogs.postgresql.cnpg.io spec: @@ -6002,7 +6116,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 helm.sh/resource-policy: keep name: poolers.postgresql.cnpg.io spec: @@ -6470,6 +6584,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic externalName: description: |- externalName is the external reference that discovery mechanisms will @@ -6585,6 +6700,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic ports: description: |- The list of ports that are exposed by this service. @@ -6715,6 +6831,16 @@ spec: type: integer type: object type: object + trafficDistribution: + description: |- + TrafficDistribution offers a way to express preferences for how traffic is + distributed to Service endpoints. Implementations can use this field as a + hint, but are not required to guarantee strict adherence. If the field is + not set, the implementation will apply its default routing strategy. If set + to "PreferClose", implementations should prioritize endpoints that are + topologically close (e.g., same zone). + This is an alpha field and requires enabling ServiceTrafficDistribution feature. + type: string type: description: |- type determines how the Service is exposed. Defaults to ClusterIP. Valid @@ -6829,11 +6955,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. @@ -6861,11 +6989,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic weight: @@ -6879,6 +7009,7 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the affinity requirements specified by this field are not met at @@ -6923,11 +7054,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. @@ -6955,14 +7088,17 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic type: array + x-kubernetes-list-type: atomic required: - nodeSelectorTerms type: object @@ -7026,11 +7162,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -7045,12 +7183,12 @@ spec: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. - Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -7060,12 +7198,12 @@ spec: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. - Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -7107,11 +7245,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -7131,6 +7271,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -7153,6 +7294,7 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the affinity requirements specified by this field are not met at @@ -7203,11 +7345,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -7222,12 +7366,12 @@ spec: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. - Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -7237,12 +7381,12 @@ spec: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. - Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -7283,11 +7427,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -7307,6 +7453,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -7319,6 +7466,7 @@ spec: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object podAntiAffinity: description: Describes pod anti-affinity scheduling rules @@ -7378,11 +7526,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -7397,12 +7547,12 @@ spec: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. - Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -7412,12 +7562,12 @@ spec: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. - Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -7459,11 +7609,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -7483,6 +7635,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -7505,6 +7658,7 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the anti-affinity requirements specified by this field are not met at @@ -7555,11 +7709,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -7574,12 +7730,12 @@ spec: description: |- MatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. - Also, MatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -7589,12 +7745,12 @@ spec: description: |- MismatchLabelKeys is a set of pod label keys to select which pods will be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)` + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` to select the group of existing pods which pods will be taken into consideration for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector. - Also, MismatchLabelKeys cannot be set when LabelSelector isn't set. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. items: type: string @@ -7635,11 +7791,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -7659,6 +7817,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -7671,6 +7830,7 @@ spec: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object type: object automountServiceAccountToken: @@ -7700,6 +7860,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic command: description: |- Entrypoint array. Not executed within a shell. @@ -7713,6 +7874,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic env: description: |- List of environment variables to set in the container. @@ -7748,10 +7910,15 @@ spec: description: The key to select. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the ConfigMap @@ -7814,10 +7981,15 @@ spec: key. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret @@ -7832,6 +8004,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map envFrom: description: |- List of sources to populate environment variables in the container. @@ -7848,10 +8023,15 @@ spec: description: The ConfigMap to select from properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the ConfigMap @@ -7867,10 +8047,15 @@ spec: description: The Secret to select from properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret must @@ -7880,6 +8065,7 @@ spec: x-kubernetes-map-type: atomic type: object type: array + x-kubernetes-list-type: atomic image: description: |- Container image name. @@ -7920,6 +8106,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: description: HTTPGet specifies the http request @@ -7950,6 +8137,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. @@ -8031,6 +8219,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: description: HTTPGet specifies the http request @@ -8061,6 +8250,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. @@ -8138,6 +8328,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: description: |- @@ -8194,6 +8385,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -8351,6 +8543,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: description: |- @@ -8407,6 +8600,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -8602,6 +8796,30 @@ spec: 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows. type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object capabilities: description: |- The capabilities to add/drop when running containers. @@ -8615,6 +8833,7 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic drop: description: Removed capabilities items: @@ -8622,6 +8841,7 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic type: object privileged: description: |- @@ -8779,6 +8999,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: description: |- @@ -8835,6 +9056,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -8977,6 +9199,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map volumeMounts: description: |- Pod volumes to mount into the container's filesystem. @@ -8996,6 +9221,8 @@ spec: to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. @@ -9005,6 +9232,29 @@ spec: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + + If ReadOnly is false, this field has no meaning and must be unspecified. + + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: description: |- Path within the volume from which the container's volume should be mounted. @@ -9022,6 +9272,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map workingDir: description: |- Container's working directory. @@ -9033,6 +9286,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map dnsConfig: description: |- Specifies the DNS parameters of a pod. @@ -9047,6 +9303,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic options: description: |- A list of DNS resolver options. @@ -9064,6 +9321,7 @@ spec: type: string type: object type: array + x-kubernetes-list-type: atomic searches: description: |- A list of DNS search domains for host-name lookup. @@ -9072,6 +9330,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object dnsPolicy: description: |- @@ -9119,6 +9378,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic command: description: |- Entrypoint array. Not executed within a shell. @@ -9132,6 +9392,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic env: description: |- List of environment variables to set in the container. @@ -9167,10 +9428,15 @@ spec: description: The key to select. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the ConfigMap @@ -9233,10 +9499,15 @@ spec: key. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret @@ -9251,6 +9522,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map envFrom: description: |- List of sources to populate environment variables in the container. @@ -9267,10 +9541,15 @@ spec: description: The ConfigMap to select from properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the ConfigMap @@ -9286,10 +9565,15 @@ spec: description: The Secret to select from properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret must @@ -9299,6 +9583,7 @@ spec: x-kubernetes-map-type: atomic type: object type: array + x-kubernetes-list-type: atomic image: description: |- Container image name. @@ -9336,6 +9621,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: description: HTTPGet specifies the http request @@ -9366,6 +9652,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. @@ -9447,6 +9734,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: description: HTTPGet specifies the http request @@ -9477,6 +9765,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. @@ -9550,6 +9839,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: description: |- @@ -9606,6 +9896,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -9751,6 +10042,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: description: |- @@ -9807,6 +10099,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -9989,6 +10282,30 @@ spec: 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows. type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object capabilities: description: |- The capabilities to add/drop when running containers. @@ -10002,6 +10319,7 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic drop: description: Removed capabilities items: @@ -10009,6 +10327,7 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic type: object privileged: description: |- @@ -10159,6 +10478,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: description: |- @@ -10215,6 +10535,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -10367,6 +10688,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map volumeMounts: description: |- Pod volumes to mount into the container's filesystem. Subpath mounts are not allowed for ephemeral containers. @@ -10386,6 +10710,8 @@ spec: to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. @@ -10395,6 +10721,29 @@ spec: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + + If ReadOnly is false, this field has no meaning and must be unspecified. + + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: description: |- Path within the volume from which the container's volume should be mounted. @@ -10412,6 +10761,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map workingDir: description: |- Container's working directory. @@ -10423,10 +10775,13 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map hostAliases: description: |- HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts - file if specified. This is only valid for non-hostNetwork pods. + file if specified. items: description: |- HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the @@ -10437,11 +10792,17 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic ip: description: IP address of the host file entry. type: string + required: + - ip type: object type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map hostIPC: description: |- Use the host's ipc namespace. @@ -10486,14 +10847,22 @@ spec: referenced object inside the same namespace. properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string type: object x-kubernetes-map-type: atomic type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map initContainers: description: |- List of initialization containers belonging to the pod. @@ -10526,6 +10895,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic command: description: |- Entrypoint array. Not executed within a shell. @@ -10539,6 +10909,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic env: description: |- List of environment variables to set in the container. @@ -10574,10 +10945,15 @@ spec: description: The key to select. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the ConfigMap @@ -10640,10 +11016,15 @@ spec: key. type: string name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret @@ -10658,6 +11039,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map envFrom: description: |- List of sources to populate environment variables in the container. @@ -10674,10 +11058,15 @@ spec: description: The ConfigMap to select from properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the ConfigMap @@ -10693,10 +11082,15 @@ spec: description: The Secret to select from properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: Specify whether the Secret must @@ -10706,6 +11100,7 @@ spec: x-kubernetes-map-type: atomic type: object type: array + x-kubernetes-list-type: atomic image: description: |- Container image name. @@ -10746,6 +11141,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: description: HTTPGet specifies the http request @@ -10776,6 +11172,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. @@ -10857,6 +11254,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object httpGet: description: HTTPGet specifies the http request @@ -10887,6 +11285,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. @@ -10964,6 +11363,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: description: |- @@ -11020,6 +11420,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -11177,6 +11578,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: description: |- @@ -11233,6 +11635,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -11428,6 +11831,30 @@ spec: 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows. type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object capabilities: description: |- The capabilities to add/drop when running containers. @@ -11441,6 +11868,7 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic drop: description: Removed capabilities items: @@ -11448,6 +11876,7 @@ spec: type type: string type: array + x-kubernetes-list-type: atomic type: object privileged: description: |- @@ -11605,6 +12034,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object failureThreshold: description: |- @@ -11661,6 +12091,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic path: description: Path to access on the HTTP server. type: string @@ -11803,6 +12234,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map volumeMounts: description: |- Pod volumes to mount into the container's filesystem. @@ -11822,6 +12256,8 @@ spec: to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. @@ -11831,6 +12267,29 @@ spec: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + + If ReadOnly is false, this field has no meaning and must be unspecified. + + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: description: |- Path within the volume from which the container's volume should be mounted. @@ -11848,6 +12307,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map workingDir: description: |- Container's working directory. @@ -11859,6 +12321,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map nodeName: description: |- NodeName is a request to schedule this pod onto a specific node. If it is non-empty, @@ -11888,6 +12353,7 @@ spec: - spec.hostPID - spec.hostIPC - spec.hostUsers + - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup @@ -11897,6 +12363,7 @@ spec: - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups + - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities @@ -11975,6 +12442,7 @@ spec: - conditionType type: object type: array + x-kubernetes-list-type: atomic resourceClaims: description: |- ResourceClaims defines which ResourceClaims must be allocated @@ -12060,9 +12528,6 @@ spec: SchedulingGates can only be set at pod creation time, and be removed only afterwards. - - - This is a beta feature enabled by the PodSchedulingReadiness feature gate. items: description: PodSchedulingGate is associated to a Pod to guard its scheduling. @@ -12084,6 +12549,29 @@ spec: SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field. properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object fsGroup: description: |- A special supplemental group that applies to all containers in a pod. @@ -12203,6 +12691,7 @@ spec: format: int64 type: integer type: array + x-kubernetes-list-type: atomic sysctls: description: |- Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported @@ -12223,6 +12712,7 @@ spec: - value type: object type: array + x-kubernetes-list-type: atomic windowsOptions: description: |- The Windows specific settings applied to all containers. @@ -12258,7 +12748,7 @@ spec: type: object serviceAccount: description: |- - DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. + DeprecatedServiceAccount is a deprecated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. type: string serviceAccountName: @@ -12338,6 +12828,7 @@ spec: type: string type: object type: array + x-kubernetes-list-type: atomic topologySpreadConstraints: description: |- TopologySpreadConstraints describes how a group of pods ought to spread across topology @@ -12379,11 +12870,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -12454,9 +12947,6 @@ spec: In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew. - - - This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default). format: int32 type: integer nodeAffinityPolicy: @@ -12638,6 +13128,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default @@ -12660,10 +13151,15 @@ spec: More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string type: object x-kubernetes-map-type: atomic @@ -12699,10 +13195,15 @@ spec: to OpenStack. properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string type: object x-kubernetes-map-type: atomic @@ -12767,11 +13268,17 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: optional specify whether the ConfigMap @@ -12804,10 +13311,15 @@ spec: secret object contains more than one secret, all secret references are passed. properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string type: object x-kubernetes-map-type: atomic @@ -12852,8 +13364,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of - the pod: only annotations, labels, name - and namespace are supported.' + the pod: only annotations, labels, name, + namespace and uid are supported.' properties: apiVersion: description: Version of the schema the @@ -12914,6 +13426,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: description: |- @@ -13019,6 +13532,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -13164,11 +13678,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -13196,7 +13712,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -13240,6 +13756,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: description: |- wwids Optional: FC volume world wide identifiers (wwids) @@ -13247,6 +13764,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: description: |- @@ -13283,10 +13801,15 @@ spec: scripts. properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string type: object x-kubernetes-map-type: atomic @@ -13467,6 +13990,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: description: |- readOnly here will force the ReadOnly setting in VolumeMounts. @@ -13477,10 +14001,15 @@ spec: target and initiator authentication properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string type: object x-kubernetes-map-type: atomic @@ -13658,11 +14187,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -13741,11 +14272,17 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: optional specify whether @@ -13768,8 +14305,8 @@ spec: fieldRef: description: 'Required: Selects a field of the pod: only annotations, - labels, name and namespace are - supported.' + labels, name, namespace and uid + are supported.' properties: apiVersion: description: Version of the @@ -13835,6 +14372,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the @@ -13878,11 +14416,17 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string optional: description: optional field specify whether @@ -13921,6 +14465,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: description: quobyte represents a Quobyte mount on the @@ -13991,6 +14536,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic pool: description: |- pool is the rados pool name. @@ -14011,10 +14557,15 @@ spec: More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string type: object x-kubernetes-map-type: atomic @@ -14058,10 +14609,15 @@ spec: sensitive information. If this is not provided, Login operation will fail. properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string type: object x-kubernetes-map-type: atomic @@ -14146,6 +14702,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: description: optional field specify whether the Secret or its keys must be defined @@ -14177,10 +14734,15 @@ spec: credentials. If not specified, default values will be attempted. properties: name: + default: "" description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string type: object x-kubernetes-map-type: atomic @@ -14229,6 +14791,9 @@ spec: - name type: object type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map required: - containers type: object @@ -14319,7 +14884,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 helm.sh/resource-policy: keep name: scheduledbackups.postgresql.cnpg.io spec: diff --git a/charts/cloudnative-pg/templates/rbac.yaml b/charts/cloudnative-pg/templates/rbac.yaml index 1865bccab..f2bf0e805 100644 --- a/charts/cloudnative-pg/templates/rbac.yaml +++ b/charts/cloudnative-pg/templates/rbac.yaml @@ -67,14 +67,6 @@ rules: verbs: - create - patch -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - list - - watch - apiGroups: - "" resources: From fd5eff94b986797100155ad4638555cda3fb5823 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 15 Jun 2024 11:11:40 -0500 Subject: [PATCH 84/87] Add walStorage capability and remove gkeEnvironment conflict in cluster chart. (#312) * Add walStorage option to cluster.yaml template * Bug Fix: Remove applicationCredentials when gktEnvironment is true. Signed-off-by: Jon --- charts/cluster/templates/_barman_object_store.tpl | 2 ++ charts/cluster/templates/cluster.yaml | 6 +++++- charts/cluster/values.schema.json | 11 +++++++++++ charts/cluster/values.yaml | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/charts/cluster/templates/_barman_object_store.tpl b/charts/cluster/templates/_barman_object_store.tpl index c685bbec1..becb9cfca 100644 --- a/charts/cluster/templates/_barman_object_store.tpl +++ b/charts/cluster/templates/_barman_object_store.tpl @@ -62,8 +62,10 @@ {{- $secretName := coalesce .scope.secret.name (printf "%s-%s-google-creds" .chartFullname .secretPrefix) }} googleCredentials: gkeEnvironment: {{ .scope.google.gkeEnvironment }} +{{- if not .scope.google.gkeEnvironment }} applicationCredentials: name: {{ $secretName }} key: APPLICATION_CREDENTIALS +{{- end }} {{- end -}} {{- end -}} diff --git a/charts/cluster/templates/cluster.yaml b/charts/cluster/templates/cluster.yaml index 5ff0bb2fa..2dace8e34 100644 --- a/charts/cluster/templates/cluster.yaml +++ b/charts/cluster/templates/cluster.yaml @@ -24,7 +24,11 @@ spec: storage: size: {{ .Values.cluster.storage.size }} storageClass: {{ .Values.cluster.storage.storageClass }} - +{{- if .Values.cluster.walStorage }} + walStorage: + size: {{ .Values.cluster.walStorage.size }} + storageClass: {{ .Values.cluster.walStorage.storageClass }} +{{- end }} {{- with .Values.cluster.resources }} resources: {{- toYaml . | nindent 4 }} diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 1ceac4e19..9bcf7b4b3 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -270,6 +270,17 @@ } } }, + "walStorage": { + "type": "object", + "properties": { + "size": { + "type": "string" + }, + "storageClass": { + "type": "string" + } + } + }, "superuserSecret": { "type": "string" } diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index c0c10c512..c4a48d232 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -101,6 +101,10 @@ cluster: storage: size: 8Gi storageClass: "" + + walStorage: + size: 1Gi + storageClass: "" # -- The UID of the postgres user inside the image, defaults to 26 postgresUID: 26 From 3ed5aa8c736dc97fa277f3e73634b7d1e03253a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 09:10:17 +0200 Subject: [PATCH 85/87] chore(deps): update actions/setup-python action to v5.1.1 (#330) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ccd1dbd2a..6f40dcd39 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: with: version: v3.4.0 - - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: 3.7 From c165c5ce760a01655b7c94d4bc9d951d941f3c5d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 22:55:11 +0300 Subject: [PATCH 86/87] chore(deps): update docker/login-action action to v3.3.0 (#333) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index a2dcd3537..5854184dc 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -58,7 +58,7 @@ jobs: run: shred --remove=wipesync /tmp/keyring.gpg /tmp/passphrase-file.txt - name: Login to GitHub Container Registry - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ghcr.io username: ${{ github.actor }} From b134c4e3862b76bb6661725cbbbf360bc5c9ad88 Mon Sep 17 00:00:00 2001 From: Marc Khair <67377306+marckhair@users.noreply.github.com> Date: Tue, 30 Jul 2024 04:19:20 -0400 Subject: [PATCH 87/87] chore: add support for `hostNetwork` and `dnsPolicy` in chart (#324) In some Kubernetes clusters the default values for `dnsPolicy` and `hostNetwork` is not set for the needs of the cluster or in some cases it just need to be changed, this patch allows to set the `dnsPolicy` and the `hostNetwork` values for the operator deployments to fit the cluster requirement. Closes #177 Signed-off-by: Marc Khair <67377306+marckhair@users.noreply.github.com> --- charts/cloudnative-pg/templates/deployment.yaml | 6 ++++++ charts/cloudnative-pg/values.schema.json | 6 ++++++ charts/cloudnative-pg/values.yaml | 3 +++ 3 files changed, 15 insertions(+) diff --git a/charts/cloudnative-pg/templates/deployment.yaml b/charts/cloudnative-pg/templates/deployment.yaml index 515d52ecc..569752007 100644 --- a/charts/cloudnative-pg/templates/deployment.yaml +++ b/charts/cloudnative-pg/templates/deployment.yaml @@ -46,6 +46,12 @@ spec: imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} + {{- if .Values.hostNetwork }} + hostNetwork: {{ .Values.hostNetwork }} + {{- end }} + {{- if .Values.dnsPolicy }} + dnsPolicy: {{ .Values.dnsPolicy }} + {{- end }} containers: - args: - controller diff --git a/charts/cloudnative-pg/values.schema.json b/charts/cloudnative-pg/values.schema.json index 6c3779ac2..73aa3de39 100644 --- a/charts/cloudnative-pg/values.schema.json +++ b/charts/cloudnative-pg/values.schema.json @@ -95,6 +95,12 @@ "imagePullSecrets": { "type": "array" }, + "hostNetwork": { + "type": "boolean" + }, + "dnsPolicy": { + "type": "string" + }, "monitoring": { "type": "object", "properties": { diff --git a/charts/cloudnative-pg/values.yaml b/charts/cloudnative-pg/values.yaml index f240cb359..41c9aadc1 100644 --- a/charts/cloudnative-pg/values.yaml +++ b/charts/cloudnative-pg/values.yaml @@ -29,6 +29,9 @@ imagePullSecrets: [] nameOverride: "" fullnameOverride: "" +hostNetwork: false +dnsPolicy: "" + crds: # -- Specifies whether the CRDs should be created when installing the chart. create: true