diff --git a/README.md b/README.md index df96a58..8189f0e 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ helm repo add appcat https://charts.appcat.ch | Downloads & Changelog | Chart | | --- | --- | | [![chart downloads](https://img.shields.io/github/downloads/vshn/appcat-charts/vshnmariadb-0.0.10/total)](https://github.com/vshn/appcat-charts/releases/tag/vshnmariadb-0.0.10) | [vshnmariadb](charts/vshnmariadb/README.md) | +| [![chart downloads](https://img.shields.io/github/downloads/vshn/appcat-charts/vshnpostgresql-0.0.1/total)](https://github.com/vshn/appcat-charts/releases/tag/vshnpostgresql-0.0.1) | [vshnpostgresql](charts/vshnpostgresql/README.md) | ## Add / Update Charts diff --git a/charts/vshnpostgresql/Chart.yaml b/charts/vshnpostgresql/Chart.yaml new file mode 100644 index 0000000..3d226a8 --- /dev/null +++ b/charts/vshnpostgresql/Chart.yaml @@ -0,0 +1,30 @@ +# +# Modified by VSHN AG, Copyright 2025 +# Based on CloudNativePG cluster chart +# https://github.com/cloudnative-pg/charts +# +# Copyright © contributors to CloudNativePG, established as +# CloudNativePG a Series of LF Projects, LLC. +# +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# +apiVersion: v2 +name: vshnpostgresql +description: Deploys and manages a CloudNativePG cluster and its associated resources. +type: application +version: 0.0.1 +maintainers: + - name: Schedar Team + email: info@vshn.ch diff --git a/charts/vshnpostgresql/LICENSE b/charts/vshnpostgresql/LICENSE new file mode 100644 index 0000000..dab15ac --- /dev/null +++ b/charts/vshnpostgresql/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Support. While redistributing the Work or + Derivative Works thereof, You may choose to offer, and charge a fee + for, acceptance of support, warranty, indemnity, or other liability + obligations and/or rights consistent with this License. However, in + accepting such obligations, You may act only on Your own behalf and + on Your sole responsibility, not on behalf of any other Contributor, + and only if You agree to indemnify, defend, and hold each Contributor + harmless for any liability incurred by, or claims asserted against, + such Contributor by reason of your accepting any such warranty or + additional liability. + + END OF TERMS AND CONDITIONS diff --git a/charts/vshnpostgresql/NOTICE b/charts/vshnpostgresql/NOTICE new file mode 100644 index 0000000..a0090a9 --- /dev/null +++ b/charts/vshnpostgresql/NOTICE @@ -0,0 +1,20 @@ +vshnpostgresql Helm Chart +Copyright 2025 VSHN AG + +This chart is based on the CloudNativePG "cluster" Helm chart from: +https://github.com/cloudnative-pg/charts + +Original work Copyright contributors to CloudNativePG, established as +CloudNativePG a Series of LF Projects, LLC. + +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. diff --git a/charts/vshnpostgresql/README.gotmpl.md b/charts/vshnpostgresql/README.gotmpl.md new file mode 100644 index 0000000..6d20539 --- /dev/null +++ b/charts/vshnpostgresql/README.gotmpl.md @@ -0,0 +1,83 @@ + + +## Introduction + +This helm chart is used to deploy PostgreSQL instances using the [CloudNativePG operator](https://cloudnative-pg.io). + +## Configuration + +The following table lists the configurable parameters chart. For default values and examples, consult `values.yaml`. + +### Generic parameters +| Parameter | Description | Default +|--- | --- | --- +| `type` | Type of database. Supported types: `postgresql`, `postgis`, `timescaledb` | `postgresql` +| `version.postgresql` | PostgreSQL major version to use | `16` +| `cluster.instances` | Number of instances to deploy | 3 +| `cluster.imageName` | Container image name supporting both tags and digests | +| `cluster.storage.size` | PVC size of the instance | 8Gi +| `cluster.storage.storageClass` | StorageClass to use for the PVC | +| `cluster.resources` | Resources describes the compute resource requirements. We strongly advise using the same settings for limits and requests for Guaranteed QoS. See: [resource-units] | +| `cluster.affinity` | Defines the affinity for the pods | `{"topologyKey": "topology.kubernetes.io/zone"}` + +### Backup + +| Parameter | Description | Default +| --- | --- | --- +| `backups.enabled` | Enabled is a flag to enable backups | false +| `backups.method` | Backup method: `barmanObjectStore` or `plugin` | `barmanObjectStore` +| `backups.provider` | Backup provider: `s3`, `azure` or `google` | `s3` +| `backups.retentionPolicy` | Retention policy for backups | `30d` +| `backups.s3.bucket` | Name of the s3 bucket for backups | +| `backups.s3.region` | AWS region for the s3 bucket | +| `backups.s3.accessKey` | Access key for the s3 bucket | +| `backups.s3.secretKey` | Secret key for the s3 bucket | +| `backups.scheduledBackups[].name` | Name of the scheduled backup | `daily-backup` +| `backups.scheduledBackups[].schedule` | Cron schedule for the backup | `0 0 0 * * *` + +### Monitoring + +| Parameter | Description | Default +| --- | --- | --- +| `cluster.monitoring.enabled` | Enable Prometheus monitoring | false +| `cluster.monitoring.podMonitor.enabled` | Enable PodMonitor creation | true +| `cluster.monitoring.prometheusRule.enabled` | Enable PrometheusRule for automated alerts | true + +### PostgreSQL configuration + +| Parameter | Description | Default +| --- | --- | --- +| `cluster.postgresql.parameters` | PostgreSQL configuration options (postgresql.conf) | `{}` +| `cluster.postgresql.pg_hba` | PostgreSQL Host Based Authentication rules | `[]` +| `cluster.postgresql.shared_preload_libraries` | Lists of shared preload libraries to add to defaults | `[]` + +### Connection Pooling (PgBouncer) + +| Parameter | Description | Default +| --- | --- | --- +| `poolers[].name` | Name of the pooler resource | +| `poolers[].type` | Type of service: `rw` or `ro` | `rw` +| `poolers[].poolMode` | PgBouncer pooling mode | `session` +| `poolers[].instances` | Number of PgBouncer instances | 1 +| `poolers[].parameters` | Additional PgBouncer configuration parameters | `{}` + +### Recovery + +| Parameter | Description | Default +| --- | --- | --- +| `mode` | Cluster mode: `standalone` or `recovery` | `standalone` +| `recovery.method` | Recovery method: `backup`, `object_store`, `pg_basebackup`, or `import` | `backup` +| `recovery.backupName` | Name of the backup to recover from (required for `backup` method) | +| `recovery.clusterName` | Original cluster name when used in backups | +| `recovery.provider` | Recovery provider: `s3`, `azure` or `google` | `s3` +| `recovery.pitrTarget.time` | Point-in-time recovery target in RFC3339 format | + + +[resource-units]: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes +[cloudnative-pg]: https://cloudnative-pg.io/documentation/current/ diff --git a/charts/vshnpostgresql/README.md b/charts/vshnpostgresql/README.md new file mode 100644 index 0000000..4d88b25 --- /dev/null +++ b/charts/vshnpostgresql/README.md @@ -0,0 +1,101 @@ +# vshnpostgresql + +![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) + +Deploys and manages a CloudNativePG cluster and its associated resources. + +## Installation + +```bash +helm repo add appcat https://charts.appcat.ch +helm install vshnpostgresql vshn/vshnpostgresql +``` + + +## Introduction + +This helm chart is used to deploy PostgreSQL instances using the [CloudNativePG operator](https://cloudnative-pg.io). + +## Configuration + +The following table lists the configurable parameters chart. For default values and examples, consult `values.yaml`. + +### Generic parameters +| Parameter | Description | Default +|--- | --- | --- +| `type` | Type of database. Supported types: `postgresql`, `postgis`, `timescaledb` | `postgresql` +| `version.postgresql` | PostgreSQL major version to use | `16` +| `cluster.instances` | Number of instances to deploy | 3 +| `cluster.imageName` | Container image name supporting both tags and digests | +| `cluster.storage.size` | PVC size of the instance | 8Gi +| `cluster.storage.storageClass` | StorageClass to use for the PVC | +| `cluster.resources` | Resources describes the compute resource requirements. We strongly advise using the same settings for limits and requests for Guaranteed QoS. See: [resource-units] | +| `cluster.affinity` | Defines the affinity for the pods | `{"topologyKey": "topology.kubernetes.io/zone"}` + +### Backup + +| Parameter | Description | Default +| --- | --- | --- +| `backups.enabled` | Enabled is a flag to enable backups | false +| `backups.method` | Backup method: `barmanObjectStore` or `plugin` | `barmanObjectStore` +| `backups.provider` | Backup provider: `s3`, `azure` or `google` | `s3` +| `backups.retentionPolicy` | Retention policy for backups | `30d` +| `backups.s3.bucket` | Name of the s3 bucket for backups | +| `backups.s3.region` | AWS region for the s3 bucket | +| `backups.s3.accessKey` | Access key for the s3 bucket | +| `backups.s3.secretKey` | Secret key for the s3 bucket | +| `backups.scheduledBackups[].name` | Name of the scheduled backup | `daily-backup` +| `backups.scheduledBackups[].schedule` | Cron schedule for the backup | `0 0 0 * * *` + +### Monitoring + +| Parameter | Description | Default +| --- | --- | --- +| `cluster.monitoring.enabled` | Enable Prometheus monitoring | false +| `cluster.monitoring.podMonitor.enabled` | Enable PodMonitor creation | true +| `cluster.monitoring.prometheusRule.enabled` | Enable PrometheusRule for automated alerts | true + +### PostgreSQL configuration + +| Parameter | Description | Default +| --- | --- | --- +| `cluster.postgresql.parameters` | PostgreSQL configuration options (postgresql.conf) | `{}` +| `cluster.postgresql.pg_hba` | PostgreSQL Host Based Authentication rules | `[]` +| `cluster.postgresql.shared_preload_libraries` | Lists of shared preload libraries to add to defaults | `[]` + +### Connection Pooling (PgBouncer) + +| Parameter | Description | Default +| --- | --- | --- +| `poolers[].name` | Name of the pooler resource | +| `poolers[].type` | Type of service: `rw` or `ro` | `rw` +| `poolers[].poolMode` | PgBouncer pooling mode | `session` +| `poolers[].instances` | Number of PgBouncer instances | 1 +| `poolers[].parameters` | Additional PgBouncer configuration parameters | `{}` + +### Recovery + +| Parameter | Description | Default +| --- | --- | --- +| `mode` | Cluster mode: `standalone` or `recovery` | `standalone` +| `recovery.method` | Recovery method: `backup`, `object_store`, `pg_basebackup`, or `import` | `backup` +| `recovery.backupName` | Name of the backup to recover from (required for `backup` method) | +| `recovery.clusterName` | Original cluster name when used in backups | +| `recovery.provider` | Recovery provider: `s3`, `azure` or `google` | `s3` +| `recovery.pitrTarget.time` | Point-in-time recovery target in RFC3339 format | + + +[resource-units]: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes +[cloudnative-pg]: https://cloudnative-pg.io/documentation/current/ + + +[resource-units]: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes +[prometheus-operator]: https://github.com/coreos/prometheus-operator diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-ha-critical.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-ha-critical.yaml new file mode 100644 index 0000000..246a5af --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-ha-critical.yaml @@ -0,0 +1,26 @@ +{{- $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 + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-ha-warning.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-ha-warning.yaml new file mode 100644 index 0000000..736ddf3 --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-ha-warning.yaml @@ -0,0 +1,24 @@ +{{- $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 + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-high_connection-critical.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-high_connection-critical.yaml new file mode 100644 index 0000000..df13ce3 --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-high_connection-critical.yaml @@ -0,0 +1,17 @@ +{{- $alert := "CNPGClusterHighConnectionsCritical" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Instance maximum number of connections critical! + description: |- + 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: | + 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 + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-high_connection-warning.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-high_connection-warning.yaml new file mode 100644 index 0000000..73cc783 --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-high_connection-warning.yaml @@ -0,0 +1,17 @@ +{{- $alert := "CNPGClusterHighConnectionsWarning" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Instance is approaching the maximum number of connections. + description: |- + 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: | + 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 + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-high_replication_lag.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-high_replication_lag.yaml new file mode 100644 index 0000000..660db25 --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-high_replication_lag.yaml @@ -0,0 +1,19 @@ +{{- $alert := "CNPGClusterHighReplicationLag" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster high replication lag + description: |- + 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. + 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 + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-instances_on_same_node.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-instances_on_same_node.yaml new file mode 100644 index 0000000..aafcfab --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-instances_on_same_node.yaml @@ -0,0 +1,19 @@ +{{- $alert := "CNPGClusterInstancesOnSameNode" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster instances are located on the same node. + description: |- + 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. + 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 + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-low_disk_space-critical.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-low_disk_space-critical.yaml new file mode 100644 index 0000000..c211bc6 --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-low_disk_space-critical.yaml @@ -0,0 +1,24 @@ +{{- $alert := "CNPGClusterLowDiskSpaceCritical" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Instance is running out of disk space! + description: |- + 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 + 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 + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-low_disk_space-warning.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-low_disk_space-warning.yaml new file mode 100644 index 0000000..5ed3653 --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-low_disk_space-warning.yaml @@ -0,0 +1,24 @@ +{{- $alert := "CNPGClusterLowDiskSpaceWarning" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Instance is running out of disk space. + description: |- + 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 + 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 + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-offline.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-offline.yaml new file mode 100644 index 0000000..4206c02 --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-offline.yaml @@ -0,0 +1,19 @@ +{{- $alert := "CNPGClusterOffline" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster has no running instances! + description: |- + CloudNativePG Cluster "{{ .namespace }}/{{ .cluster }}" 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: | + (count(cnpg_collector_up{namespace="{{ .namespace }}",pod=~"{{ .podSelector }}"}) OR on() vector(0)) == 0 +for: 5m +labels: + severity: critical + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/prometheus_rules/cluster-zone_spread-warning.yaml b/charts/vshnpostgresql/prometheus_rules/cluster-zone_spread-warning.yaml new file mode 100644 index 0000000..41fa400 --- /dev/null +++ b/charts/vshnpostgresql/prometheus_rules/cluster-zone_spread-warning.yaml @@ -0,0 +1,18 @@ +{{- $alert := "CNPGClusterZoneSpreadWarning" -}} +{{- if not (has $alert .excludeRules) -}} +alert: {{ $alert }} +annotations: + summary: CNPG Cluster instances in the same zone. + description: |- + 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 +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 + namespace: {{ .namespace }} + cnpg_cluster: {{ .cluster }} +{{- end -}} diff --git a/charts/vshnpostgresql/templates/NOTES.txt b/charts/vshnpostgresql/templates/NOTES.txt new file mode 100644 index 0000000..5e96a74 --- /dev/null +++ b/charts/vshnpostgresql/templates/NOTES.txt @@ -0,0 +1,102 @@ +{{- if .Values.pooler -}} + {{ fail ".Values.pooler has been deprecated. Use .Values.poolers instead." }} +{{- end -}} + +{{- if gt (omit .Values.cluster.postgresql "parameters" "synchronous" "pg_hba" "pg_ident" "syncReplicaElectionConstraint" "shared_preload_libraries" "ldap" "promotionTimeout" "enableAlterSystem" | keys | len) 0 -}} + {{ fail ".Values.cluster.postgresql has been deprecated. Use .Values.cluster.postgresql.parameters instead." }} +{{- end -}} + + +{{ 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 -}} +{{- if eq (len .Values.backups.scheduledBackups) 0 }} + {{- $scheduledBackups = "None" -}} +{{- end -}} + +{{- $mode := .Values.mode -}} +{{- $source := "" -}} +{{- if eq .Values.mode "recovery" }} +{{- $mode = printf "%s (%s)" .Values.mode .Values.recovery.method -}} + {{- if eq .Values.recovery.method "pg_basebackup" }} + {{- $source = printf "postgresql://%s@%s:%.0f/%s" .Values.recovery.pgBaseBackup.source.username .Values.recovery.pgBaseBackup.source.host .Values.recovery.pgBaseBackup.source.port .Values.recovery.pgBaseBackup.source.database -}} + {{- end -}} +{{- end -}} + +{{- $image := (include "cluster.image" .) | fromYaml -}} +{{- if $image.imageCatalogRef -}} + {{- $image = printf "%s: %s(%s)" $image.imageCatalogRef.kind $image.imageCatalogRef.name (include "cluster.postgresqlMajor" .) -}} +{{- else if $image.imageName -}} + {{- $image = $image.imageName -}} +{{- end }} + +╭───────────────────┬──────────────────────────────────────────────────────────╮ +│ Configuration │ Value │ +┝━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥ +│ Cluster mode │ {{ printf "%-56s" $mode }} │ +│ Type │ {{ printf "%-56s" .Values.type }} │ +│ Image │ {{ include "cluster.color-info" (printf "%-56s" $image) }} │ +{{- if eq .Values.mode "recovery" }} +│ Source │ {{ printf "%-56s" $source }} │ +{{- end }} +│ Instances │ {{ include (printf "%s%s" "cluster.color-" $redundancyColor) (printf "%-56s" (toString .Values.cluster.instances)) }} │ +│ Backups │ {{ include (printf "%s%s" "cluster.color-" (ternary "ok" "error" .Values.backups.enabled)) (printf "%-56s" (ternary "Enabled" "Disabled" .Values.backups.enabled)) }} │ +{{- if .Values.backups.enabled }} +│ Backup Provider │ {{ printf "%-56s" (title .Values.backups.provider) }} │ +│ Scheduled Backups │ {{ printf "%-56s" $scheduledBackups }} │ +{{- end }} +│ Storage │ {{ printf "%-56s" .Values.cluster.storage.size }} │ +│ Storage Class │ {{ printf "%-56s" (default "Default" .Values.cluster.storage.storageClass) }} │ +│ PGBouncer │ {{ printf "%-56s" (ternary "Enabled" "Disabled" (gt (len .Values.poolers) 0)) }} │ +│ Monitoring │ {{ include (printf "%s%s" "cluster.color-" (ternary "ok" "error" .Values.cluster.monitoring.enabled)) (printf "%-56s" (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" }} +{{ 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/vshnpostgresql/templates/_backup.tpl b/charts/vshnpostgresql/templates/_backup.tpl new file mode 100644 index 0000000..ac9bdab --- /dev/null +++ b/charts/vshnpostgresql/templates/_backup.tpl @@ -0,0 +1,23 @@ +{{- define "cluster.backup" -}} +{{- if and .Values.backups.enabled (ne .Values.backups.method "plugin") }} +backup: + target: "prefer-standby" + retentionPolicy: {{ .Values.backups.retentionPolicy }} + barmanObjectStore: + wal: + compression: {{ .Values.backups.wal.compression }} + {{- if .Values.backups.wal.encryption }} + encryption: {{ .Values.backups.wal.encryption }} + {{- end }} + maxParallel: {{ .Values.backups.wal.maxParallel }} + data: + compression: {{ .Values.backups.data.compression }} + {{- if .Values.backups.data.encryption }} + encryption: {{ .Values.backups.data.encryption }} + {{- end }} + jobs: {{ .Values.backups.data.jobs }} + + {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.backups "secretPrefix" "backup" }} + {{- include "cluster.barmanObjectStoreConfig" $d | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/vshnpostgresql/templates/_barman_object_store.tpl b/charts/vshnpostgresql/templates/_barman_object_store.tpl new file mode 100644 index 0000000..c00e412 --- /dev/null +++ b/charts/vshnpostgresql/templates/_barman_object_store.tpl @@ -0,0 +1,75 @@ +{{- define "cluster.barmanObjectStoreConfig" -}} + +{{- if .scope.endpointURL }} + endpointURL: {{ .scope.endpointURL | quote }} +{{- end }} + +{{- if or (.scope.endpointCA.create) (.scope.endpointCA.name) }} + endpointCA: + name: {{.scope.endpointCA.name }} + key: {{ .scope.endpointCA.key }} +{{- 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 }} + {{- $secretName := coalesce .scope.secret.name (printf "%s-%s-s3-creds" .chartFullname .secretPrefix) }} + s3Credentials: + {{- if .scope.s3.inheritFromIAMRole }} + inheritFromIAMRole: true + {{- else }} + accessKeyId: + name: {{ $secretName }} + key: ACCESS_KEY_ID + secretAccessKey: + name: {{ $secretName }} + key: ACCESS_SECRET_KEY + {{- end }} +{{- 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: {{ $secretName }} + key: AZURE_CONNECTION_STRING + {{- else }} + storageAccount: + name: {{ $secretName }} + key: AZURE_STORAGE_ACCOUNT + {{- if .scope.azure.storageKey }} + storageKey: + name: {{ $secretName }} + key: AZURE_STORAGE_KEY + {{- else }} + storageSasToken: + name: {{ $secretName }} + 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 }} + {{- $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/vshnpostgresql/templates/_bootstrap.tpl b/charts/vshnpostgresql/templates/_bootstrap.tpl new file mode 100644 index 0000000..95bedd2 --- /dev/null +++ b/charts/vshnpostgresql/templates/_bootstrap.tpl @@ -0,0 +1,112 @@ +{{- define "cluster.bootstrap" -}} +{{- if eq .Values.mode "standalone" }} +bootstrap: + initdb: + {{- with .Values.cluster.initdb }} + {{- with (omit . "postInitApplicationSQL" "owner" "import") }} + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- end }} + {{- if .Values.cluster.initdb.owner }} + owner: {{ tpl .Values.cluster.initdb.owner . }} + {{- end }} + {{- if or (eq .Values.type "postgis") (eq .Values.type "timescaledb") (not (empty .Values.cluster.initdb.postInitApplicationSQL)) }} + 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 -}} + {{- end }} +{{- else if eq .Values.mode "recovery" -}} +bootstrap: +{{- if eq .Values.recovery.method "pg_basebackup" }} + pg_basebackup: + source: pgBaseBackupSource + {{ with .Values.recovery.pgBaseBackup.database }} + database: {{ . }} + {{- end }} + {{ with .Values.recovery.pgBaseBackup.owner }} + owner: {{ . }} + {{- end }} + {{ with .Values.recovery.pgBaseBackup.secret }} + secret: + {{- toYaml . | nindent 6 }} + {{- end }} + +externalClusters: + {{- include "cluster.externalSourceCluster" (list "pgBaseBackupSource" .Values.recovery.pgBaseBackup.source) | nindent 2 }} + +{{- else if eq .Values.recovery.method "import" }} + initdb: + {{- with .Values.cluster.initdb }} + {{- with (omit . "owner" "import") }} + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- end }} + {{- if .Values.cluster.initdb.owner }} + owner: {{ tpl .Values.cluster.initdb.owner . }} + {{- end }} + import: + source: + externalCluster: importSource + type: {{ .Values.recovery.import.type }} + databases: {{ .Values.recovery.import.databases | toJson }} + {{ with .Values.recovery.import.roles }} + roles: {{ . | toJson }} + {{- end }} + {{ with .Values.recovery.import.postImportApplicationSQL }} + postImportApplicationSQL: + {{- . | toYaml | nindent 6 }} + {{- end }} + schemaOnly: {{ .Values.recovery.import.schemaOnly }} + {{ with .Values.recovery.import.pgDumpExtraOptions }} + pgDumpExtraOptions: + {{- . | toYaml | nindent 6 }} + {{- end }} + {{ with .Values.recovery.import.pgRestoreExtraOptions }} + pgRestoreExtraOptions: + {{- . | toYaml | nindent 6 }} + {{- end }} + +externalClusters: + {{- include "cluster.externalSourceCluster" (list "importSource" .Values.recovery.import.source) | nindent 2 }} + +{{- else }} + recovery: + {{- with .Values.recovery.pitrTarget.time }} + recoveryTarget: + targetTime: {{ . }} + {{- end }} + {{ with .Values.recovery.database }} + database: {{ . }} + {{- end }} + {{ with .Values.recovery.owner }} + owner: {{ . }} + {{- end }} + {{- if eq .Values.recovery.method "backup" }} + backup: + name: {{ .Values.recovery.backupName }} + {{- else if eq .Values.recovery.method "object_store" }} + source: objectStoreRecoveryCluster + +externalClusters: + - name: objectStoreRecoveryCluster + barmanObjectStore: + serverName: {{ .Values.recovery.clusterName }} + {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.recovery "secretPrefix" "recovery" -}} + {{- include "cluster.barmanObjectStoreConfig" $d | nindent 4 }} + {{- end }} +{{- end }} +{{- else }} + {{ fail "Invalid cluster mode!" }} +{{- end }} +{{- end }} diff --git a/charts/vshnpostgresql/templates/_colorize.tpl b/charts/vshnpostgresql/templates/_colorize.tpl new file mode 100644 index 0000000..35b2ba1 --- /dev/null +++ b/charts/vshnpostgresql/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/vshnpostgresql/templates/_external_source_cluster.tpl b/charts/vshnpostgresql/templates/_external_source_cluster.tpl new file mode 100644 index 0000000..6d21e20 --- /dev/null +++ b/charts/vshnpostgresql/templates/_external_source_cluster.tpl @@ -0,0 +1,33 @@ +{{- define "cluster.externalSourceCluster" -}} +{{- $name := first . -}} +{{- $config := last . -}} +- name: {{ first . }} + connectionParameters: + host: {{ $config.host | quote }} + port: {{ $config.port | quote }} + user: {{ $config.username | quote }} + {{- with $config.database }} + dbname: {{ . | quote }} + {{- end }} + sslmode: {{ $config.sslMode | quote }} + {{- if $config.passwordSecret.name }} + password: + name: {{ $config.passwordSecret.name }} + key: {{ $config.passwordSecret.key }} + {{- end }} + {{- if $config.sslKeySecret.name }} + sslKey: + name: {{ $config.sslKeySecret.name }} + key: {{ $config.sslKeySecret.key }} + {{- end }} + {{- if $config.sslCertSecret.name }} + sslCert: + name: {{ $config.sslCertSecret.name }} + key: {{ $config.sslCertSecret.key }} + {{- end }} + {{- if $config.sslRootCertSecret.name }} + sslRootCert: + name: {{ $config.sslRootCertSecret.name }} + key: {{ $config.sslRootCertSecret.key }} + {{- end }} +{{- end }} diff --git a/charts/vshnpostgresql/templates/_helpers.tpl b/charts/vshnpostgresql/templates/_helpers.tpl new file mode 100644 index 0000000..563901f --- /dev/null +++ b/charts/vshnpostgresql/templates/_helpers.tpl @@ -0,0 +1,147 @@ +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "cluster.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* +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 }} +app.kubernetes.io/part-of: cloudnative-pg +{{- end }} + +{{/* +Whether we need to use TimescaleDB defaults +*/}} +{{- define "cluster.useTimescaleDBDefaults" -}} +{{ and (eq .Values.type "timescaledb") .Values.imageCatalog.create (empty .Values.cluster.imageCatalogRef.name) (empty .Values.imageCatalog.images) (empty .Values.cluster.imageName) }} +{{- end -}} + +{{/* +Get the PostgreSQL major version from .Values.version.postgresql +*/}} +{{- define "cluster.postgresqlMajor" -}} +{{ index (regexSplit "\\." (toString .Values.version.postgresql) 2) 0 }} +{{- 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" -}} + {{- printf "ghcr.io/cloudnative-pg/postgresql:%s" .Values.version.postgresql -}} + {{- else if eq .Values.type "postgis" -}} + {{- printf "ghcr.io/cloudnative-pg/postgis:%s-%s" .Values.version.postgresql .Values.version.postgis -}} + {{- else -}} + {{ fail "Invalid cluster type!" }} + {{- end }} +{{- end -}} + +{{/* +Cluster Image +If imageCatalogRef defined, use it, otherwise calculate ordinary imageName. +*/}} +{{- define "cluster.image" }} +{{- if .Values.cluster.imageCatalogRef.name }} +imageCatalogRef: + apiGroup: postgresql.cnpg.io + {{- toYaml .Values.cluster.imageCatalogRef | nindent 2 }} + major: {{ include "cluster.postgresqlMajor" . }} +{{- else if and .Values.imageCatalog.create (not (empty .Values.imageCatalog.images )) }} +imageCatalogRef: + apiGroup: postgresql.cnpg.io + kind: ImageCatalog + name: {{ include "cluster.fullname" . }} + major: {{ include "cluster.postgresqlMajor" . }} +{{- else if eq (include "cluster.useTimescaleDBDefaults" .) "true" -}} +imageCatalogRef: + apiGroup: postgresql.cnpg.io + kind: ImageCatalog + name: {{ include "cluster.fullname" . }}-timescaledb-ha + major: {{ include "cluster.postgresqlMajor" . }} +{{- else }} +imageName: {{ include "cluster.imageName" . }} +{{- end }} +{{- end }} + +{{/* +Postgres UID +*/}} +{{- define "cluster.postgresUID" -}} + {{- if ge (int .Values.cluster.postgresUID) 0 -}} + {{- .Values.cluster.postgresUID }} + {{- else if and (eq (include "cluster.useTimescaleDBDefaults" .) "true") (eq .Values.type "timescaledb") -}} + {{- 1000 -}} + {{- else -}} + {{- 26 -}} + {{- end -}} +{{- end -}} + +{{/* +Postgres GID +*/}} +{{- define "cluster.postgresGID" -}} + {{- if ge (int .Values.cluster.postgresGID) 0 -}} + {{- .Values.cluster.postgresGID }} + {{- else if and (eq (include "cluster.useTimescaleDBDefaults" .) "true") (eq .Values.type "timescaledb") -}} + {{- 1000 -}} + {{- else -}} + {{- 26 -}} + {{- end -}} +{{- end -}} + diff --git a/charts/vshnpostgresql/templates/backup-azure-creds.yaml b/charts/vshnpostgresql/templates/backup-azure-creds.yaml new file mode 100644 index 0000000..7d384ea --- /dev/null +++ b/charts/vshnpostgresql/templates/backup-azure-creds.yaml @@ -0,0 +1,12 @@ +{{- if and .Values.backups.enabled (eq .Values.backups.provider "azure") .Values.backups.secret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ default (printf "%s-backup-azure-creds" (include "cluster.fullname" .)) .Values.backups.secret.name }} + namespace: {{ include "cluster.namespace" . }} +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 }} diff --git a/charts/vshnpostgresql/templates/backup-google-creds.yaml b/charts/vshnpostgresql/templates/backup-google-creds.yaml new file mode 100644 index 0000000..1b1f52f --- /dev/null +++ b/charts/vshnpostgresql/templates/backup-google-creds.yaml @@ -0,0 +1,9 @@ +{{- if and .Values.backups.enabled (eq .Values.backups.provider "google") .Values.backups.secret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ default (printf "%s-backup-google-creds" (include "cluster.fullname" .)) .Values.backups.secret.name }} + namespace: {{ include "cluster.namespace" . }} +data: + APPLICATION_CREDENTIALS: {{ .Values.backups.google.applicationCredentials | b64enc | quote }} +{{- end }} diff --git a/charts/vshnpostgresql/templates/backup-s3-creds.yaml b/charts/vshnpostgresql/templates/backup-s3-creds.yaml new file mode 100644 index 0000000..bd7cbfe --- /dev/null +++ b/charts/vshnpostgresql/templates/backup-s3-creds.yaml @@ -0,0 +1,10 @@ +{{- if and .Values.backups.enabled (ne .Values.backups.method "plugin") (eq .Values.backups.provider "s3") (not .Values.backups.s3.inheritFromIAMRole) .Values.backups.secret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ default (printf "%s-backup-s3-creds" (include "cluster.fullname" .)) .Values.backups.secret.name }} + namespace: {{ include "cluster.namespace" . }} +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/vshnpostgresql/templates/barman-cloud-objectstore.yaml b/charts/vshnpostgresql/templates/barman-cloud-objectstore.yaml new file mode 100644 index 0000000..f80f0d6 --- /dev/null +++ b/charts/vshnpostgresql/templates/barman-cloud-objectstore.yaml @@ -0,0 +1,35 @@ +{{ if and .Values.backups.enabled (eq .Values.backups.method "plugin") .Values.backups.plugin.createObjectStore }} +apiVersion: barmancloud.cnpg.io/v1 +kind: ObjectStore +metadata: + name: {{ printf "%s-objectstore" .Release.Name }} + namespace: {{ include "cluster.namespace" . }} + labels: {{ include "cluster.labels" . | nindent 4 }} +spec: + configuration: + {{- if .Values.backups.endpointURL }} + endpointURL: {{ .Values.backups.endpointURL | quote }} + {{- end }} + destinationPath: "s3://{{ required "You need to specify S3 bucket." .Values.backups.s3.bucket }}" + s3Credentials: + accessKeyId: + name: {{ printf "%s-barman-plugin-s3-creds" .Release.Name }} + key: ACCESS_KEY_ID + secretAccessKey: + name: {{ printf "%s-barman-plugin-s3-creds" .Release.Name }} + key: ACCESS_SECRET_KEY + wal: + {{- if .Values.backups.wal.compression }} + compression: {{ .Values.backups.wal.compression }} + {{- end }} + {{- if .Values.backups.wal.encryption }} + encryption: {{ .Values.backups.wal.encryption }} + {{- end }} + data: + {{- if .Values.backups.data.compression }} + compression: {{ .Values.backups.data.compression }} + {{- end }} + {{- if .Values.backups.data.encryption }} + encryption: {{ .Values.backups.data.encryption }} + {{- end }} +{{ end }} diff --git a/charts/vshnpostgresql/templates/barman-plugin-s3-creds.yaml b/charts/vshnpostgresql/templates/barman-plugin-s3-creds.yaml new file mode 100644 index 0000000..99d663a --- /dev/null +++ b/charts/vshnpostgresql/templates/barman-plugin-s3-creds.yaml @@ -0,0 +1,10 @@ +{{- if and .Values.backups.enabled (eq .Values.backups.method "plugin") .Values.backups.plugin.createObjectStore }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-barman-plugin-s3-creds" .Release.Name }} + namespace: {{ include "cluster.namespace" . }} +data: + ACCESS_KEY_ID: {{ required ".Values.backups.s3.accessKey is required." .Values.backups.s3.accessKey | b64enc | quote }} + ACCESS_SECRET_KEY: {{ required ".Values.backups.s3.secretKey is required." .Values.backups.s3.secretKey | b64enc | quote }} +{{- end }} diff --git a/charts/vshnpostgresql/templates/cluster.yaml b/charts/vshnpostgresql/templates/cluster.yaml new file mode 100644 index 0000000..7ea0a33 --- /dev/null +++ b/charts/vshnpostgresql/templates/cluster.yaml @@ -0,0 +1,152 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: {{ include "cluster.fullname" . }} + namespace: {{ include "cluster.namespace" . }} + {{- 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 }} + {{- include "cluster.image" . | nindent 2 }} + imagePullPolicy: {{ .Values.cluster.imagePullPolicy }} + {{- with .Values.cluster.imagePullSecrets }} + imagePullSecrets: + {{- . | toYaml | nindent 4 }} + {{- end }} + postgresUID: {{ include "cluster.postgresUID" . }} + postgresGID: {{ include "cluster.postgresGID" . }} + storage: + size: {{ .Values.cluster.storage.size }} + {{- if not (empty .Values.cluster.storage.storageClass) }} + storageClass: {{ .Values.cluster.storage.storageClass }} + {{- end }} + {{- if .Values.cluster.walStorage.enabled }} + walStorage: + size: {{ .Values.cluster.walStorage.size }} + {{- if not (empty .Values.cluster.walStorage.storageClass) }} + storageClass: {{ .Values.cluster.walStorage.storageClass }} + {{- end }} + {{- end }} + {{- with .Values.cluster.resources }} + resources: + {{- toYaml . | nindent 4 }} + {{ end }} + {{- with .Values.cluster.affinity }} + affinity: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.cluster.env }} + env: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.cluster.envFrom }} + envFrom: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if .Values.cluster.priorityClassName }} + priorityClassName: {{ .Values.cluster.priorityClassName }} + {{- end }} + + 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 }} + {{- with .Values.cluster.superuserSecret }} + superuserSecret: + name: {{ . }} + {{ end }} + enablePDB: {{ .Values.cluster.enablePDB }} + postgresql: + {{- if or (eq .Values.type "timescaledb") (not (empty .Values.cluster.postgresql.shared_preload_libraries)) }} + shared_preload_libraries: + {{- if eq .Values.type "timescaledb" }} + - timescaledb + {{- end }} + {{- with .Values.cluster.postgresql.shared_preload_libraries }} + {{- toYaml . | nindent 6 }} + {{- end }} + {{- end }} + {{- with .Values.cluster.postgresql.pg_hba }} + pg_hba: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.cluster.postgresql.pg_ident }} + pg_ident: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.cluster.postgresql.ldap }} + ldap: + {{- toYaml . | nindent 6 }} + {{- end}} + {{- with .Values.cluster.postgresql.synchronous }} + synchronous: + {{- toYaml . | nindent 6 }} + {{ end }} + {{- with .Values.cluster.postgresql.parameters }} + parameters: + {{- toYaml . | nindent 6 }} + {{- end }} + + {{- if not (and (empty .Values.cluster.roles) (empty .Values.cluster.services)) }} + managed: + {{- with .Values.cluster.services }} + services: + {{- toYaml . | nindent 6 }} + {{ end }} + {{- with .Values.cluster.roles }} + roles: + {{- toYaml . | nindent 6 }} + {{ end }} + {{- end }} + + {{- with .Values.cluster.serviceAccountTemplate }} + serviceAccountTemplate: + {{- toYaml . | nindent 4 }} + {{- end }} + + monitoring: + enablePodMonitor: {{ and .Values.cluster.monitoring.enabled .Values.cluster.monitoring.podMonitor.enabled }} + disableDefaultQueries: {{ .Values.cluster.monitoring.disableDefaultQueries }} + {{- if not (empty .Values.cluster.monitoring.customQueries) }} + customQueriesConfigMap: + - name: {{ include "cluster.fullname" . }}-monitoring + key: custom-queries + {{- end }} + {{- if not (empty .Values.cluster.monitoring.customQueriesSecret) }} + {{- with .Values.cluster.monitoring.customQueriesSecret }} + customQueriesSecret: + {{- toYaml . | nindent 6 }} + {{ end }} + {{- end }} + {{- if not (empty .Values.cluster.monitoring.podMonitor.relabelings) }} + {{- with .Values.cluster.monitoring.podMonitor.relabelings }} + podMonitorRelabelings: + {{- toYaml . | nindent 6 }} + {{ end }} + {{- end }} + {{- if not (empty .Values.cluster.monitoring.podMonitor.metricRelabelings) }} + {{- with .Values.cluster.monitoring.podMonitor.metricRelabelings }} + podMonitorMetricRelabelings: + {{- toYaml . | nindent 6 }} + {{ end }} + {{- end }} + {{- if and .Values.backups.enabled (eq .Values.backups.method "plugin") }} + plugins: + - name: barman-cloud.cloudnative-pg.io + isWALArchiver: true + parameters: + barmanObjectName: {{ printf "%s-objectstore" .Release.Name }} + {{- end }} + {{ include "cluster.bootstrap" . | nindent 2 }} + {{ include "cluster.backup" . | nindent 2 }} diff --git a/charts/vshnpostgresql/templates/image-catalog-timescaledb-ha.yaml b/charts/vshnpostgresql/templates/image-catalog-timescaledb-ha.yaml new file mode 100644 index 0000000..1a4422d --- /dev/null +++ b/charts/vshnpostgresql/templates/image-catalog-timescaledb-ha.yaml @@ -0,0 +1,19 @@ +{{- if eq (include "cluster.useTimescaleDBDefaults" .) "true" -}} +apiVersion: postgresql.cnpg.io/v1 +kind: ImageCatalog +metadata: + name: {{ include "cluster.fullname" . }}-timescaledb-ha + namespace: {{ include "cluster.namespace" . }} +spec: + images: + - major: 12 + image: timescale/timescaledb-ha:pg12-ts{{ .Values.version.timescaledb }} + - major: 13 + image: timescale/timescaledb-ha:pg13-ts{{ .Values.version.timescaledb }} + - major: 14 + image: timescale/timescaledb-ha:pg14-ts{{ .Values.version.timescaledb }} + - major: 15 + image: timescale/timescaledb-ha:pg15-ts{{ .Values.version.timescaledb }} + - major: 16 + image: timescale/timescaledb-ha:pg16-ts{{ .Values.version.timescaledb }} +{{ end }} diff --git a/charts/vshnpostgresql/templates/image-catalog.yaml b/charts/vshnpostgresql/templates/image-catalog.yaml new file mode 100644 index 0000000..e67e76f --- /dev/null +++ b/charts/vshnpostgresql/templates/image-catalog.yaml @@ -0,0 +1,13 @@ +{{ if and .Values.imageCatalog.create (not (empty .Values.imageCatalog.images )) }} +apiVersion: postgresql.cnpg.io/v1 +kind: ImageCatalog +metadata: + name: {{ include "cluster.fullname" . }} + namespace: {{ include "cluster.namespace" . }} +spec: + images: + {{- range $image := .Values.imageCatalog.images }} + - image: {{ $image.image }} + major: {{ $image.major }} + {{- end }} +{{- end }} diff --git a/charts/vshnpostgresql/templates/pooler.yaml b/charts/vshnpostgresql/templates/pooler.yaml new file mode 100644 index 0000000..f97e8d9 --- /dev/null +++ b/charts/vshnpostgresql/templates/pooler.yaml @@ -0,0 +1,49 @@ +{{- range .Values.poolers }} +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Pooler +metadata: + name: {{ include "cluster.fullname" $ }}-pooler-{{ .name }} + namespace: {{ include "cluster.namespace" $ }} +spec: + cluster: + name: {{ include "cluster.fullname" $ }} + instances: {{ .instances }} + type: {{ default "rw" .type }} + pgbouncer: + poolMode: {{ default "session" .poolMode }} + {{- with .authQuerySecret }} + authQuerySecret: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .authQuery }} + authQuery: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .parameters }} + parameters: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .pg_hba }} + pg_hba: + {{- toYaml . | nindent 6 }} + {{- end }} + {{ with .monitoring }} + monitoring: + {{- if not (empty .podMonitor) }} + enablePodMonitor: {{ and .enabled .podMonitor.enabled }} + {{- with .podMonitor.relabelings }} + podMonitorRelabelings: + {{- toYaml . | nindent 6 }} + {{ end }} + {{- with .podMonitor.metricRelabelings }} + podMonitorMetricRelabelings: + {{- toYaml . | nindent 6 }} + {{ end }} + {{- end }} + {{- end }} + {{- with .template }} + template: + {{- . | toYaml | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/vshnpostgresql/templates/prometheus-rule.yaml b/charts/vshnpostgresql/templates/prometheus-rule.yaml new file mode 100644 index 0000000..360b479 --- /dev/null +++ b/charts/vshnpostgresql/templates/prometheus-rule.yaml @@ -0,0 +1,30 @@ +{{- if and .Values.cluster.monitoring.enabled .Values.cluster.monitoring.prometheusRule.enabled -}} +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 + namespace: {{ include "cluster.namespace" . }} +spec: + groups: + - name: cloudnative-pg/{{ include "cluster.fullname" . }} + rules: + {{- $dict := dict "excludeRules" .Values.cluster.monitoring.prometheusRule.excludeRules -}} + {{- $_ := set $dict "value" "{{ $value }}" -}} + {{- $_ := set $dict "namespace" .Release.Namespace -}} + {{- $_ := 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 := tpl ($.Files.Get $path) $dict | nindent 10 | trim -}} + {{- with $tpl }} + - {{ $tpl }} + {{- end -}} + {{- end -}} +{{ end }} diff --git a/charts/vshnpostgresql/templates/recovery-azure-creds.yaml b/charts/vshnpostgresql/templates/recovery-azure-creds.yaml new file mode 100644 index 0000000..fc4f676 --- /dev/null +++ b/charts/vshnpostgresql/templates/recovery-azure-creds.yaml @@ -0,0 +1,12 @@ +{{- 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: {{ default (printf "%s-recovery-azure-creds" (include "cluster.fullname" .)) .Values.recovery.secret.name }} + namespace: {{ include "cluster.namespace" . }} +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 }} diff --git a/charts/vshnpostgresql/templates/recovery-google-creds.yaml b/charts/vshnpostgresql/templates/recovery-google-creds.yaml new file mode 100644 index 0000000..013e9d7 --- /dev/null +++ b/charts/vshnpostgresql/templates/recovery-google-creds.yaml @@ -0,0 +1,9 @@ +{{- 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 }} + namespace: {{ include "cluster.namespace" . }} +data: + APPLICATION_CREDENTIALS: {{ .Values.recovery.google.applicationCredentials | b64enc | quote }} +{{- end }} diff --git a/charts/vshnpostgresql/templates/recovery-pg_basebackup-password.yaml b/charts/vshnpostgresql/templates/recovery-pg_basebackup-password.yaml new file mode 100644 index 0000000..b6f46a2 --- /dev/null +++ b/charts/vshnpostgresql/templates/recovery-pg_basebackup-password.yaml @@ -0,0 +1,9 @@ +{{- if and (eq .Values.mode "recovery") (eq .Values.recovery.method "pg_basebackup") .Values.recovery.pgBaseBackup.source.passwordSecret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ default (printf "%s-pg-basebackup-password" (include "cluster.fullname" .)) .Values.recovery.pgBaseBackup.source.passwordSecret.name }} + namespace: {{ include "cluster.namespace" . }} +data: + {{ .Values.recovery.pgBaseBackup.source.passwordSecret.key }}: {{ required ".Values.recovery.pgBaseBackup.source.passwordSecret.value required when creating a password secret." .Values.recovery.pgBaseBackup.source.passwordSecret.value | b64enc | quote }} +{{- end }} diff --git a/charts/vshnpostgresql/templates/recovery-s3-creds.yaml b/charts/vshnpostgresql/templates/recovery-s3-creds.yaml new file mode 100644 index 0000000..d70ed21 --- /dev/null +++ b/charts/vshnpostgresql/templates/recovery-s3-creds.yaml @@ -0,0 +1,10 @@ +{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "s3") (not .Values.recovery.s3.inheritFromIAMRole) .Values.recovery.secret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ default (printf "%s-recovery-s3-creds" (include "cluster.fullname" .)) .Values.recovery.secret.name }} + namespace: {{ include "cluster.namespace" . }} +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 }} diff --git a/charts/vshnpostgresql/templates/scheduled-backups.yaml b/charts/vshnpostgresql/templates/scheduled-backups.yaml new file mode 100644 index 0000000..e2af3f6 --- /dev/null +++ b/charts/vshnpostgresql/templates/scheduled-backups.yaml @@ -0,0 +1,33 @@ +{{ if .Values.backups.enabled }} +{{ $context := . -}} +{{ range .Values.backups.scheduledBackups -}} +--- +apiVersion: postgresql.cnpg.io/v1 +kind: ScheduledBackup +metadata: + name: {{ include "cluster.fullname" $context }}-{{ .name }} + namespace: {{ include "cluster.namespace" $ }} + labels: {{ include "cluster.labels" $context | nindent 4 }} +spec: + {{- if .immediate }} + immediate: {{ .immediate }} + {{- else }} + immediate: true + {{- end }} + schedule: {{ .schedule | quote }} + {{- if eq $context.Values.backups.method "plugin" }} + method: plugin + cluster: + name: {{ include "cluster.fullname" $context }} + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + parameters: + barmanObjectName: {{ printf "%s-objectstore" $context.Release.Name }} + {{- else }} + method: {{ $context.Values.backups.method }} + backupOwnerReference: {{ .backupOwnerReference }} + cluster: + name: {{ include "cluster.fullname" $context }} + {{- end }} +{{ end -}} +{{ end }} diff --git a/charts/vshnpostgresql/templates/tests/ping.yaml b/charts/vshnpostgresql/templates/tests/ping.yaml new file mode 100644 index 0000000..aa911bd --- /dev/null +++ b/charts/vshnpostgresql/templates/tests/ping.yaml @@ -0,0 +1,44 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "cluster.fullname" . }}-ping-test + namespace: {{ include "cluster.namespace" . }} + 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 + - name: PGDBNAME + valueFrom: + secretKeyRef: + name: {{ include "cluster.fullname" . }}-app + key: dbname + optional: true + args: + - "-c" + - >- + apk add postgresql-client && + psql "postgresql://$PGUSER:$PGPASS@{{ include "cluster.fullname" . }}-rw.{{ include "cluster.namespace" . }}.svc.cluster.local:5432/${PGDBNAME:-$PGUSER}" -c 'SELECT 1' diff --git a/charts/vshnpostgresql/templates/user-metrics.yaml b/charts/vshnpostgresql/templates/user-metrics.yaml new file mode 100644 index 0000000..2ec310e --- /dev/null +++ b/charts/vshnpostgresql/templates/user-metrics.yaml @@ -0,0 +1,24 @@ +{{- if not (empty .Values.cluster.monitoring.customQueries) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "cluster.fullname" . }}-monitoring + namespace: {{ include "cluster.namespace" . }} + labels: + cnpg.io/reload: "" + {{- include "cluster.labels" . | nindent 4 }} +data: + custom-queries: | + {{- range .Values.cluster.monitoring.customQueries }} + {{ .name }}: + query: {{ .query | quote }} + {{- with .target_databases }} + target_databases: {{ . | toJson }} + {{- end }} + {{- with .predicate_query }} + predicate_query: {{ tpl . $ | quote }} + {{- end }} + metrics: + {{- .metrics | toYaml | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/vshnpostgresql/test/monitoring/01-monitoring_cluster-assert.yaml b/charts/vshnpostgresql/test/monitoring/01-monitoring_cluster-assert.yaml new file mode 100644 index 0000000..017ac59 --- /dev/null +++ b/charts/vshnpostgresql/test/monitoring/01-monitoring_cluster-assert.yaml @@ -0,0 +1,135 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: monitoring-cluster + labels: + foo: bar + annotations: + foo: bar +spec: + instances: 2 + storage: + size: 256Mi + storageClass: standard + monitoring: + disableDefaultQueries: true + customQueriesConfigMap: + - name: monitoring-cluster-monitoring + key: custom-queries + enablePodMonitor: true + podMonitorRelabelings: + - action: replace + replacement: test + targetLabel: environment + - action: replace + replacement: alpha + targetLabel: team + podMonitorMetricRelabelings: + - action: replace + sourceLabels: + - cluster + targetLabel: cnpg_cluster + - action: labeldrop + regex: cluster +--- +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: monitoring-cluster +spec: + selector: + matchLabels: + cnpg.io/cluster: monitoring-cluster + podMetricsEndpoints: + - bearerTokenSecret: + key: '' + name: '' + relabelings: + - targetLabel: environment + replacement: test + - targetLabel: team + replacement: alpha + metricRelabelings: + - action: replace + sourceLabels: + - cluster + targetLabel: cnpg_cluster + - action: labeldrop + regex: cluster +--- +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: monitoring-cluster-pooler-rw +spec: + selector: + matchLabels: + cnpg.io/poolerName: monitoring-cluster-pooler-rw + podMetricsEndpoints: + - bearerTokenSecret: + key: '' + name: '' + relabelings: + - targetLabel: type + replacement: rw + action: replace + - targetLabel: team + replacement: alpha + action: replace + metricRelabelings: + - action: replace + sourceLabels: + - cluster + targetLabel: cnpg_cluster + - action: labeldrop + regex: cluster +--- +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: monitoring-cluster-pooler-ro +spec: + selector: + matchLabels: + cnpg.io/poolerName: monitoring-cluster-pooler-ro + podMetricsEndpoints: + - bearerTokenSecret: + key: '' + name: '' + relabelings: + - targetLabel: type + replacement: ro + action: replace + - targetLabel: team + replacement: alpha + action: replace + metricRelabelings: + - action: replace + sourceLabels: + - cluster + targetLabel: cnpg_cluster + - action: labeldrop + regex: cluster +--- +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: monitoring-cluster-alert-rules +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: monitoring-cluster-monitoring +data: + custom-queries: | + 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;" + target_databases: ["*"] + predicate_query: "SELECT 'postgresql';" + metrics: + - datname: + description: Name of the database + usage: LABEL + - ratio: + description: Cache hit ratio + usage: GAUGE diff --git a/charts/vshnpostgresql/test/monitoring/01-monitoring_cluster.yaml b/charts/vshnpostgresql/test/monitoring/01-monitoring_cluster.yaml new file mode 100644 index 0000000..314fccf --- /dev/null +++ b/charts/vshnpostgresql/test/monitoring/01-monitoring_cluster.yaml @@ -0,0 +1,80 @@ +type: postgresql +mode: standalone +cluster: + instances: 2 + storage: + size: 256Mi + storageClass: standard + monitoring: + enabled: true + disableDefaultQueries: 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;" + target_databases: ["*"] + predicate_query: "SELECT '{{ .Values.type }}';" + metrics: + - datname: + usage: "LABEL" + description: "Name of the database" + - ratio: + usage: GAUGE + description: "Cache hit ratio" + podMonitor: + relabelings: + - targetLabel: environment + replacement: test + - targetLabel: team + replacement: alpha + metricRelabelings: + - action: replace + sourceLabels: + - cluster + targetLabel: cnpg_cluster + - action: labeldrop + regex: cluster + additionalLabels: + foo: bar + annotations: + foo: bar +backups: + enabled: false +poolers: + - name: rw + type: rw + instances: 1 + monitoring: + enabled: true + podMonitor: + enabled: true + relabelings: + - targetLabel: type + replacement: rw + - targetLabel: team + replacement: alpha + metricRelabelings: + - action: replace + sourceLabels: + - cluster + targetLabel: cnpg_cluster + - action: labeldrop + regex: cluster + - name: ro + type: ro + instances: 1 + monitoring: + enabled: true + podMonitor: + enabled: true + relabelings: + - targetLabel: type + replacement: ro + - targetLabel: team + replacement: alpha + metricRelabelings: + - action: replace + sourceLabels: + - cluster + targetLabel: cnpg_cluster + - action: labeldrop + regex: cluster diff --git a/charts/vshnpostgresql/test/monitoring/chainsaw-test.yaml b/charts/vshnpostgresql/test/monitoring/chainsaw-test.yaml new file mode 100644 index 0000000..ce647a4 --- /dev/null +++ b/charts/vshnpostgresql/test/monitoring/chainsaw-test.yaml @@ -0,0 +1,29 @@ +## +# This is a test that checks if PodMonitors, ConfigMaps and PrometheusRules are correctly provisioned when requested. +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: monitoring +spec: + timeouts: + apply: 1s + assert: 20s + cleanup: 30s + steps: + - name: Install the monitoring cluster + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-monitoring_cluster.yaml \ + --wait \ + monitoring ../../ + - assert: + file: ./01-monitoring_cluster-assert.yaml + - name: Cleanup + try: + - script: + content: | + helm uninstall --namespace $NAMESPACE monitoring diff --git a/charts/vshnpostgresql/test/pooler/01-pooler_cluster-assert.yaml b/charts/vshnpostgresql/test/pooler/01-pooler_cluster-assert.yaml new file mode 100644 index 0000000..db23167 --- /dev/null +++ b/charts/vshnpostgresql/test/pooler/01-pooler_cluster-assert.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pooler-cluster-pooler-rw +status: + readyReplicas: 2 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pooler-cluster-pooler-ro +status: + readyReplicas: 2 +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Pooler +metadata: + name: pooler-cluster-pooler-rw +spec: + cluster: + name: pooler-cluster + instances: 2 + pgbouncer: + poolMode: transaction + type: rw +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Pooler +metadata: + name: pooler-cluster-pooler-ro +spec: + cluster: + name: pooler-cluster + instances: 2 + pgbouncer: + poolMode: session + type: ro diff --git a/charts/vshnpostgresql/test/pooler/01-pooler_cluster.yaml b/charts/vshnpostgresql/test/pooler/01-pooler_cluster.yaml new file mode 100644 index 0000000..8fcbf65 --- /dev/null +++ b/charts/vshnpostgresql/test/pooler/01-pooler_cluster.yaml @@ -0,0 +1,17 @@ +type: postgresql +mode: standalone +cluster: + instances: 2 + storage: + size: 256Mi + storageClass: standard +backups: + enabled: false +poolers: + - name: rw + type: rw + instances: 2 + poolMode: transaction + - name: ro + type: ro + instances: 2 diff --git a/charts/vshnpostgresql/test/pooler/chainsaw-test.yaml b/charts/vshnpostgresql/test/pooler/chainsaw-test.yaml new file mode 100644 index 0000000..427b84e --- /dev/null +++ b/charts/vshnpostgresql/test/pooler/chainsaw-test.yaml @@ -0,0 +1,30 @@ +## +# This is a test that verifies that non-default configuration options are correctly propagated to the CNPG cluster. +# P.S. This test is not designed to have a good running configuration, it is designed to test the configuration propagation! +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: pooler +spec: + timeouts: + apply: 1s + assert: 20s + cleanup: 30s + steps: + - name: Install the non-default configuration cluster + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-pooler_cluster.yaml \ + --wait \ + pooler ../../ + - assert: + file: ./01-pooler_cluster-assert.yaml + - name: Cleanup + try: + - script: + content: | + helm uninstall --namespace $NAMESPACE pooler diff --git a/charts/vshnpostgresql/test/postgresql-cluster-configuration/01-non_default_configuration_cluster-assert.yaml b/charts/vshnpostgresql/test/postgresql-cluster-configuration/01-non_default_configuration_cluster-assert.yaml new file mode 100644 index 0000000..bc5cce7 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-cluster-configuration/01-non_default_configuration_cluster-assert.yaml @@ -0,0 +1,133 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: non-default-configuration-cluster + labels: + foo: bar + annotations: + foo: bar +spec: + imageName: ghcr.io/cloudnative-pg/crazycustomimage:99.99 + imagePullPolicy: Always + postgresUID: 1001 + postgresGID: 1002 + instances: 2 + postgresql: + ldap: + server: 'openldap.default.svc.cluster.local' + bindSearchAuth: + baseDN: 'ou=org,dc=example,dc=com' + bindDN: 'cn=admin,dc=example,dc=com' + bindPassword: + name: 'ldapBindPassword' + key: 'data' + searchAttribute: 'uid' + parameters: + max_connections: "42" + pg_hba: + - host all 1.2.3.4/32 trust + pg_ident: + - mymap /^(.*)@mydomain\.com$ \1 + shared_preload_libraries: + - pgaudit + synchronous: + method: any + number: 1 + bootstrap: + initdb: + database: mydb + owner: dante + secret: + name: mydb-secret + postInitApplicationSQL: + - CREATE TABLE mytable (id serial PRIMARY KEY, name VARCHAR(255)); + postInitTemplateSQL: + - CREATE TABLE mytable (id serial PRIMARY KEY, name VARCHAR(255)); + postInitSQL: + - CREATE TABLE mytable (id serial PRIMARY KEY, name VARCHAR(255)); + superuserSecret: + name: supersecret-secret + enableSuperuserAccess: true + enablePDB: false + certificates: + serverCASecret: ca-secret + serverTLSSecret: tls-secret + replicationTLSSecret: replication-tls-secret + clientCASecret: client-ca-secret + imagePullSecrets: + - name: image-pull-secret + storage: + size: 256Mi + storageClass: standard + walStorage: + size: 256Mi + storageClass: standard + affinity: + topologyKey: kubernetes.io/hostname + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - node1 + - node2 + env: + - name: MY_CUSTOM_FLAG + value: enabled + - name: MY_CUSTROM_ENV + valueFrom: + configMapKeyRef: + name: my-custom-env + key: env + optional: true + - name: MY_CUSTOM_SECRET_ENV + valueFrom: + secretKeyRef: + name: my-custom-secret + key: secret + optional: true + envFrom: + - configMapRef: + name: global-envs + optional: true + - secretRef: + name: db-credentials + optional: true + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 100m + memory: 256Mi + priorityClassName: mega-high + primaryUpdateStrategy: supervised + primaryUpdateMethod: restart + logLevel: warning + managed: + roles: + - name: dante + ensure: present + comment: Dante Alighieri + login: true + inRoles: + - pg_monitor + - pg_signal_backend + services: + additional: + - selectorType: rw + serviceTemplate: + metadata: + name: "test-lb" + labels: + test-label: "true" + annotations: + test-annotation: "true" + spec: + type: LoadBalancer + serviceAccountTemplate: + metadata: + annotations: + my-annotation: my-service-account diff --git a/charts/vshnpostgresql/test/postgresql-cluster-configuration/01-non_default_configuration_cluster.yaml b/charts/vshnpostgresql/test/postgresql-cluster-configuration/01-non_default_configuration_cluster.yaml new file mode 100644 index 0000000..90f1a68 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-cluster-configuration/01-non_default_configuration_cluster.yaml @@ -0,0 +1,132 @@ +type: postgresql +mode: standalone +cluster: + instances: 2 + imageName: ghcr.io/cloudnative-pg/crazycustomimage:99.99 + imagePullPolicy: Always + imagePullSecrets: + - name: "image-pull-secret" + storage: + size: 256Mi + storageClass: standard + walStorage: + enabled: true + size: 256Mi + storageClass: standard + postgresUID: 1001 + postgresGID: 1002 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 100m + memory: 256Mi + priorityClassName: mega-high + primaryUpdateMethod: restart + primaryUpdateStrategy: supervised + logLevel: warning + affinity: + topologyKey: kubernetes.io/hostname + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - node1 + - node2 + env: + - name: MY_CUSTOM_FLAG + value: enabled + - name: MY_CUSTROM_ENV + valueFrom: + configMapKeyRef: + name: my-custom-env + key: env + optional: true + - name: MY_CUSTOM_SECRET_ENV + valueFrom: + secretKeyRef: + name: my-custom-secret + key: secret + optional: true + envFrom: + - configMapRef: + name: global-envs + optional: true + - secretRef: + name: db-credentials + optional: true + certificates: + serverCASecret: ca-secret + serverTLSSecret: tls-secret + replicationTLSSecret: replication-tls-secret + clientCASecret: client-ca-secret + enableSuperuserAccess: true + superuserSecret: supersecret-secret + enablePDB: false + services: + additional: + - selectorType: rw + serviceTemplate: + metadata: + name: "test-lb" + labels: + test-label: "true" + annotations: + test-annotation: "true" + spec: + type: LoadBalancer + roles: + - name: dante + ensure: present + comment: Dante Alighieri + login: true + inRoles: + - pg_monitor + - pg_signal_backend + postgresql: + ldap: + server: 'openldap.default.svc.cluster.local' + bindSearchAuth: + baseDN: 'ou=org,dc=example,dc=com' + bindDN: 'cn=admin,dc=example,dc=com' + bindPassword: + name: 'ldapBindPassword' + key: 'data' + searchAttribute: 'uid' + parameters: + max_connections: "42" + pg_hba: + - host all 1.2.3.4/32 trust + pg_ident: + - mymap /^(.*)@mydomain\.com$ \1 + shared_preload_libraries: + - pgaudit + synchronous: + method: any + number: 1 + initdb: + database: mydb + owner: dante + secret: + name: mydb-secret + postInitApplicationSQL: + - CREATE TABLE mytable (id serial PRIMARY KEY, name VARCHAR(255)); + postInitTemplateSQL: + - CREATE TABLE mytable (id serial PRIMARY KEY, name VARCHAR(255)); + postInitSQL: + - CREATE TABLE mytable (id serial PRIMARY KEY, name VARCHAR(255)); + additionalLabels: + foo: bar + annotations: + foo: bar + serviceAccountTemplate: + metadata: + annotations: + my-annotation: my-service-account + +backups: + enabled: false diff --git a/charts/vshnpostgresql/test/postgresql-cluster-configuration/02-recovery_object_store_database_owner-assert.yaml b/charts/vshnpostgresql/test/postgresql-cluster-configuration/02-recovery_object_store_database_owner-assert.yaml new file mode 100644 index 0000000..90c023a --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-cluster-configuration/02-recovery_object_store_database_owner-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: recovery-object-store-database-owner-cluster +spec: + bootstrap: + recovery: + source: objectStoreRecoveryCluster + database: my-special-database + owner: me diff --git a/charts/vshnpostgresql/test/postgresql-cluster-configuration/02-recovery_object_store_database_owner.yaml b/charts/vshnpostgresql/test/postgresql-cluster-configuration/02-recovery_object_store_database_owner.yaml new file mode 100644 index 0000000..21349ed --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-cluster-configuration/02-recovery_object_store_database_owner.yaml @@ -0,0 +1,15 @@ +type: postgresql +mode: recovery + +recovery: + method: object_store + database: my-special-database + owner: me + s3: + bucket: "mybucket" + accessKey: "minio" + secretKey: "minio123" + region: "local" + +backups: + enabled: false diff --git a/charts/vshnpostgresql/test/postgresql-cluster-configuration/03-recovery_backup_database_owner-assert.yaml b/charts/vshnpostgresql/test/postgresql-cluster-configuration/03-recovery_backup_database_owner-assert.yaml new file mode 100644 index 0000000..67f6780 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-cluster-configuration/03-recovery_backup_database_owner-assert.yaml @@ -0,0 +1,11 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: recovery-backup-database-owner-cluster +spec: + bootstrap: + recovery: + backup: + name: post-init-backup + database: my-special-database + owner: me diff --git a/charts/vshnpostgresql/test/postgresql-cluster-configuration/03-recovery_backup_database_owner.yaml b/charts/vshnpostgresql/test/postgresql-cluster-configuration/03-recovery_backup_database_owner.yaml new file mode 100644 index 0000000..e131cb3 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-cluster-configuration/03-recovery_backup_database_owner.yaml @@ -0,0 +1,16 @@ +type: postgresql +mode: recovery + +recovery: + method: backup + backupName: "post-init-backup" + database: my-special-database + owner: me + s3: + bucket: "mybucket" + accessKey: "minio" + secretKey: "minio123" + region: "local" + +backups: + enabled: false diff --git a/charts/vshnpostgresql/test/postgresql-cluster-configuration/chainsaw-test.yaml b/charts/vshnpostgresql/test/postgresql-cluster-configuration/chainsaw-test.yaml new file mode 100644 index 0000000..9eeb457 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-cluster-configuration/chainsaw-test.yaml @@ -0,0 +1,56 @@ +## +# This is a test that verifies that non-default configuration options are correctly propagated to the CNPG cluster. +# P.S. This test is not designed to have a good running configuration, it is designed to test the configuration propagation! +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: postgresql-cluster-configuration +spec: + timeouts: + apply: 1s + assert: 5s + cleanup: 30s + steps: + - name: Install the non-default configuration cluster + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-non_default_configuration_cluster.yaml \ + --wait \ + non-default-configuration ../../ + - assert: + file: ./01-non_default_configuration_cluster-assert.yaml + - name: Install object-store recovery-cluster for specific database and owner + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./02-recovery_object_store_database_owner.yaml \ + --wait \ + recovery-object-store-database-owner ../../ + - assert: + file: ./02-recovery_object_store_database_owner-assert.yaml + - name: Install backup recovery-cluster for specific database and owner + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./03-recovery_backup_database_owner.yaml \ + --wait \ + recovery-backup-database-owner ../../ + - assert: + file: ./03-recovery_backup_database_owner-assert.yaml + - name: Cleanup + try: + - script: + content: | + helm uninstall --namespace $NAMESPACE non-default-configuration + helm uninstall --namespace $NAMESPACE recovery-object-store-database-owner + helm uninstall --namespace $NAMESPACE recovery-backup-database-owner diff --git a/charts/vshnpostgresql/test/postgresql-import/00-source-cluster-assert.yaml b/charts/vshnpostgresql/test/postgresql-import/00-source-cluster-assert.yaml new file mode 100644 index 0000000..90ea90f --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/00-source-cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: source-cluster +status: + readyInstances: 1 diff --git a/charts/vshnpostgresql/test/postgresql-import/00-source-cluster.yaml b/charts/vshnpostgresql/test/postgresql-import/00-source-cluster.yaml new file mode 100644 index 0000000..790e208 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/00-source-cluster.yaml @@ -0,0 +1,9 @@ +type: postgresql +mode: "standalone" +cluster: + instances: 1 + superuserSecret: source-cluster-superuser + storage: + size: 256Mi +backups: + enabled: false diff --git a/charts/vshnpostgresql/test/postgresql-import/00-source-superuser-password.yaml b/charts/vshnpostgresql/test/postgresql-import/00-source-superuser-password.yaml new file mode 100644 index 0000000..b88a808 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/00-source-superuser-password.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: source-cluster-superuser +type: Opaque +data: + username: "cG9zdGdyZXM=" + password: "cG9zdGdyZXM=" diff --git a/charts/vshnpostgresql/test/postgresql-import/01-data_write-assert.yaml b/charts/vshnpostgresql/test/postgresql-import/01-data_write-assert.yaml new file mode 100644 index 0000000..831f963 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/01-data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-import/01-data_write.yaml b/charts/vshnpostgresql/test/postgresql-import/01-data_write.yaml new file mode 100644 index 0000000..138fa8a --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/01-data_write.yaml @@ -0,0 +1,33 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_USER + valueFrom: + secretKeyRef: + name: source-cluster-superuser + key: username + - name: DB_PASS + valueFrom: + secretKeyRef: + name: source-cluster-superuser + key: password + - name: DB_URI + value: postgres://$(DB_USER):$(DB_PASS)@source-cluster-rw:5432 + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + set -e + apk --no-cache add postgresql-client + psql "$DB_URI" -c "CREATE DATABASE mygooddb;" + psql "$DB_URI/mygooddb" -c "CREATE TABLE mygoodtable (id serial PRIMARY KEY);" + psql "$DB_URI/mygooddb" -c "INSERT INTO mygoodtable VALUES (314159265);" + psql "$DB_URI/mygooddb" -c "CREATE TABLE mybadtable (id serial PRIMARY KEY);" diff --git a/charts/vshnpostgresql/test/postgresql-import/02-import-cluster-assert.yaml b/charts/vshnpostgresql/test/postgresql-import/02-import-cluster-assert.yaml new file mode 100644 index 0000000..a194edc --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/02-import-cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: import-cluster +status: + readyInstances: 2 diff --git a/charts/vshnpostgresql/test/postgresql-import/02-import-cluster.yaml b/charts/vshnpostgresql/test/postgresql-import/02-import-cluster.yaml new file mode 100644 index 0000000..b2de4cc --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/02-import-cluster.yaml @@ -0,0 +1,32 @@ +type: postgresql +mode: "recovery" +recovery: + method: "import" + import: + type: "microservice" + databases: [ "mygooddb" ] + pgDumpExtraOptions: + - --table=mygood* + source: + host: "source-cluster-rw" + username: "postgres" + passwordSecret: + name: source-cluster-superuser + key: password + sslMode: "require" + sslKeySecret: + name: source-cluster-replication + key: tls.key + sslCertSecret: + name: source-cluster-replication + key: tls.crt + +cluster: + instances: 2 + storage: + size: 256Mi + initdb: + database: mygooddb + +backups: + enabled: false diff --git a/charts/vshnpostgresql/test/postgresql-import/03-data_test-assert.yaml b/charts/vshnpostgresql/test/postgresql-import/03-data_test-assert.yaml new file mode 100644 index 0000000..04df941 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/03-data_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-import/03-data_test.yaml b/charts/vshnpostgresql/test/postgresql-import/03-data_test.yaml new file mode 100644 index 0000000..0c516d7 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/03-data_test.yaml @@ -0,0 +1,29 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: import-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + set -e + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + test "$(psql "${DB_URI}mygooddb" -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mygoodtable$$)' --csv -q 2>/dev/null)" = "t" + echo "mygoodtable exist" + test "$(psql "${DB_URI}mygooddb" -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mybadtable$$)' --csv -q 2>/dev/null)" = "f" + echo "mybadtable doesn't exist" + test "$(psql "${DB_URI}mygooddb" -t -c 'SELECT EXISTS (SELECT FROM mygoodtable WHERE id = 314159265)' --csv -q 2>/dev/null)" = "t" + echo "mygoodtable contains the desired value" diff --git a/charts/vshnpostgresql/test/postgresql-import/04-import-cluster-schema_only-assert.yaml b/charts/vshnpostgresql/test/postgresql-import/04-import-cluster-schema_only-assert.yaml new file mode 100644 index 0000000..f461b2b --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/04-import-cluster-schema_only-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: import-schemaonly-cluster +status: + readyInstances: 2 diff --git a/charts/vshnpostgresql/test/postgresql-import/04-import-cluster-schema_only.yaml b/charts/vshnpostgresql/test/postgresql-import/04-import-cluster-schema_only.yaml new file mode 100644 index 0000000..001e58b --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/04-import-cluster-schema_only.yaml @@ -0,0 +1,33 @@ +type: postgresql +mode: "recovery" +recovery: + method: "import" + import: + type: "microservice" + databases: [ "mygooddb" ] + pgRestoreExtraOptions: + - --table=mygoodtable + schemaOnly: true + source: + host: "source-cluster-rw" + username: "postgres" + passwordSecret: + name: source-cluster-superuser + key: password + sslMode: "require" + sslKeySecret: + name: source-cluster-replication + key: tls.key + sslCertSecret: + name: source-cluster-replication + key: tls.crt + +cluster: + instances: 2 + storage: + size: 256Mi + initdb: + database: mygooddb + +backups: + enabled: false diff --git a/charts/vshnpostgresql/test/postgresql-import/05-data_test-assert.yaml b/charts/vshnpostgresql/test/postgresql-import/05-data_test-assert.yaml new file mode 100644 index 0000000..b29534f --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/05-data_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-schemaonly +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-import/05-data_test.yaml b/charts/vshnpostgresql/test/postgresql-import/05-data_test.yaml new file mode 100644 index 0000000..0508468 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/05-data_test.yaml @@ -0,0 +1,29 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-schemaonly +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: import-schemaonly-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + set -e + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + test "$(psql "${DB_URI}mygooddb" -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mygoodtable$$)' --csv -q 2>/dev/null)" = "t" + echo "mygoodtable exist" + test "$(psql "${DB_URI}mygooddb" -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mybadtable$$)' --csv -q 2>/dev/null)" = "f" + echo "mybadtable doesn't exist" + test "$(psql "${DB_URI}mygooddb" -t -c 'SELECT COUNT(*) FROM mygoodtable' --csv -q 2>/dev/null)" = "0" + echo "mygoodtable is empty" diff --git a/charts/vshnpostgresql/test/postgresql-import/chainsaw-test.yaml b/charts/vshnpostgresql/test/postgresql-import/chainsaw-test.yaml new file mode 100644 index 0000000..f4b431f --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-import/chainsaw-test.yaml @@ -0,0 +1,97 @@ +## +# This test the CNPG PostgreSQL import capability. +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: postgresql-import +spec: + timeouts: + apply: 1s + assert: 2m + cleanup: 1m + steps: + - name: Install the external PostgreSQL cluster + try: + - apply: + file: ./00-source-superuser-password.yaml + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./00-source-cluster.yaml \ + --wait \ + source ../../ + - assert: + file: ./00-source-cluster-assert.yaml + - apply: + file: ./01-data_write.yaml + - assert: + file: ./01-data_write-assert.yaml + - name: Install the import cluster + timeouts: + assert: 5m + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./02-import-cluster.yaml \ + --wait \ + import ../../ + - assert: + file: ./02-import-cluster-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - name: Verify the data exists + try: + - apply: + file: ./03-data_test.yaml + - assert: + file: ./03-data_test-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + - podLogs: + selector: batch.kubernetes.io/job-name=data-test + - name: Install the schema-only import cluster + timeouts: + assert: 5m + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./04-import-cluster-schema_only.yaml \ + --wait \ + import-schemaonly ../../ + - assert: + file: ./04-import-cluster-schema_only-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - name: Verify only the schema exists + try: + - apply: + file: ./05-data_test.yaml + - assert: + file: ./05-data_test-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + - podLogs: + selector: batch.kubernetes.io/job-name=data-test-schemaonly + - name: Cleanup + try: + - script: + content: | + helm uninstall --namespace $NAMESPACE source + helm uninstall --namespace $NAMESPACE import + helm uninstall --namespace $NAMESPACE import-schemaonly diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/00-minio_cleanup-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/00-minio_cleanup-assert.yaml new file mode 100644 index 0000000..9c0f3eb --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/00-minio_cleanup-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/00-minio_cleanup.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/00-minio_cleanup.yaml new file mode 100644 index 0000000..97cfc73 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/00-minio_cleanup.yaml @@ -0,0 +1,16 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: minio-cleanup + image: minio/mc + command: ['sh', '-c'] + args: + - | + mc alias set myminio https://minio.minio.svc.cluster.local minio minio123 + mc rm --recursive --force myminio/mybucket/postgresql-minio-backup-restore diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/01-standalone_cluster-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/01-standalone_cluster-assert.yaml new file mode 100644 index 0000000..0663e78 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/01-standalone_cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: standalone-cluster +status: + readyInstances: 2 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/01-standalone_cluster.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/01-standalone_cluster.yaml new file mode 100644 index 0000000..7db3fe9 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/01-standalone_cluster.yaml @@ -0,0 +1,27 @@ +type: postgresql +mode: standalone + +cluster: + instances: 2 + storage: + size: 256Mi + +backups: + enabled: true + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/postgresql-minio-backup-restore/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/02-data_write-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/02-data_write-assert.yaml new file mode 100644 index 0000000..831f963 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/02-data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/02-data_write.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/02-data_write.yaml new file mode 100644 index 0000000..e674d8b --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/02-data_write.yaml @@ -0,0 +1,23 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: standalone-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + psql "$DB_URI" -c "CREATE TABLE mygoodtable (id serial PRIMARY KEY);" diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-backup.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-backup.yaml new file mode 100644 index 0000000..c3afd46 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-backup.yaml @@ -0,0 +1,8 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + method: barmanObjectStore + cluster: + name: standalone-cluster diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-backup_completed-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-backup_completed-assert.yaml new file mode 100644 index 0000000..7b1e9e5 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-backup_completed-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + cluster: + name: standalone-cluster + method: barmanObjectStore +status: + phase: completed diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-backup_running-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-backup_running-assert.yaml new file mode 100644 index 0000000..cbd9645 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-backup_running-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + cluster: + name: standalone-cluster + method: barmanObjectStore +status: + phase: running diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-checkpoint.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-checkpoint.yaml new file mode 100644 index 0000000..52862bf --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/03-checkpoint.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: backup-checkpoint +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: create-checkpoint + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: standalone-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + END_TIME=$(( $(date +%s) + 30 )) + while [ $(date +%s) -lt $END_TIME ]; do + psql "$DB_URI" -c "SELECT pg_switch_wal();CHECKPOINT;" + sleep 5 + done diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/04-post_backup_data_write-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/04-post_backup_data_write-assert.yaml new file mode 100644 index 0000000..ad9be77 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/04-post_backup_data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write-post-backup +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/04-post_backup_data_write.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/04-post_backup_data_write.yaml new file mode 100644 index 0000000..2e56595 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/04-post_backup_data_write.yaml @@ -0,0 +1,57 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: configmap-creator-sa +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: configmap-creator +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["create"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: configmap-creator-binding +subjects: +- kind: ServiceAccount + name: configmap-creator-sa +roleRef: + kind: Role + name: configmap-creator + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write-post-backup +spec: + template: + spec: + serviceAccountName: configmap-creator-sa + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: standalone-cluster-superuser + key: uri + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client kubectl coreutils + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + DATE_NO_BAD_TABLE=$(date --rfc-3339=ns) + sleep 30 + psql "$DB_URI" -c "CREATE TABLE mybadtable (id serial PRIMARY KEY);" + kubectl create configmap date-no-bad-table --from-literal=date="$DATE_NO_BAD_TABLE" diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/05-recovery_backup_cluster-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/05-recovery_backup_cluster-assert.yaml new file mode 100644 index 0000000..90c4b24 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/05-recovery_backup_cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: recovery-backup-cluster +status: + readyInstances: 2 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/05-recovery_backup_cluster.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/05-recovery_backup_cluster.yaml new file mode 100644 index 0000000..9eda6d9 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/05-recovery_backup_cluster.yaml @@ -0,0 +1,32 @@ +type: postgresql +mode: recovery + +cluster: + instances: 2 + storage: + size: 256Mi + +recovery: + method: backup + backupName: "post-init-backup" + provider: s3 + +backups: + enabled: true + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/postgresql-minio-backup-restore/v2" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/06-data_test-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/06-data_test-assert.yaml new file mode 100644 index 0000000..2ef11b8 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/06-data_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/06-data_test.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/06-data_test.yaml new file mode 100644 index 0000000..86a1543 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/06-data_test.yaml @@ -0,0 +1,23 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: recovery-backup-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mygoodtable$$)' --csv -q 2>/dev/null)" = "t" diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/07-recovery_object_store_cluster-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/07-recovery_object_store_cluster-assert.yaml new file mode 100644 index 0000000..f869303 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/07-recovery_object_store_cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: recovery-object-store-cluster +status: + readyInstances: 2 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/07-recovery_object_store_cluster.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/07-recovery_object_store_cluster.yaml new file mode 100644 index 0000000..7f059e3 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/07-recovery_object_store_cluster.yaml @@ -0,0 +1,48 @@ +type: postgresql +mode: recovery + +cluster: + instances: 2 + storage: + size: 256Mi + +recovery: + method: object_store + clusterName: "standalone-cluster" + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/postgresql-minio-backup-restore/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" + +backups: + enabled: true + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/postgresql-minio-backup-restore/v2" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/08-data_test-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/08-data_test-assert.yaml new file mode 100644 index 0000000..36eb4ff --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/08-data_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-object-store +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/08-data_test.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/08-data_test.yaml new file mode 100644 index 0000000..94ac2c3 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/08-data_test.yaml @@ -0,0 +1,23 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-object-store +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: recovery-object-store-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mygoodtable$$)' --csv -q 2>/dev/null)" = "t" diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/09-recovery_backup_pitr_cluster-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/09-recovery_backup_pitr_cluster-assert.yaml new file mode 100644 index 0000000..2b6b965 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/09-recovery_backup_pitr_cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: recovery-backup-pitr-cluster +status: + readyInstances: 2 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/09-recovery_backup_pitr_cluster.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/09-recovery_backup_pitr_cluster.yaml new file mode 100644 index 0000000..c2731b3 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/09-recovery_backup_pitr_cluster.yaml @@ -0,0 +1,48 @@ +type: postgresql +mode: recovery + +cluster: + instances: 2 + storage: + size: 256Mi + +recovery: + method: backup + backupName: "post-init-backup" + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/postgresql-minio-backup-restore/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" + +backups: + enabled: true + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/postgresql-minio-backup-restore/v2" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/10-data_test-assert.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/10-data_test-assert.yaml new file mode 100644 index 0000000..6f14d5f --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/10-data_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup-pitr +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/10-data_test.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/10-data_test.yaml new file mode 100644 index 0000000..5fb4faf --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/10-data_test.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup-pitr +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: recovery-backup-pitr-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + set -e + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mygoodtable$$)' --csv -q 2>/dev/null)" = "t" + echo "Good table exists" + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mybadtable$$)' --csv -q 2>/dev/null)" = "f" + echo "Bad table does not exist" diff --git a/charts/vshnpostgresql/test/postgresql-minio-backup-restore/chainsaw-test.yaml b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/chainsaw-test.yaml new file mode 100644 index 0000000..79a7d2d --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-minio-backup-restore/chainsaw-test.yaml @@ -0,0 +1,148 @@ +## +# This test sets up a CNPG cluster with MinIO backups and then restores the cluster from the backup using backup, +# object store, and object store with PITR recovery. +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: postgresql-minio-backup-restore +spec: + timeouts: + apply: 1s + assert: 2m + cleanup: 1m + steps: + - name: Clear the MinIO bucket + try: + - apply: + file: ./00-minio_cleanup.yaml + - assert: + file: ./00-minio_cleanup-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + - podLogs: + selector: batch.kubernetes.io/job-name=minio_cleanup + - name: Install the standalone cluster + try: + - script: + content: | + kubectl -n $NAMESPACE create secret generic kube-root-ca.crt --from-literal=ca.crt="$(kubectl -n kube-system get configmaps kube-root-ca.crt -o jsonpath='{.data.ca\.crt}')" --dry-run=client -o yaml | kubectl apply -f - + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-standalone_cluster.yaml \ + --wait \ + standalone ../../ + - assert: + file: 01-standalone_cluster-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - name: Write some data to the cluster + try: + - apply: + file: ./02-data_write.yaml + - assert: + file: ./02-data_write-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + - podLogs: + selector: batch.kubernetes.io/job-name=data-write + - name: Create a backup + try: + - apply: + file: ./03-backup.yaml + - assert: + file: ./03-backup_running-assert.yaml + - apply: + file: ./03-checkpoint.yaml + - assert: + file: ./03-backup_completed-assert.yaml + - name: Write more data to the database after the backup + try: + - apply: + file: ./04-post_backup_data_write.yaml + - assert: + file: ./04-post_backup_data_write-assert.yaml + timeouts: + apply: 1s + assert: 10m + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Backup + - name: Create a recovery cluster from backup + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./05-recovery_backup_cluster.yaml \ + --wait \ + recovery-backup ../../ + - assert: + file: ./05-recovery_backup_cluster-assert.yaml + - name: Verify the data on the backup recovery cluster exists + try: + - apply: + file: 06-data_test.yaml + - assert: + file: 06-data_test-assert.yaml + - name: Create a recovery cluster from object store + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./07-recovery_object_store_cluster.yaml \ + --wait \ + recovery-object-store ../../ + - assert: + file: ./07-recovery_object_store_cluster-assert.yaml + - name: Verify the data on the object store recovery cluster exists + try: + - apply: + file: 08-data_test.yaml + - assert: + file: 08-data_test-assert.yaml + - name: Create a recovery cluster from backup with a PITR target + try: + - script: + content: | + DATE_NO_BAD_TABLE=$(kubectl -n $NAMESPACE get configmap date-no-bad-table -o 'jsonpath={.data.date}') + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./09-recovery_backup_pitr_cluster.yaml \ + --set recovery.pitrTarget.time="$DATE_NO_BAD_TABLE" \ + --wait \ + recovery-backup-pitr ../../ + - assert: + file: ./09-recovery_backup_pitr_cluster-assert.yaml + - name: Verify the pre-backup data on the recovery cluster exists but not the post-backup data + try: + - apply: + file: 10-data_test.yaml + - assert: + file: 10-data_test-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + selector: batch.kubernetes.io/job-name=data-test-backup-pitr + - podLogs: + selector: batch.kubernetes.io/job-name=data-test-backup-pitr + - name: Cleanup + try: + - script: + content: | + helm uninstall --namespace $NAMESPACE standalone + helm uninstall --namespace $NAMESPACE recovery-backup + helm uninstall --namespace $NAMESPACE recovery-object-store + helm uninstall --namespace $NAMESPACE recovery-backup-pitr diff --git a/charts/vshnpostgresql/test/postgresql-pg_basebackup/00-source-cluster-assert.yaml b/charts/vshnpostgresql/test/postgresql-pg_basebackup/00-source-cluster-assert.yaml new file mode 100644 index 0000000..90ea90f --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-pg_basebackup/00-source-cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: source-cluster +status: + readyInstances: 1 diff --git a/charts/vshnpostgresql/test/postgresql-pg_basebackup/00-source-cluster.yaml b/charts/vshnpostgresql/test/postgresql-pg_basebackup/00-source-cluster.yaml new file mode 100644 index 0000000..5d0baa1 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-pg_basebackup/00-source-cluster.yaml @@ -0,0 +1,8 @@ +type: postgresql +mode: "standalone" +cluster: + instances: 1 + storage: + size: 256Mi +backups: + enabled: false diff --git a/charts/vshnpostgresql/test/postgresql-pg_basebackup/01-data_write-assert.yaml b/charts/vshnpostgresql/test/postgresql-pg_basebackup/01-data_write-assert.yaml new file mode 100644 index 0000000..831f963 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-pg_basebackup/01-data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-pg_basebackup/01-data_write.yaml b/charts/vshnpostgresql/test/postgresql-pg_basebackup/01-data_write.yaml new file mode 100644 index 0000000..cc5a743 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-pg_basebackup/01-data_write.yaml @@ -0,0 +1,30 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_USER + valueFrom: + secretKeyRef: + name: source-cluster-superuser + key: username + - name: DB_PASS + valueFrom: + secretKeyRef: + name: source-cluster-superuser + key: password + - name: DB_URI + value: postgres://$(DB_USER):$(DB_PASS)@source-cluster-rw:5432 + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + psql "$DB_URI" -c "CREATE DATABASE mygooddb;" + psql "$DB_URI/mygooddb" -c "CREATE TABLE mygoodtable (id serial PRIMARY KEY);" diff --git a/charts/vshnpostgresql/test/postgresql-pg_basebackup/02-pg_basebackup-cluster-assert.yaml b/charts/vshnpostgresql/test/postgresql-pg_basebackup/02-pg_basebackup-cluster-assert.yaml new file mode 100644 index 0000000..9b953d4 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-pg_basebackup/02-pg_basebackup-cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: pg-basebackup-cluster +status: + readyInstances: 2 diff --git a/charts/vshnpostgresql/test/postgresql-pg_basebackup/02-pg_basebackup-cluster.yaml b/charts/vshnpostgresql/test/postgresql-pg_basebackup/02-pg_basebackup-cluster.yaml new file mode 100644 index 0000000..310074e --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-pg_basebackup/02-pg_basebackup-cluster.yaml @@ -0,0 +1,24 @@ +type: postgresql +mode: "recovery" +recovery: + method: "pg_basebackup" + pgBaseBackup: + source: + host: "source-cluster-rw" + database: "mygooddb" + username: "streaming_replica" + sslMode: "require" + sslKeySecret: + name: source-cluster-replication + key: tls.key + sslCertSecret: + name: source-cluster-replication + key: tls.crt + +cluster: + instances: 2 + storage: + size: 256Mi + +backups: + enabled: false diff --git a/charts/vshnpostgresql/test/postgresql-pg_basebackup/03-data_test-assert.yaml b/charts/vshnpostgresql/test/postgresql-pg_basebackup/03-data_test-assert.yaml new file mode 100644 index 0000000..04df941 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-pg_basebackup/03-data_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/postgresql-pg_basebackup/03-data_test.yaml b/charts/vshnpostgresql/test/postgresql-pg_basebackup/03-data_test.yaml new file mode 100644 index 0000000..40eb902 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-pg_basebackup/03-data_test.yaml @@ -0,0 +1,23 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: pg-basebackup-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + test "$(psql "${DB_URI}mygooddb" -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mygoodtable$$)' --csv -q 2>/dev/null)" = "t" diff --git a/charts/vshnpostgresql/test/postgresql-pg_basebackup/chainsaw-test.yaml b/charts/vshnpostgresql/test/postgresql-pg_basebackup/chainsaw-test.yaml new file mode 100644 index 0000000..85f2d97 --- /dev/null +++ b/charts/vshnpostgresql/test/postgresql-pg_basebackup/chainsaw-test.yaml @@ -0,0 +1,64 @@ +## +# This is a test that provisions a regular (non CNPG) PostgreSQL cluster and attempts to perform a pg_basebackup recovery. +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: postgresql-pg-basebackup +spec: + timeouts: + apply: 1s + assert: 2m + cleanup: 1m + steps: + - name: Install the external PostgreSQL cluster + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./00-source-cluster.yaml \ + --wait \ + source ../../ + - assert: + file: ./00-source-cluster-assert.yaml + - apply: + file: ./01-data_write.yaml + - assert: + file: ./01-data_write-assert.yaml + - name: Install the pg_basebackup cluster + timeouts: + assert: 5m + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./02-pg_basebackup-cluster.yaml \ + --wait \ + pg-basebackup ../../ + - assert: + file: ./02-pg_basebackup-cluster-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - name: Verify the data from step 1 exists + try: + - apply: + file: ./03-data_test.yaml + - assert: + file: ./03-data_test-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + - podLogs: + selector: batch.kubernetes.io/job-name=data-test + - name: Cleanup + try: + - script: + content: | + helm uninstall --namespace $NAMESPACE source + helm uninstall --namespace $NAMESPACE pg-basebackup diff --git a/charts/vshnpostgresql/test/scheduledbackups/00-minio_cleanup-assert.yaml b/charts/vshnpostgresql/test/scheduledbackups/00-minio_cleanup-assert.yaml new file mode 100644 index 0000000..9c0f3eb --- /dev/null +++ b/charts/vshnpostgresql/test/scheduledbackups/00-minio_cleanup-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/scheduledbackups/00-minio_cleanup.yaml b/charts/vshnpostgresql/test/scheduledbackups/00-minio_cleanup.yaml new file mode 100644 index 0000000..90151a9 --- /dev/null +++ b/charts/vshnpostgresql/test/scheduledbackups/00-minio_cleanup.yaml @@ -0,0 +1,16 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: minio-cleanup + image: minio/mc + command: ['sh', '-c'] + args: + - | + mc alias set myminio https://minio.minio.svc.cluster.local minio minio123 + mc rm --recursive --force myminio/mybucket/scheduledbackups diff --git a/charts/vshnpostgresql/test/scheduledbackups/01-scheduledbackups_cluster-assert.yaml b/charts/vshnpostgresql/test/scheduledbackups/01-scheduledbackups_cluster-assert.yaml new file mode 100644 index 0000000..a3af1a2 --- /dev/null +++ b/charts/vshnpostgresql/test/scheduledbackups/01-scheduledbackups_cluster-assert.yaml @@ -0,0 +1,37 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: scheduledbackups-cluster +status: + readyInstances: 1 +--- +apiVersion: postgresql.cnpg.io/v1 +kind: ScheduledBackup +metadata: + name: scheduledbackups-cluster-daily-backup +spec: + immediate: true + schedule: "0 0 0 * * *" + method: barmanObjectStore + backupOwnerReference: self + cluster: + name: scheduledbackups-cluster +--- +apiVersion: postgresql.cnpg.io/v1 +kind: ScheduledBackup +metadata: + name: scheduledbackups-cluster-weekly-backup +spec: + immediate: true + schedule: "0 0 0 * * 1" + method: barmanObjectStore + backupOwnerReference: self + cluster: + name: scheduledbackups-cluster +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +spec: + method: barmanObjectStore + cluster: + name: scheduledbackups-cluster diff --git a/charts/vshnpostgresql/test/scheduledbackups/01-scheduledbackups_cluster.yaml b/charts/vshnpostgresql/test/scheduledbackups/01-scheduledbackups_cluster.yaml new file mode 100644 index 0000000..c5ea25b --- /dev/null +++ b/charts/vshnpostgresql/test/scheduledbackups/01-scheduledbackups_cluster.yaml @@ -0,0 +1,36 @@ +type: postgresql +mode: standalone + +cluster: + instances: 1 + storage: + size: 256Mi + +backups: + enabled: true + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/scheduledbackups/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + retentionPolicy: "30d" + scheduledBackups: + - name: daily-backup + schedule: "0 0 0 * * *" + backupOwnerReference: self + method: barmanObjectStore + - name: weekly-backup + schedule: "0 0 0 * * 1" + backupOwnerReference: self + method: barmanObjectStore + diff --git a/charts/vshnpostgresql/test/scheduledbackups/chainsaw-test.yaml b/charts/vshnpostgresql/test/scheduledbackups/chainsaw-test.yaml new file mode 100644 index 0000000..b72c53f --- /dev/null +++ b/charts/vshnpostgresql/test/scheduledbackups/chainsaw-test.yaml @@ -0,0 +1,27 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: scheduledbackups +spec: + timeouts: + apply: 1s + assert: 1m + cleanup: 5m + steps: + - name: Install the a cluster with ScheduledBackups + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-scheduledbackups_cluster.yaml \ + --wait \ + scheduledbackups ../../ + - assert: + file: ./01-scheduledbackups_cluster-assert.yaml + - name: Cleanup + try: + - script: + content: | + helm uninstall --namespace $NAMESPACE scheduledbackups diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/00-minio_cleanup-assert.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/00-minio_cleanup-assert.yaml new file mode 100644 index 0000000..9c0f3eb --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/00-minio_cleanup-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/00-minio_cleanup.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/00-minio_cleanup.yaml new file mode 100644 index 0000000..ce71b1e --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/00-minio_cleanup.yaml @@ -0,0 +1,16 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: minio-cleanup + image: minio/mc + command: ['sh', '-c'] + args: + - | + mc alias set myminio https://minio.minio.svc.cluster.local minio minio123 + mc rm --recursive --force myminio/mybucket/timescale diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/01-timescale_cluster-assert.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/01-timescale_cluster-assert.yaml new file mode 100644 index 0000000..3bbd2f8 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/01-timescale_cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: timescale-cluster +status: + readyInstances: 2 diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/01-timescale_cluster.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/01-timescale_cluster.yaml new file mode 100644 index 0000000..f84117f --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/01-timescale_cluster.yaml @@ -0,0 +1,28 @@ +type: timescaledb +mode: standalone + +cluster: + instances: 2 + storage: + size: 256Mi + +backups: + enabled: true + + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/timescale/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/03-timescale_test-assert.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/03-timescale_test-assert.yaml new file mode 100644 index 0000000..aa63a21 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/03-timescale_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: timescale-test +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/03-timescale_test.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/03-timescale_test.yaml new file mode 100644 index 0000000..9b7581f --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/03-timescale_test.yaml @@ -0,0 +1,22 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: timescale-test +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: timescale-cluster-app + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM pg_extension WHERE extname = '\''timescaledb'\'')' --csv -q 2>/dev/null)" = "t" \ No newline at end of file diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/04-data_write-assert.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/04-data_write-assert.yaml new file mode 100644 index 0000000..831f963 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/04-data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/04-data_write.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/04-data_write.yaml new file mode 100644 index 0000000..b827de1 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/04-data_write.yaml @@ -0,0 +1,54 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: configmap-creator-sa +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: configmap-creator +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["create"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: configmap-creator-binding +subjects: +- kind: ServiceAccount + name: configmap-creator-sa +roleRef: + kind: Role + name: configmap-creator + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +spec: + template: + spec: + serviceAccountName: configmap-creator-sa + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: timescale-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client kubectl coreutils + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + psql "$DB_URI" -c "CREATE TABLE mygoodtable (id serial PRIMARY KEY);" + sleep 5 + DATE_NO_BAD_TABLE=$(date --rfc-3339=ns) + kubectl create configmap date-no-bad-table --from-literal=date="$DATE_NO_BAD_TABLE" + sleep 5 diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-backup.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-backup.yaml new file mode 100644 index 0000000..be5e4b1 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-backup.yaml @@ -0,0 +1,8 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + method: barmanObjectStore + cluster: + name: timescale-cluster diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-backup_completed-assert.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-backup_completed-assert.yaml new file mode 100644 index 0000000..040b1a4 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-backup_completed-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + cluster: + name: timescale-cluster + method: barmanObjectStore +status: + phase: completed diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-backup_running-assert.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-backup_running-assert.yaml new file mode 100644 index 0000000..dc35727 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-backup_running-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + cluster: + name: timescale-cluster + method: barmanObjectStore +status: + phase: running diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-checkpoint.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-checkpoint.yaml new file mode 100644 index 0000000..3ba7fc7 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/05-checkpoint.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: backup-checkpoint +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: create-checkpoint + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: timescale-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + END_TIME=$(( $(date +%s) + 30 )) + while [ $(date +%s) -lt $END_TIME ]; do + psql "$DB_URI" -c "SELECT pg_switch_wal();CHECKPOINT;" + sleep 5 + done diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/06-post_backup_data_write-assert.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/06-post_backup_data_write-assert.yaml new file mode 100644 index 0000000..ad9be77 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/06-post_backup_data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write-post-backup +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/06-post_backup_data_write.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/06-post_backup_data_write.yaml new file mode 100644 index 0000000..8585b24 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/06-post_backup_data_write.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write-post-backup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: timescale-cluster-superuser + key: uri + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + psql "$DB_URI" -c "CREATE TABLE mybadtable (id serial PRIMARY KEY);" diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/07-recovery_backup_pitr_cluster-assert.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/07-recovery_backup_pitr_cluster-assert.yaml new file mode 100644 index 0000000..2b6b965 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/07-recovery_backup_pitr_cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: recovery-backup-pitr-cluster +status: + readyInstances: 2 diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/07-recovery_backup_pitr_cluster.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/07-recovery_backup_pitr_cluster.yaml new file mode 100644 index 0000000..7e9c38f --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/07-recovery_backup_pitr_cluster.yaml @@ -0,0 +1,48 @@ +type: timescaledb +mode: recovery + +cluster: + instances: 2 + storage: + size: 256Mi + +recovery: + method: backup + backupName: "post-init-backup" + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/timescale/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" + +backups: + enabled: true + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/timescale/v2" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/08-data_test-assert.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/08-data_test-assert.yaml new file mode 100644 index 0000000..6f14d5f --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/08-data_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup-pitr +status: + succeeded: 1 diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/08-data_test.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/08-data_test.yaml new file mode 100644 index 0000000..5fb4faf --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/08-data_test.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup-pitr +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: recovery-backup-pitr-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + set -e + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mygoodtable$$)' --csv -q 2>/dev/null)" = "t" + echo "Good table exists" + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mybadtable$$)' --csv -q 2>/dev/null)" = "f" + echo "Bad table does not exist" diff --git a/charts/vshnpostgresql/test/timescale-minio-backup-restore/chainsaw-test.yaml b/charts/vshnpostgresql/test/timescale-minio-backup-restore/chainsaw-test.yaml new file mode 100644 index 0000000..4961533 --- /dev/null +++ b/charts/vshnpostgresql/test/timescale-minio-backup-restore/chainsaw-test.yaml @@ -0,0 +1,129 @@ +## +# This test sets up a timescale cluster with MinIO backups and ensured that timescale extensions are installed and +# PITR recovery is enabled and working. +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: timescale +spec: + timeouts: + apply: 1s + assert: 5m + cleanup: 1m + steps: + - name: Clear the MinIO bucket + try: + - apply: + file: ./00-minio_cleanup.yaml + - assert: + file: ./00-minio_cleanup-assert.yaml + - name: Install a standalone timescale cluster + try: + - script: + content: | + kubectl -n $NAMESPACE create secret generic kube-root-ca.crt --from-literal=ca.crt="$(kubectl -n kube-system get configmaps kube-root-ca.crt -o jsonpath='{.data.ca\.crt}')" --dry-run=client -o yaml | kubectl apply -f - + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-timescale_cluster.yaml \ + --wait \ + timescale ../../ + - assert: + file: ./01-timescale_cluster-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=timescale-cluster + - name: Verify timescale extensions are installed + timeouts: + apply: 1s + assert: 30s + try: + - apply: + file: 03-timescale_test.yaml + - assert: + file: 03-timescale_test-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + - podLogs: + selector: batch.kubernetes.io/job-name=data-test + - name: Write some data to the cluster + timeouts: + apply: 1s + assert: 30s + try: + - apply: + file: 04-data_write.yaml + - assert: + file: 04-data_write-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + - podLogs: + selector: batch.kubernetes.io/job-name=data-test + - name: Create a backup + try: + - apply: + file: ./05-backup.yaml + - assert: + file: ./05-backup_running-assert.yaml + - apply: + file: ./05-checkpoint.yaml + - assert: + file: ./05-backup_completed-assert.yaml + - name: Write more data to the database after the backup + try: + - apply: + file: ./06-post_backup_data_write.yaml + - assert: + file: ./06-post_backup_data_write-assert.yaml + timeouts: + apply: 1s + assert: 10m + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Backup + - name: Create a recovery cluster from backup with a PITR target + try: + - script: + content: | + DATE_NO_BAD_TABLE=$(kubectl -n $NAMESPACE get configmap date-no-bad-table -o 'jsonpath={.data.date}') + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./07-recovery_backup_pitr_cluster.yaml \ + --set recovery.pitrTarget.time="$DATE_NO_BAD_TABLE" \ + --wait \ + recovery-backup-pitr ../../ + - assert: + file: ./07-recovery_backup_pitr_cluster-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=recovery-backup-pitr-cluster + - name: Verify the pre-backup data on the recovery cluster exists but not the post-backup data + try: + - apply: + file: 08-data_test.yaml + - assert: + file: 08-data_test-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + selector: batch.kubernetes.io/job-name=data-test-backup-pitr + - podLogs: + selector: batch.kubernetes.io/job-name=data-test-backup-pitr + - name: Cleanup + try: + - script: + content: | + helm uninstall --namespace $NAMESPACE timescale diff --git a/charts/vshnpostgresql/values.schema.json b/charts/vshnpostgresql/values.schema.json new file mode 100644 index 0000000..8890226 --- /dev/null +++ b/charts/vshnpostgresql/values.schema.json @@ -0,0 +1,703 @@ +{ + "$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" + } + } + }, + "data": { + "type": "object", + "properties": { + "compression": { + "type": "string" + }, + "encryption": { + "type": "string" + }, + "jobs": { + "type": "integer" + } + } + }, + "destinationPath": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "endpointCA": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "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" + }, + "inheritFromIAMRole": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "region": { + "type": "string" + }, + "secretKey": { + "type": "string" + } + } + }, + "scheduledBackups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backupOwnerReference": { + "type": "string" + }, + "method": { + "type": "string" + }, + "name": { + "type": "string" + }, + "schedule": { + "type": "string" + } + } + } + }, + "secret": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "wal": { + "type": "object", + "properties": { + "compression": { + "type": "string" + }, + "encryption": { + "type": "string" + }, + "maxParallel": { + "type": "integer" + } + } + } + } + }, + "cluster": { + "type": "object", + "properties": { + "additionalLabels": { + "type": "object" + }, + "affinity": { + "type": "object", + "properties": { + "topologyKey": { + "type": "string" + } + } + }, + "annotations": { + "type": "object" + }, + "certificates": { + "type": "object" + }, + "enablePDB": { + "type": "boolean" + }, + "enableSuperuserAccess": { + "type": "boolean" + }, + "env": { + "type": "array" + }, + "envFrom": { + "type": "array" + }, + "imageCatalogRef": { + "type": "object" + }, + "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" + }, + "customQueriesSecret": { + "type": "array" + }, + "disableDefaultQueries": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "podMonitor": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "metricRelabelings": { + "type": "array" + }, + "relabelings": { + "type": "array" + } + } + }, + "prometheusRule": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "excludeRules": { + "type": "array" + } + } + } + } + }, + "postgresGID": { + "type": "integer" + }, + "postgresUID": { + "type": "integer" + }, + "postgresql": { + "type": "object", + "properties": { + "ldap": { + "type": "object" + }, + "parameters": { + "type": "object" + }, + "pg_hba": { + "type": "array" + }, + "pg_ident": { + "type": "array" + }, + "shared_preload_libraries": { + "type": "array" + }, + "synchronous": { + "type": "object" + } + } + }, + "primaryUpdateMethod": { + "type": "string" + }, + "primaryUpdateStrategy": { + "type": "string" + }, + "priorityClassName": { + "type": "string" + }, + "resources": { + "type": "object" + }, + "roles": { + "type": "array" + }, + "serviceAccountTemplate": { + "type": "object" + }, + "services": { + "type": "object" + }, + "storage": { + "type": "object", + "properties": { + "size": { + "type": "string" + }, + "storageClass": { + "type": "string" + } + } + }, + "superuserSecret": { + "type": "string" + }, + "walStorage": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "size": { + "type": "string" + }, + "storageClass": { + "type": "string" + } + } + } + } + }, + "fullnameOverride": { + "type": "string" + }, + "imageCatalog": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "images": { + "type": "array" + } + } + }, + "mode": { + "type": "string" + }, + "nameOverride": { + "type": "string" + }, + "namespaceOverride": { + "type": "string" + }, + "poolers": { + "type": "array" + }, + "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" + }, + "database": { + "type": "string" + }, + "destinationPath": { + "type": "string" + }, + "endpointCA": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "endpointURL": { + "type": "string" + }, + "google": { + "type": "object", + "properties": { + "applicationCredentials": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "gkeEnvironment": { + "type": "boolean" + }, + "path": { + "type": "string" + } + } + }, + "import": { + "type": "object", + "properties": { + "databases": { + "type": "array" + }, + "pgDumpExtraOptions": { + "type": "array" + }, + "pgRestoreExtraOptions": { + "type": "array" + }, + "postImportApplicationSQL": { + "type": "array" + }, + "roles": { + "type": "array" + }, + "schemaOnly": { + "type": "boolean" + }, + "source": { + "type": "object", + "properties": { + "database": { + "type": "string" + }, + "host": { + "type": "string" + }, + "passwordSecret": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "port": { + "type": "integer" + }, + "sslCertSecret": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "sslKeySecret": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "sslMode": { + "type": "string" + }, + "sslRootCertSecret": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "username": { + "type": "string" + } + } + }, + "type": { + "type": "string" + } + } + }, + "method": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "pgBaseBackup": { + "type": "object", + "properties": { + "database": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "secret": { + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "database": { + "type": "string" + }, + "host": { + "type": "string" + }, + "passwordSecret": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "port": { + "type": "integer" + }, + "sslCertSecret": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "sslKeySecret": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "sslMode": { + "type": "string" + }, + "sslRootCertSecret": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "username": { + "type": "string" + } + } + } + } + }, + "pitrTarget": { + "type": "object", + "properties": { + "time": { + "type": "string" + } + } + }, + "provider": { + "type": "string" + }, + "s3": { + "type": "object", + "properties": { + "accessKey": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "inheritFromIAMRole": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "region": { + "type": "string" + }, + "secretKey": { + "type": "string" + } + } + }, + "secret": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + } + } + }, + "type": { + "type": "string" + }, + "version": { + "type": "object", + "properties": { + "postgis": { + "type": "string" + }, + "postgresql": { + "type": "string" + }, + "timescaledb": { + "type": "string" + } + } + } + } +} diff --git a/charts/vshnpostgresql/values.yaml b/charts/vshnpostgresql/values.yaml new file mode 100644 index 0000000..11f3b60 --- /dev/null +++ b/charts/vshnpostgresql/values.yaml @@ -0,0 +1,503 @@ +# -- Override the name of the chart +nameOverride: "" +# -- Override the full name of the chart +fullnameOverride: "" +# -- Override the namespace of the chart +namespaceOverride: "" + +### +# -- Type of the CNPG database. Available types: +# * `postgresql` +# * `postgis` +# * `timescaledb` +type: postgresql + +version: + # -- PostgreSQL major version to use + postgresql: "16" + # -- If using TimescaleDB, specify the version + timescaledb: "2.15" + # -- If using PostGIS, specify the version + postgis: "3.4" + +### +# -- 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. + # * `import` - Import one or more databases from an existing Postgres cluster. + 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`. + + ## + # -- The original cluster name when used in backups. Also known as serverName. + clusterName: "" + # -- Name of the database used by the application. Default: `app`. + database: app + # -- Name of the owner of the database in the instance to be used by applications. Defaults to the value of the `database` key. + owner: "" + # -- Overrides the provider specific default endpoint. Defaults to: + # 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/ + # Google: gs:// + destinationPath: "" + # -- One of `s3`, `azure` or `google` + provider: s3 + s3: + region: "" + bucket: "" + path: "/" + accessKey: "" + secretKey: "" + # -- Use the role based authentication without providing explicitly the keys + inheritFromIAMRole: false + azure: + path: "/" + connectionString: "" + storageAccount: "" + storageKey: "" + storageSasToken: "" + containerName: "" + serviceName: blob + inheritFromAzureAD: false + google: + path: "/" + bucket: "" + gkeEnvironment: false + applicationCredentials: "" + secret: + # -- Whether to create a secret for the backup credentials + create: true + # -- Name of the backup credentials secret + name: "" + + # See https://cloudnative-pg.io/documentation/current/bootstrap/#bootstrap-from-a-live-cluster-pg_basebackup + pgBaseBackup: + # -- Name of the database used by the application. Default: `app`. + database: app + # -- 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 + secret: "" + # -- Name of the owner of the database in the instance to be used by applications. Defaults to the value of the `database` key. + owner: "" + source: + host: "" + port: 5432 + username: "" + database: "app" + sslMode: "verify-full" + passwordSecret: + # -- Whether to create a secret for the password + create: false + # -- Name of the secret containing the password + name: "" + # -- The key in the secret containing the password + key: "password" + # -- The password value to use when creating the secret + value: "" + sslKeySecret: + name: "" + key: "" + sslCertSecret: + name: "" + key: "" + sslRootCertSecret: + name: "" + key: "" + + # See: https://cloudnative-pg.io/documentation/current/cloudnative-pg.v1/#postgresql-cnpg-io-v1-Import + import: + # -- One of `microservice` or `monolith.` + # See: https://cloudnative-pg.io/documentation/current/database_import/#how-it-works + type: "microservice" + # -- Databases to import + databases: [] + # -- Roles to import + roles: [] + # -- List of SQL queries to be executed as a superuser in the application database right after is imported. + # To be used with extreme care. Only available in microservice type. + postImportApplicationSQL: [] + # -- When set to true, only the pre-data and post-data sections of pg_restore are invoked, avoiding data import. + schemaOnly: false + # -- List of custom options to pass to the `pg_dump` command. IMPORTANT: Use these options with caution and at your + # own risk, as the operator does not validate their content. Be aware that certain options may conflict with the + # operator's intended functionality or design. + pgDumpExtraOptions: [] + # -- List of custom options to pass to the `pg_restore` command. IMPORTANT: Use these options with caution and at + # your own risk, as the operator does not validate their content. Be aware that certain options may conflict with the + # operator's intended functionality or design. + pgRestoreExtraOptions: [] + source: + host: "" + port: 5432 + username: "" + database: "" + sslMode: "verify-full" + passwordSecret: + # -- Whether to create a secret for the password + create: false + # -- Name of the secret containing the password + name: "" + # -- The key in the secret containing the password + key: "password" + # -- The password value to use when creating the secret + value: "" + sslKeySecret: + name: "" + key: "" + sslCertSecret: + name: "" + key: "" + sslRootCertSecret: + name: "" + key: "" + + +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) + + # -- Reference to `ImageCatalog` of `ClusterImageCatalog`, if specified takes precedence over `cluster.imageName` + imageCatalogRef: {} + # kind: ImageCatalog + # name: postgresql + + # -- 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: "" + + walStorage: + enabled: false + size: 1Gi + storageClass: "" + + # -- The UID of the postgres user inside the image, defaults to 26 + postgresUID: -1 + + # -- The GID of the postgres user inside the image, defaults to 26 + postgresGID: -1 + + # -- Customization of service definitions. Please refer to https://cloudnative-pg.io/documentation/current/service_management/ + services: {} + + # -- 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 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 + + # -- Env follows the Env format to pass environment variables to the pods created in the cluster + env: [] + # - name: MY_CUSTOM_FLAG + # value: "enabled" + # - name: MY_CUSTROM_ENV + # valueFrom: + # configMapKeyRef: + # name: my-custom-env + # key: env + # optional: true + # - name: MY_CUSTOM_SECRET_ENV + # valueFrom: + # secretKeyRef: + # name: my-custom-secret + # key: secret + # optional: true + + # -- EnvFrom follows the EnvFrom format to pass environment variables sources to the pods to be used by Env + envFrom: [] + # - configMapRef: + # name: global-envs + # optional: true + # - secretRef: + # name: db-credentials + # optional: true + + # -- 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: "" + + # -- Allow to disable PDB, mainly useful for upgrade of single-instance clusters or development purposes + # See: https://cloudnative-pg.io/documentation/current/kubernetes_upgrade/#pod-disruption-budgets + enablePDB: true + + # -- 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 + podMonitor: + # -- Whether to enable the PodMonitor + enabled: true + # --The list of relabelings for the PodMonitor. + # Applied to samples before scraping. + relabelings: [] + # -- The list of metric relabelings for the PodMonitor. + # Applied to samples before ingestion. + metricRelabelings: [] + prometheusRule: + # -- Whether to enable the PrometheusRule automated alerts + enabled: true + # -- Exclude specified rules + excludeRules: [] + # - CNPGClusterZoneSpreadWarning + # -- Whether the default queries should be injected. + # Set it to true if you don't want to inject default queries into the cluster. + disableDefaultQueries: false + # -- Custom Prometheus metrics + # Will be stored in the ConfigMap + 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;" + # target_databases: ["*"] + # predicate_query: "SELECT '{{ .Values.version.postgresql }}';" + # metrics: + # - datname: + # usage: "LABEL" + # description: "Name of the database" + # - ratio: + # usage: GAUGE + # description: "Cache hit ratio" + # -- The list of secrets containing the custom queries + customQueriesSecret: [] + # - name: custom-queries-secret + # key: custom-queries + + postgresql: + # -- PostgreSQL configuration options (postgresql.conf) + parameters: {} + # max_connections: 300 + # -- Quorum-based Synchronous Replication + synchronous: {} + # method: any + # number: 1 + # -- PostgreSQL Host Based Authentication rules (lines to be appended to the pg_hba.conf file) + pg_hba: [] + # - host all all 10.244.0.0/16 md5 + # -- PostgreSQL User Name Maps rules (lines to be appended to the pg_ident.conf file) + pg_ident: [] + # - mymap /^(.*)@mydomain\.com$ \1 + # -- Lists of shared preload libraries to add to the default ones + shared_preload_libraries: [] + # - pgaudit + # -- PostgreSQL LDAP configuration (see https://cloudnative-pg.io/documentation/current/postgresql_conf/#ldap-configuration) + ldap: {} + # https://cloudnative-pg.io/documentation/current/postgresql_conf/#ldap-configuration + # server: 'openldap.default.svc.cluster.local' + # bindSearchAuth: + # baseDN: 'ou=org,dc=example,dc=com' + # bindDN: 'cn=admin,dc=example,dc=com' + # bindPassword: + # name: 'ldapBindPassword' + # key: 'data' + # searchAttribute: 'uid' + + + # -- 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: "" # 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 + # options: [] + # encoding: UTF8 + # postInitSQL: + # - CREATE EXTENSION IF NOT EXISTS vector; + # postInitApplicationSQL: [] + # postInitTemplateSQL: [] + + # -- Configure the metadata of the generated service account + serviceAccountTemplate: {} + + additionalLabels: {} + annotations: {} + + +backups: + # -- You need to configure backups manually, so backups are disabled by default. + enabled: false + + # -- Backup method to use. Available methods: + # * `barmanObjectStore` - Uses CNPG's built-in barman object store (legacy method) + # * `plugin` - Uses CNPG's plugin system with barman-cloud plugin (recommended) + method: barmanObjectStore + + # -- Overrides the provider specific default endpoint. Defaults to: + # S3: https://s3..amazonaws.com" + endpointURL: "" # Leave empty if using the default S3 endpoint + + # -- One of `s3`, `azure` or `google` (only used for non-plugin backup method) + provider: s3 + + s3: + region: "" + bucket: "" + accessKey: "" + secretKey: "" + + 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 + 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 + + # -- Retention policy for backups + retentionPolicy: "30d" + + # -- Plugin-specific configuration (only used when method is 'plugin') + plugin: + # -- Create an ObjectStore resource + createObjectStore: true + + scheduledBackups: + - + # -- Scheduled backup name + name: daily-backup + # -- Schedule in cron format + schedule: "0 0 0 * * *" + # -- Backup owner reference (only used with barmanObjectStore method) + backupOwnerReference: self + # -- Whether to take a backup immediately upon creation (only used with plugin method) + immediate: true + +imageCatalog: + # -- Whether to provision an image catalog. If imageCatalog.images is empty this option will be ignored. + create: true + # -- List of images to be provisioned in an image catalog. + images: [] + # - image: ghcr.io/your_repo/your_image:your_tag + # major: 16 + +# -- List of PgBouncer poolers +poolers: [] + # - + # # -- Pooler name + # name: rw + # # -- PgBouncer type of service to forward traffic to. + # type: rw + # # -- PgBouncer pooling mode + # poolMode: transaction + # # -- Number of PgBouncer instances + # instances: 3 + # # -- PgBouncer configuration parameters + # 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: {} + # - + # # -- Pooler name + # name: ro + # # -- PgBouncer type of service to forward traffic to. + # type: ro + # # -- PgBouncer pooling mode + # poolMode: transaction + # # -- Number of PgBouncer instances + # instances: 3 + # # -- PgBouncer configuration parameters + # 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: {}