Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,22 +91,26 @@ regions, pass them as arguments:
> command line.

This process takes a few minutes to complete.
It installs the latest version of CloudNativePG, cert-manager, and the
[Barman Cloud plugin](https://cloudnative-pg.io/plugin-barman-cloud/),
followed by the deployment of the PostgreSQL clusters.
It installs the latest version of CloudNativePG, cert-manager, the
[Barman Cloud plugin](https://cloudnative-pg.io/plugin-barman-cloud/), and a
[`ClusterImageCatalog`](https://github.com/cloudnative-pg/artifacts/tree/main/image-catalogs-extensions)
providing common extensions (pgvector, PostGIS, TimescaleDB, pgaudit,
wal2json, pg-crash), followed by the deployment of the PostgreSQL clusters.

### Options

| Variable | Default | Description |
|----------|---------|-------------|
| `LEGACY=true` | `false` | Use the legacy in-tree Barman Cloud code instead of the Barman Cloud Plugin |
| `TRUNK=true` | `false` | Deploy from the `main` branch of both CloudNativePG and the Barman Cloud Plugin |
| `REQUIREMENTS_ONLY=true` | `false` | Deploy only CloudNativePG, cert-manager, and the Barman Cloud Plugin; skip ObjectStores/Clusters. A later plain run automatically detects and skips already-installed requirements |
| `REQUIREMENTS_ONLY=true` | `false` | Deploy only CloudNativePG, cert-manager, the Barman Cloud Plugin, and the `ClusterImageCatalog`; skip ObjectStores/Clusters. A later plain run automatically detects and skips already-installed requirements |
| `IMAGE_CATALOG_URL=<url>` | [`catalog-minimal-trixie.yaml`](https://github.com/cloudnative-pg/artifacts/blob/main/image-catalogs-extensions/catalog-minimal-trixie.yaml) | `ClusterImageCatalog` manifest applied in every region |
| `IMAGE_CATALOG_NAME=<name>` | `postgresql-minimal-trixie` | Must match `metadata.name` in `IMAGE_CATALOG_URL`; referenced by the Cluster's `imageCatalogRef` (plugin mode) |
| `POSTGRESQL_VERSION=<major>` | `18` | PostgreSQL major version selected from the catalog, in plugin mode |
| `DRY_RUN=true` | `false` | Print the generated YAML to stdout without applying it |
| `OUTPUT_DIR=<path>` | _(unset)_ | Save the generated YAML to `<path>/<region>.yaml` (one file per region) and apply it |
| `DRY_RUN=true OUTPUT_DIR=<path>` | | Save the generated YAML to files only, without applying |
| `POSTGRESQL_IMAGE=<image>` | `ghcr.io/cloudnative-pg/postgresql:18-standard-trixie` | PostgreSQL image used in plugin mode |
| `POSTGRESQL_LEGACY_IMAGE=<image>` | `ghcr.io/cloudnative-pg/postgresql:18-system-trixie` | PostgreSQL image used in legacy mode |
| `POSTGRESQL_LEGACY_IMAGE=<image>` | `ghcr.io/cloudnative-pg/postgresql:18-system-trixie` | PostgreSQL image used directly (`imageName`) in legacy mode; the catalog only ships minimal images, so legacy mode can't use `imageCatalogRef` |
| `K8S_CONTEXT_PREFIX` | `kind-` | Prefix of kubectl context names; override when targeting non-Kind clusters |
| `K8S_NAME` | `k8s-` | Base name of clusters in kubectl context names |
| `DEBUG=true` | `false` | Enable shell trace output (`set -x`) for debugging |
Expand All @@ -127,8 +131,10 @@ fragments without modifying the repository.
|----------|-------------|
| `TEMPLATES_DIR=<path>` | Replace the whole templates directory with your own |
| `CLUSTER_TEMPLATE=<file>` | Override `cluster.yaml` only |
| `STORAGE_TEMPLATE=<file>` | Override `storage.yaml` (shared by both modes) |
| `BOOTSTRAP_INITDB_TEMPLATE=<file>` | Override `bootstrap-initdb.yaml` |
| `BOOTSTRAP_RECOVERY_TEMPLATE=<file>` | Override `bootstrap-recovery.yaml` |
| `IMAGE_CATALOG_TEMPLATE=<file>` | Override `image-catalog.yaml` (plugin mode's `imageCatalogRef`) |
| `CLUSTER_PLUGIN_PARAMS_TEMPLATE=<file>` | Override `cluster-plugin-params.yaml` |
| `REPLICA_SECTION_TEMPLATE=<file>` | Override `replica-section.yaml` |
| `EXTERNAL_CLUSTER_PLUGIN_TEMPLATE=<file>` | Override `external-cluster-plugin.yaml` |
Expand All @@ -141,6 +147,7 @@ Legacy-mode equivalents (used with `LEGACY=true`):
| Variable | Description |
|----------|-------------|
| `CLUSTER_LEGACY_PARAMS_TEMPLATE=<file>` | Override `legacy/cluster-legacy-params.yaml` |
| `IMAGE_LEGACY_TEMPLATE=<file>` | Override `legacy/image-legacy.yaml` (legacy mode's `imageName`) |
| `EXTERNAL_CLUSTER_LEGACY_TEMPLATE=<file>` | Override `legacy/external-cluster-legacy.yaml` |
| `SCHEDULEDBACKUP_LEGACY_TEMPLATE=<file>` | Override `legacy/scheduledbackup-legacy.yaml` |

Expand Down
20 changes: 14 additions & 6 deletions demo/funcs_requirements.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

#
# Deployment of the CloudNativePG demo requirements: the CNPG operator,
# cert-manager, and the Barman Cloud Plugin. Sourced by demo/setup.sh.
# cert-manager, the Barman Cloud Plugin, and a ClusterImageCatalog (common
# extensions). Sourced by demo/setup.sh.
#

# Check whether a CRD exists in the given cluster context
Expand All @@ -30,18 +31,19 @@ check_crd_existence() {
kubectl --context "${context}" get crd "${crd}" &>/dev/null
}

# Deploy CloudNativePG, cert-manager, and the Barman Cloud Plugin into a
# single region, unless they are already installed there.
# Deploy CloudNativePG, cert-manager, the Barman Cloud Plugin, and a
# ClusterImageCatalog into a single region, unless they are already
# installed there.
# Globals used: trunk, CERT_MANAGER_VERSION, CNPG_RELEASE_BRANCH,
# CNPG_VERSION_BARE, BARMAN_CLOUD_PLUGIN_VERSION (set by scripts/common.sh
# and demo/setup.sh).
# CNPG_VERSION_BARE, BARMAN_CLOUD_PLUGIN_VERSION, IMAGE_CATALOG_URL (set by
# scripts/common.sh and demo/setup.sh).
deploy_cnpg_requirements() {
local region="$1"
local context="$2"

if check_crd_existence "${context}" clusters.postgresql.cnpg.io; then
echo "ℹ️ CloudNativePG requirements already installed in region '${region}' (context: ${context});" \
"skipping operator/cert-manager/Barman Cloud Plugin installation."
"skipping operator/cert-manager/Barman Cloud Plugin/ClusterImageCatalog installation."
return
fi

Expand Down Expand Up @@ -71,6 +73,12 @@ deploy_cnpg_requirements() {
echo "📦 CloudNativePG: $(kubectl --context "${context}" get deployment cnpg-controller-manager \
-n cnpg-system -o jsonpath='{.spec.template.spec.containers[0].image}')"

# Deploy the ClusterImageCatalog with common extensions (requires the
# postgresql.cnpg.io CRDs installed by the operator above)
kubectl apply --context "${context}" -f "${IMAGE_CATALOG_URL}"
echo "📦 ClusterImageCatalog: $(kubectl --context "${context}" get clusterimagecatalog \
-o jsonpath='{.items[*].metadata.name}')"

# Deploy cert-manager
kubectl apply --context "${context}" -f \
"https://github.com/cert-manager/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.yaml"
Expand Down
40 changes: 30 additions & 10 deletions demo/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,26 @@ kube_config_path="${KUBE_CONFIG_PATH}"
templates_dir="${TEMPLATES_DIR:-${REPO_ROOT}/demo/templates}"
legacy_templates_dir="${templates_dir}/legacy"

# Default PostgreSQL image (overridable via env var)
POSTGRESQL_IMAGE="${POSTGRESQL_IMAGE:-ghcr.io/cloudnative-pg/postgresql:18-standard-trixie}"
# Default PostgreSQL major version for plugin mode (selects the entry in
# IMAGE_CATALOG_NAME's ClusterImageCatalog). Legacy mode still selects a
# full image name directly, since the catalog only ships minimal images.
POSTGRESQL_VERSION="${POSTGRESQL_VERSION:-18}"
POSTGRESQL_LEGACY_IMAGE="${POSTGRESQL_LEGACY_IMAGE:-ghcr.io/cloudnative-pg/postgresql:18-system-trixie}"

# Template file overrides — set any of these to replace the corresponding built-in fragment
tmpl_cluster="${CLUSTER_TEMPLATE:-${templates_dir}/cluster.yaml}"
tmpl_storage="${STORAGE_TEMPLATE:-${templates_dir}/storage.yaml}"
tmpl_bootstrap_initdb="${BOOTSTRAP_INITDB_TEMPLATE:-${templates_dir}/bootstrap-initdb.yaml}"
tmpl_bootstrap_recovery="${BOOTSTRAP_RECOVERY_TEMPLATE:-${templates_dir}/bootstrap-recovery.yaml}"
tmpl_image_catalog="${IMAGE_CATALOG_TEMPLATE:-${templates_dir}/image-catalog.yaml}"
tmpl_cluster_plugin_params="${CLUSTER_PLUGIN_PARAMS_TEMPLATE:-${templates_dir}/cluster-plugin-params.yaml}"
tmpl_replica_section="${REPLICA_SECTION_TEMPLATE:-${templates_dir}/replica-section.yaml}"
tmpl_external_cluster_plugin="${EXTERNAL_CLUSTER_PLUGIN_TEMPLATE:-${templates_dir}/external-cluster-plugin.yaml}"
tmpl_scheduledbackup_plugin="${SCHEDULEDBACKUP_PLUGIN_TEMPLATE:-${templates_dir}/scheduledbackup-plugin.yaml}"
tmpl_objectstore="${OBJECTSTORE_TEMPLATE:-${templates_dir}/objectstore.yaml}"
tmpl_podmonitor="${PODMONITOR_TEMPLATE:-${templates_dir}/podmonitor.yaml}"
tmpl_cluster_legacy_params="${CLUSTER_LEGACY_PARAMS_TEMPLATE:-${legacy_templates_dir}/cluster-legacy-params.yaml}"
tmpl_image_legacy="${IMAGE_LEGACY_TEMPLATE:-${legacy_templates_dir}/image-legacy.yaml}"
tmpl_external_cluster_legacy="${EXTERNAL_CLUSTER_LEGACY_TEMPLATE:-${legacy_templates_dir}/external-cluster-legacy.yaml}"
tmpl_scheduledbackup_legacy="${SCHEDULEDBACKUP_LEGACY_TEMPLATE:-${legacy_templates_dir}/scheduledbackup-legacy.yaml}"

Expand Down Expand Up @@ -156,9 +161,10 @@ kubectl_apply() {
# ---------------------------------------------------------------------------
# YAML generators — each function writes a complete YAML stream to stdout.
# The caller is responsible for piping to kubectl.
# Template files use ${REGION}, ${PRIMARY_REGION}, ${SOURCE_REGION}, and
# ${POSTGRESQL_IMAGE} as placeholders; envsubst substitutes only those
# variables (explicit list prevents accidental expansion of env vars).
# Template files use ${REGION}, ${PRIMARY_REGION}, ${SOURCE_REGION},
# ${IMAGE_CATALOG_NAME}, ${POSTGRESQL_VERSION}, and ${POSTGRESQL_LEGACY_IMAGE}
# as placeholders; envsubst substitutes only those variables (explicit list
# prevents accidental expansion of env vars).
# ---------------------------------------------------------------------------

generate_objectstore_yaml() {
Expand All @@ -179,9 +185,16 @@ generate_cluster_yaml_plugin() {
local source_region
source_region=$(get_source_region "${region}")

# Cluster header: apiVersion through affinity
REGION="${region}" POSTGRESQL_IMAGE="${POSTGRESQL_IMAGE}" \
envsubst '${REGION} ${POSTGRESQL_IMAGE}' < "${tmpl_cluster}"
# Cluster header: apiVersion through postgresql parameters
REGION="${region}" \
envsubst '${REGION}' < "${tmpl_cluster}"

# Storage (data + WAL volumes)
cat "${tmpl_storage}"

# ClusterImageCatalog reference (see demo/funcs_requirements.sh for the catalog itself)
IMAGE_CATALOG_NAME="${IMAGE_CATALOG_NAME}" POSTGRESQL_VERSION="${POSTGRESQL_VERSION}" \
envsubst '${IMAGE_CATALOG_NAME} ${POSTGRESQL_VERSION}' < "${tmpl_image_catalog}"

# Bootstrap: initdb for the primary (or single-region); recovery for replicas
if [ "${region}" = "${primary_region}" ] || [ "${num_regions}" -eq 1 ]; then
Expand Down Expand Up @@ -219,8 +232,15 @@ generate_cluster_yaml_legacy() {
local source_region
source_region=$(get_source_region "${region}")

REGION="${region}" POSTGRESQL_IMAGE="${POSTGRESQL_LEGACY_IMAGE}" \
envsubst '${REGION} ${POSTGRESQL_IMAGE}' < "${tmpl_cluster}"
REGION="${region}" \
envsubst '${REGION}' < "${tmpl_cluster}"

# Storage (data + WAL volumes)
cat "${tmpl_storage}"

# Direct image reference — no catalog available for legacy/system images
POSTGRESQL_LEGACY_IMAGE="${POSTGRESQL_LEGACY_IMAGE}" \
envsubst '${POSTGRESQL_LEGACY_IMAGE}' < "${tmpl_image_legacy}"

if [ "${region}" = "${primary_region}" ] || [ "${num_regions}" -eq 1 ]; then
cat "${tmpl_bootstrap_initdb}"
Expand Down
4 changes: 4 additions & 0 deletions demo/teardown.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ for region in "${REGIONS[@]}"; do
kubectl delete --context "${CONTEXT_NAME}" --ignore-not-found=true -f \
"https://github.com/cert-manager/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.yaml"

# Delete the ClusterImageCatalog
kubectl delete --context "${CONTEXT_NAME}" --ignore-not-found=true -f \
"${IMAGE_CATALOG_URL}"

# Delete CNPG operator
kubectl delete --context "${CONTEXT_NAME}" --ignore-not-found=true -f \
"https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-${CNPG_RELEASE_BRANCH}/releases/cnpg-${CNPG_VERSION_BARE}.yaml"
Expand Down
8 changes: 0 additions & 8 deletions demo/templates/cluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ metadata:
name: pg-${REGION}
spec:
instances: 3
imageName: ${POSTGRESQL_IMAGE}

storage:
size: 1Gi

# See https://cloudnative-pg.io/documentation/current/storage/#volume-for-wal
walStorage:
size: 1Gi

# See https://cloudnative-pg.io/documentation/current/scheduling/#isolating-postgresql-workloads
affinity:
Expand Down
7 changes: 7 additions & 0 deletions demo/templates/image-catalog.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# See https://cloudnative-pg.io/documentation/current/image_catalog/
imageCatalogRef:
apiGroup: postgresql.cnpg.io
kind: ClusterImageCatalog
name: ${IMAGE_CATALOG_NAME}
major: ${POSTGRESQL_VERSION}

2 changes: 2 additions & 0 deletions demo/templates/legacy/image-legacy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
imageName: ${POSTGRESQL_LEGACY_IMAGE}

7 changes: 7 additions & 0 deletions demo/templates/storage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
storage:
size: 1Gi

# See https://cloudnative-pg.io/documentation/current/storage/#volume-for-wal
walStorage:
size: 1Gi

7 changes: 7 additions & 0 deletions scripts/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,12 @@ CNPG_RELEASE_BRANCH="${CNPG_VERSION_BARE%.*}"
BARMAN_CLOUD_PLUGIN_VERSION="${BARMAN_CLOUD_PLUGIN_VERSION:-v0.13.0}"
# renovate: datasource=github-releases depName=grafana/grafana-operator
GRAFANA_OPERATOR_VERSION="${GRAFANA_OPERATOR_VERSION:-v5.24.0}"
# ClusterImageCatalog with common extensions (pg-crash, pgaudit, pgvector,
# postgis, timescaledb-oss, wal2json); images are pinned by digest within the
# file, so tracking the `main` branch is safe. See
# https://github.com/cloudnative-pg/artifacts/tree/main/image-catalogs-extensions
IMAGE_CATALOG_URL="${IMAGE_CATALOG_URL:-https://raw.githubusercontent.com/cloudnative-pg/artifacts/refs/heads/main/image-catalogs-extensions/catalog-minimal-trixie.yaml}"
# Must match metadata.name in IMAGE_CATALOG_URL; referenced by Cluster.spec.imageCatalogRef
IMAGE_CATALOG_NAME="${IMAGE_CATALOG_NAME:-postgresql-minimal-trixie}"

source "${REPO_ROOT}/scripts/funcs_regions.sh"