set fallback := true export CNPG_NAMESPACE := env("CNPG_NAMESPACE", "postgres") export CNPG_CHART_VERSION := env("CNPG_CHART_VERSION", "0.26.0") export CNPG_CLUSTER_CHART_VERSION := env("CNPG_CLUSTER_CHART_VERSION", "0.3.1") export K8S_VAULT_NAMESPACE := env("K8S_VAULT_NAMESPACE", "vault") export EXTERNAL_SECRETS_NAMESPACE := env("EXTERNAL_SECRETS_NAMESPACE", "external-secrets") [private] default: @just --list --unsorted --list-submodules # Add Helm repository add-helm-repo: @helm repo add cnpg https://cloudnative-pg.github.io/charts @helm repo update # Remove Helm repository remove-helm-repo: @helm repo remove cnpg # Install CloudNativePG and create a cluster install: @just install-cnpg @just create-cluster # Uninstall CloudNativePG and delete the cluster uninstall: @just delete-cluster @just uninstall-cnpg # Install CloudNativePG install-cnpg: @just add-helm-repo @helm upgrade --cleanup-on-fail --install cnpg cnpg/cloudnative-pg \ --version ${CNPG_CHART_VERSION} \ -n ${CNPG_NAMESPACE} --create-namespace --wait # Uninstall CloudNativePG uninstall-cnpg: @helm uninstall cnpg -n ${CNPG_NAMESPACE} --wait @kubectl delete namespace ${CNPG_NAMESPACE} --ignore-not-found # Create Postgres cluster create-cluster: #!/bin/bash set -euo pipefail if helm status external-secrets -n ${EXTERNAL_SECRETS_NAMESPACE} &>/dev/null; then echo "External Secrets Operator detected. Creating admin credentials via ExternalSecret..." password=$(just utils::random-password) just vault::put-root postgres/admin username=postgres password="${password}" kubectl delete externalsecret postgres-cluster-superuser -n ${CNPG_NAMESPACE} --ignore-not-found gomplate -f postgres-superuser-external-secret.gomplate.yaml | kubectl apply -f - echo "Waiting for ExternalSecret to sync..." kubectl wait --for=condition=Ready externalsecret/postgres-cluster-superuser \ -n ${CNPG_NAMESPACE} --timeout=60s else echo "External Secrets Operator not found. Creating superuser secret directly..." password=$(just utils::random-password) kubectl delete secret postgres-cluster-superuser -n ${CNPG_NAMESPACE} --ignore-not-found kubectl create secret generic postgres-cluster-superuser -n ${CNPG_NAMESPACE} \ --from-literal=username=postgres \ --from-literal=password="${password}" if helm status vault -n ${K8S_VAULT_NAMESPACE} &>/dev/null; then just vault::put-root postgres/admin username=postgres password="${password}" fi fi helm upgrade --install postgres-cluster cnpg/cluster \ --version ${CNPG_CLUSTER_CHART_VERSION} \ -n ${CNPG_NAMESPACE} --wait -f postgres-cluster-values.yaml echo "Waiting for PostgreSQL cluster to be ready..." kubectl wait --for=condition=Ready clusters.postgresql.cnpg.io/postgres-cluster \ -n ${CNPG_NAMESPACE} --timeout=300s # Delete Postgres cluster delete-cluster: @helm uninstall postgres-cluster -n ${CNPG_NAMESPACE} --ignore-not-found --wait @kubectl delete externalsecret postgres-cluster-superuser -n ${CNPG_NAMESPACE} --ignore-not-found @kubectl delete secret postgres-cluster-superuser -n ${CNPG_NAMESPACE} --ignore-not-found # Print Postgres username admin-username: @echo "postgres" # Print Postgres password admin-password: @kubectl get -n ${CNPG_NAMESPACE} secret postgres-cluster-superuser \ -o jsonpath="{.data.password}" | base64 --decode @echo # Create Postgres database create-db db_name='': #!/bin/bash set -euo pipefail DB_NAME=${DB_NAME:-{{ db_name }}} while [ -z "${DB_NAME}" ]; do DB_NAME=$(gum input --prompt="Database name: " --width=100) done if just db-exists ${DB_NAME} &>/dev/null; then echo "Database ${DB_NAME} already exists" >&2 exit fi echo "Creating database ${DB_NAME}..." just psql -c "\"CREATE DATABASE ${DB_NAME};\"" echo "Database ${DB_NAME} created." # Delete Postgres database delete-db db_name='': #!/bin/bash set -euo pipefail DB_NAME=${DB_NAME:-{{ db_name }}} if ! just db-exists ${DB_NAME} &>/dev/null; then echo "Database ${DB_NAME} does not exist." >&2 exit fi # Terminate all connections to the database just psql -c "\"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${DB_NAME}' AND pid <> pg_backend_pid();\"" # Force disconnect if needed just psql -c "\"UPDATE pg_database SET datallowconn = false WHERE datname = '${DB_NAME}';\"" just psql -c "\"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${DB_NAME}';\"" just psql -c "\"DROP DATABASE ${DB_NAME};\"" echo "Database ${DB_NAME} deleted." # Check if database exists [no-exit-message] db-exists db_name='': #!/bin/bash set -euo pipefail DB_NAME=${DB_NAME:-{{ db_name }}} while [ -z "${DB_NAME}" ]; do DB_NAME=$(gum input --prompt="Database name: " --width=100) done if echo '\l' | just postgres::psql | grep -E "^ *${DB_NAME} *\|" &>/dev/null; then echo "Database ${DB_NAME} exists." else echo "Database ${DB_NAME} does not exist." >&2 exit 1 fi # Create Postgres user create-user username='' password='': #!/bin/bash set -euo pipefail USERNAME=${USERNAME:-"{{ username }}"} PASSWORD=${PASSWORD:-"{{ password }}"} while [ -z "${USERNAME}" ]; do USERNAME=$(gum input --prompt="Username: " --width=100) done if just user-exists ${USERNAME} &>/dev/null; then echo "User ${USERNAME} already exists" >&2 exit fi if [ -z "${PASSWORD}" ]; then PASSWORD=$(gum input --prompt="Password: " --password --width=100 \ --placeholder="Empty to generate a random password") fi if [ -z "${PASSWORD}" ]; then PASSWORD=$(just random-password) echo "Generated random password: ${PASSWORD}" fi just psql -c "\"CREATE USER ${USERNAME} WITH LOGIN PASSWORD '${PASSWORD}';\"" echo "User ${USERNAME} created." # Delete Postgres user delete-user username='': #!/bin/bash set -euo pipefail USERNAME=${USERNAME:-"{{ username }}"} if ! just user-exists ${USERNAME} &>/dev/null; then echo "User ${USERNAME} does not exist." >&2 exit fi just psql -c "\"ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM ${USERNAME};\"" just psql -c "\"ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM ${USERNAME};\"" just psql -c "\"ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM ${USERNAME};\"" just psql -c "\"ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TYPES FROM ${USERNAME};\"" just psql -c "\"ALTER SCHEMA public OWNER TO postgres;\"" just psql -c "\"DROP USER ${USERNAME};\"" echo "User ${USERNAME} deleted." # Check if user exists [no-exit-message] user-exists username='': #!/bin/bash set -euo pipefail USERNAME=${USERNAME:-"{{ username }}"} while [ -z "${USERNAME}" ]; do USERNAME=$(gum input --prompt="Username: " --width=100) done if echo '\du' | just postgres::psql | grep -E "^ *${USERNAME} *\|" &>/dev/null; then echo "User ${USERNAME} exists." else echo "User ${USERNAME} does not exist." >&2 exit 1 fi # Grant all privileges on database to user grant db_name='' username='': #!/bin/bash set -euo pipefail DB_NAME=${DB_NAME:-"{{ db_name }}"} USERNAME=${USERNAME:-"{{ username }}"} while [ -z "${DB_NAME}" ]; do DB_NAME=$(gum input --prompt="Database name: " --width=100) done while [ -z "${USERNAME}" ]; do USERNAME=$(gum input --prompt="Username: " --width=100) done if ! just psql ${DB_NAME} -U postgres -P pager=off -c "\"SELECT 1;\""; then echo "Database ${DB_NAME} does not exist." >&2 exit 1 fi just psql -c "\"GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${USERNAME};\"" # Grant CREATE permission on public schema (needed for PostgreSQL 15+) just psql -d ${DB_NAME} -c "\"GRANT CREATE ON SCHEMA public TO ${USERNAME};\"" echo "Privileges granted." # Revoke all privileges on database from user revoke db_name='' username='': #!/bin/bash set -euo pipefail DB_NAME=${DB_NAME:-"{{ db_name }}"} USERNAME=${USERNAME:-"{{ username }}"} while [ -z "${DB_NAME}" ]; do DB_NAME=$(gum input --prompt="Database name: " --width=100) done while [ -z "${USERNAME}" ]; do USERNAME=$(gum input --prompt="Username: " --width=100) done if ! just psql -U postgres ${DB_NAME} -P pager=off -c "\"SELECT 1;\""; then echo "Database ${DB_NAME} does not exist." >&2 exit 1 fi just psql -c "\"REVOKE ALL PRIVILEGES ON DATABASE ${DB_NAME} FROM ${USERNAME};\"" echo "Privileges revoked." # Create Postgres database and user create-user-and-db username='' db_name='' password='': just create-db "{{ db_name }}" just create-user "{{ username }}" "{{ password }}" just grant "{{ db_name }}" "{{ username }}" # Delete Postgres database and user delete-user-and-db username='' db_name='': #!/bin/bash set -euo pipefail DB_NAME=${DB_NAME:-"{{ db_name }}"} USERNAME=${USERNAME:-"{{ username }}"} if just db-exists ${DB_NAME} &>/dev/null; then if just user-exists ${USERNAME} &>/dev/null; then just revoke "${DB_NAME}" "${USERNAME}" else echo "User ${USERNAME} does not exist, skipping revoke." fi just delete-db "${DB_NAME}" else echo "Database ${DB_NAME} does not exist, skipping database deletion." fi if just user-exists ${USERNAME} &>/dev/null; then just delete-user "${USERNAME}" else echo "User ${USERNAME} does not exist, skipping user deletion." fi echo "Cleanup completed." # Run psql [no-exit-message] psql *args='': @kubectl exec -it -n postgres postgres-cluster-1 -c postgres -- psql {{ args }} # Dump Postgres database by pg_dump dump db_name file: kubectl exec -it -n ${CNPG_NAMESPACE} postgres-cluster-1 -c postgres -- bash -c \ "pg_dump -d postgresql://postgres:$(just password)@localhost/{{ db_name }} -Fc > \ /var/lib/postgresql/data/db.dump" kubectl cp -n ${CNPG_NAMESPACE} -c postgres \ postgres-cluster-1:/var/lib/postgresql/data/db.dump {{ file }} kubectl exec -it -n ${CNPG_NAMESPACE} postgres-cluster-1 -c postgres -- rm /var/lib/postgresql/data/db.dump # Restore Postgres database by pg_restore restore db_name file: just create-db {{ db_name }} kubectl cp {{ file }} -n ${CNPG_NAMESPACE} -c postgres \ postgres-cluster-1:/var/lib/postgresql/data/db.dump kubectl exec -it -n ${CNPG_NAMESPACE} postgres-cluster-1 -c postgres -- bash -c \ "pg_restore -d postgresql://postgres:$(just password)@localhost/{{ db_name }} \ /var/lib/postgresql/data/db.dump"