diff --git a/readme.md b/readme.md index e11f425..b02af90 100644 --- a/readme.md +++ b/readme.md @@ -90,6 +90,59 @@ The following inputs can be used as `step.with` keys: | `timeout` | `5m` | The timeout for the Helm command | | `working-directory` | `.` | The working directory where the action commands will operate | +### Setup PostgreSQL Preview Schema + +Sets up a PostgreSQL preview schema by cloning a base schema. + +#### Example + +```yaml + - uses: aboutbits/github-actions-kubernetes/setup-postgres-preview-schema@v3 + with: + deployment-name: my-app + namespace: my-namespace + preview-number: ${{ github.event.number }} +``` + +#### Inputs + +The following inputs can be used as `step.with` keys: + +| Name | Required/Default | Description | +|-------------------|------------------|--------------------------------------------| +| `deployment-name` | required | Name of the deployment | +| `namespace` | required | Kubernetes namespace | +| `preview-number` | required | Preview number (PR number) | +| `secret-name` | `app-secrets` | Name of the secret containing PostgreSQL password | +| `postgres-image` | `postgres:18` | PostgreSQL image to use | +| `base-schema` | `main` | Base schema to copy from | + +### Teardown PostgreSQL Preview Schema + +Drops the PostgreSQL preview schema. + +#### Example + +```yaml + - uses: aboutbits/github-actions-kubernetes/teardown-postgres-preview-schema@v3 + with: + deployment-name: my-app + namespace: my-namespace + preview-number: ${{ github.event.number }} +``` + +#### Inputs + +The following inputs can be used as `step.with` keys: + +| Name | Required/Default | Description | +|-------------------|------------------|--------------------------------------------| +| `deployment-name` | required | Name of the deployment | +| `namespace` | required | Kubernetes namespace | +| `preview-number` | required | Preview number (PR number) | +| `secret-name` | `app-secrets` | Name of the secret containing PostgreSQL password | +| `postgres-image` | `postgres:18` | PostgreSQL image to use | + ## Build & Publish To build and publish the action, visit the GitHub Actions page of the repository and trigger the workflow "Release Package" manually. diff --git a/setup-postgres-preview-schema/action.yml b/setup-postgres-preview-schema/action.yml new file mode 100644 index 0000000..583ba41 --- /dev/null +++ b/setup-postgres-preview-schema/action.yml @@ -0,0 +1,74 @@ +name: 'Setup PostgreSQL Preview Schema' +description: 'Sets up a PostgreSQL preview schema by cloning a base schema' +inputs: + deployment-name: + description: 'Name of the deployment (used for ConfigMap name)' + required: true + namespace: + description: 'Kubernetes namespace' + required: true + preview-number: + description: 'Preview number (PR number)' + required: true + secret-name: + description: 'Name of the secret containing DB password' + required: false + default: 'app-secrets' + postgres-image: + description: 'PostgreSQL image to use' + required: false + default: 'postgres:18' + base-schema: + description: 'Base schema to copy from' + required: false + default: 'main' + +runs: + using: "composite" + steps: + - name: Prepare setup-schema job + env: + JOB_NAME: "prepare-schema-preview-${{ inputs.preview-number }}" + CONFIGMAP_NAME: "${{ inputs.deployment-name }}-environments" + SECRET_NAME: "${{ inputs.secret-name }}" + PREVIEW_SCHEMA_NAME: "preview_${{ inputs.preview-number }}" + POSTGRES_IMAGE: "${{ inputs.postgres-image }}" + BASE_SCHEMA: "${{ inputs.base-schema }}" + run: | + envsubst ' + $JOB_NAME + $CONFIGMAP_NAME + $SECRET_NAME + $PREVIEW_SCHEMA_NAME + $POSTGRES_IMAGE + $BASE_SCHEMA + ' < ${{ github.action_path }}/job-template.yml > job-setup-schema.yml + + echo "Prepared job manifest: job-setup-schema.yml" + shell: bash + + - name: Create setup-schema job + run: | + kubectl apply --namespace ${{ inputs.namespace }} -f job-setup-schema.yml + shell: bash + + - name: Wait for setup-schema job to complete + env: + JOB_NAME: "prepare-schema-preview-${{ inputs.preview-number }}" + NAMESPACE: "${{ inputs.namespace }}" + run: | + echo "Waiting for job $JOB_NAME in namespace $NAMESPACE..." + + if kubectl wait --namespace $NAMESPACE --for=condition=complete --timeout=60s job/$JOB_NAME; then + echo "Job finished with status: Complete" + kubectl logs job/$JOB_NAME --namespace $NAMESPACE + elif kubectl wait --namespace $NAMESPACE --for=condition=failed --timeout=1s job/$JOB_NAME; then + echo "Job finished with status: Failed" + kubectl logs job/$JOB_NAME --namespace $NAMESPACE + exit 1 + else + echo "Timeout waiting for job to complete." + kubectl logs job/$JOB_NAME --namespace $NAMESPACE || true + exit 1 + fi + shell: bash diff --git a/setup-postgres-preview-schema/job-template.yml b/setup-postgres-preview-schema/job-template.yml new file mode 100644 index 0000000..9715b9d --- /dev/null +++ b/setup-postgres-preview-schema/job-template.yml @@ -0,0 +1,86 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: ${JOB_NAME} + labels: + app.kubernetes.io/managed-by: github-actions + app.kubernetes.io/component: database-schema-setup +spec: + ttlSecondsAfterFinished: 60 + template: + spec: + restartPolicy: Never + containers: + - name: prepare-preview-schema + image: ${POSTGRES_IMAGE} + command: [ "bash", "-c" ] + args: + - | + set -e + + echo "### START $(date --iso-8601=seconds) ###" + + if psql -t -A -c "SELECT 1 FROM information_schema.schemata WHERE schema_name = '${PREVIEW_SCHEMA_NAME}'" | grep -q 1; then + echo "Schema ${PREVIEW_SCHEMA_NAME} already exists. Skipping setup." + echo "Script finished successfully!" + echo "### END $(date --iso-8601=seconds) ###" + exit 0 + fi + + echo "Dumping base schema ${BASE_SCHEMA}..." + pg_dump --schema=${BASE_SCHEMA} \ + --no-comments \ + --no-owner \ + --no-acl \ + --quote-all-identifiers \ + --format=plain \ + --file=preview-dump.sql + + echo "Modifying dump file..." + sed -i preview-dump.sql \ + -e "s/\"${BASE_SCHEMA}\"/\"${PREVIEW_SCHEMA_NAME}\"/g" \ + -e 's/SET /--SET /g' \ + -e 's/SELECT pg_catalog/--SELECT pg_catalog/g' \ + -e 's/--SELECT pg_catalog.setval/SELECT pg_catalog.setval/g' + + echo "Creating schema ${PREVIEW_SCHEMA_NAME} with modified dump file..." + psql --echo-queries \ + --echo-errors \ + --single-transaction \ + --set=ON_ERROR_STOP=1 \ + --set=SESSION_REPLICATION_ROLE=replica \ + --file=preview-dump.sql + + echo "Script finished successfully!" + echo "### END $(date --iso-8601=seconds) ###" + env: + - name: PGHOST + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_HOST + - name: PGPORT + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_PORT + - name: PGDATABASE + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_NAME + - name: PGSCHEMA + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_SCHEMA + - name: PGUSER + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_USERNAME + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: ${SECRET_NAME} + key: DB_PASSWORD diff --git a/teardown-postgres-preview-schema/action.yml b/teardown-postgres-preview-schema/action.yml new file mode 100644 index 0000000..b753b72 --- /dev/null +++ b/teardown-postgres-preview-schema/action.yml @@ -0,0 +1,68 @@ +name: 'Teardown PostgreSQL Preview Schema' +description: 'Drops the PostgreSQL preview schema' +inputs: + deployment-name: + description: 'Name of the deployment (used for ConfigMap name)' + required: true + namespace: + description: 'Kubernetes namespace' + required: true + preview-number: + description: 'Preview number (PR number)' + required: true + secret-name: + description: 'Name of the secret containing DB password' + required: false + default: 'app-secrets' + postgres-image: + description: 'PostgreSQL image to use' + required: false + default: 'postgres:18' + +runs: + using: "composite" + steps: + - name: Prepare teardown-schema job + env: + JOB_NAME: "drop-schema-preview-${{ inputs.preview-number }}" + CONFIGMAP_NAME: "${{ inputs.deployment-name }}-environments" + SECRET_NAME: "${{ inputs.secret-name }}" + PREVIEW_SCHEMA_NAME: "preview_${{ inputs.preview-number }}" + POSTGRES_IMAGE: "${{ inputs.postgres-image }}" + run: | + envsubst ' + $JOB_NAME + $CONFIGMAP_NAME + $SECRET_NAME + $PREVIEW_SCHEMA_NAME + $POSTGRES_IMAGE + ' < ${{ github.action_path }}/job-template.yml > job-teardown-schema.yml + + echo "Prepared job manifest: job-teardown-schema.yml" + shell: bash + + - name: Create teardown-schema job + run: | + kubectl apply --namespace ${{ inputs.namespace }} -f job-teardown-schema.yml + shell: bash + + - name: Wait for teardown-schema job to complete + env: + JOB_NAME: "drop-schema-preview-${{ inputs.preview-number }}" + NAMESPACE: "${{ inputs.namespace }}" + run: | + echo "Waiting for job $JOB_NAME in namespace $NAMESPACE..." + + if kubectl wait --namespace $NAMESPACE --for=condition=complete --timeout=60s job/$JOB_NAME; then + echo "Job finished with status: Complete" + kubectl logs job/$JOB_NAME --namespace $NAMESPACE + elif kubectl wait --namespace $NAMESPACE --for=condition=failed --timeout=1s job/$JOB_NAME; then + echo "Job finished with status: Failed" + kubectl logs job/$JOB_NAME --namespace $NAMESPACE + exit 1 + else + echo "Timeout waiting for job to complete." + kubectl logs job/$JOB_NAME --namespace $NAMESPACE || true + exit 1 + fi + shell: bash diff --git a/teardown-postgres-preview-schema/job-template.yml b/teardown-postgres-preview-schema/job-template.yml new file mode 100644 index 0000000..e61de85 --- /dev/null +++ b/teardown-postgres-preview-schema/job-template.yml @@ -0,0 +1,61 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: ${JOB_NAME} + labels: + app.kubernetes.io/managed-by: github-actions + app.kubernetes.io/component: database-schema-teardown +spec: + ttlSecondsAfterFinished: 60 + template: + spec: + restartPolicy: Never + containers: + - name: drop-preview-schema + image: ${POSTGRES_IMAGE} + command: [ "bash", "-c" ] + args: + - | + set -e + + echo "### START $(date --iso-8601=seconds) ###" + echo "Drop schema ${PREVIEW_SCHEMA_NAME}..." + psql --echo-queries \ + --echo-errors \ + --single-transaction \ + --set=ON_ERROR_STOP=1 \ + --command="drop schema if exists ${PREVIEW_SCHEMA_NAME} cascade;" + + echo "Script finished successfully!" + echo "### END $(date --iso-8601=seconds) ###" + env: + - name: PGHOST + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_HOST + - name: PGPORT + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_PORT + - name: PGDATABASE + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_NAME + - name: PGSCHEMA + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_SCHEMA + - name: PGUSER + valueFrom: + configMapKeyRef: + name: ${CONFIGMAP_NAME} + key: DB_USERNAME + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: ${SECRET_NAME} + key: DB_PASSWORD