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
38 changes: 38 additions & 0 deletions .github/workflows/test-backup-database.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Test backup-database

on:
push:
branches:
- main
paths:
- gh-actions/infra/backup-database/**
- .github/workflows/test-backup-database.yaml
pull_request:
paths:
- gh-actions/infra/backup-database/**
- .github/workflows/test-backup-database.yaml

jobs:
check-python-syntax:
name: py_compile
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7

- name: Syntax check all Python files
run: |
while IFS= read -r file; do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always have to double check that scripts are running with set -e by default, but it does :)

echo "Checking $file"
python3 -m py_compile "$file"
done < <(find gh-actions/infra/backup-database -type f -name '*.py' -print0 | sort -z | tr '\0' '\n')

unit-tests:
name: Python unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7

- name: Run unit tests
run: python3 -m unittest discover -s gh-actions/infra/backup-database/tests -p 'test_*.py' -v
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.vscode
.idea
.DS_Store

# Python
__pycache__/
59 changes: 59 additions & 0 deletions gh-actions/infra/backup-database/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Database backup action

This composite GitHub Action selects a healthy unit of a Juju application and
runs a database charm backup action on it. It requires an already authenticated
Juju session and reports the result in the GitHub job summary.

The [jaas-auth](../jaas-auth/action.yml) action can be used to install Juju and
authenticate to JAAS beforehand.

## Inputs

| Input | Required | Default | Description |
| ------------- | -------- | --------------- | -------------------------------------------------- |
| `model` | yes | | Juju model name |
| `model-owner` | no | | Juju model owner, for models owned by another user |
| `application` | yes | | Application whose units will be considered |
| `action` | no | `create-backup` | Juju action name |
| `parameters` | no | `{}` | JSON object containing action parameters |
| `unit-role` | no | `non-primary` | `non-primary`, `primary`, or `any` |
| `timeout` | no | `6h` | Duration passed to Juju's `--wait`, e.g. `30m` |
| `dry-run` | no | `false` | Skip backup creation but still list backups |

Only `model` and `application` are strictly required. `model-owner` is only
needed when targeting a model owned by another user.

## Unit selection

Units with an unhealthy workload (`blocked` or `error`) or agent (`error` or
`lost`) status are excluded from selection. The unit with the workload status
message `Primary` is treated as the primary. With `non-primary`, the first
eligible non-primary unit is selected, falling back to the primary with a
degraded warning when no eligible replica remains. `any` skips role discovery.
Excluded units and degraded fallbacks are noted in the job summary.

## Usage

```yaml
steps:
- uses: actions/checkout@v7
- name: Authenticate to JAAS
id: jaas-auth
uses: canonical/desktop-engineering/gh-actions/infra/jaas-auth@main
with:
jaas-controller: ${{ vars.JUJU_CONTROLLER }}
jaas-controller-host: ${{ vars.JUJU_CONTROLLER_HOST }}
juju-client-id: ${{ secrets.JUJU_CLIENT_ID }}
juju-client-secret: ${{ secrets.JUJU_CLIENT_SECRET }}
- uses: canonical/desktop-engineering/gh-actions/infra/backup-database@main
env:
JUJU_DATA: ${{ steps.jaas-auth.outputs.juju-data }}
with:
model: example-model
model-owner: ${{ vars.JUJU_MODEL_OWNER }}
application: database
dry-run: "true"
```

Invoke the action once per backup target. Run with `dry-run: "true"` first to
validate model access and unit selection before enabling backup creation.
66 changes: 66 additions & 0 deletions gh-actions/infra/backup-database/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Database backup
description: Select a healthy Juju unit and run a database backup action

inputs:
model:
description: Juju model name
required: true
model-owner:
description: Juju model owner; only needed for models owned by another user
required: false
application:
description: Juju application to back up
required: true
action:
description: Juju backup action
required: false
default: create-backup
parameters:
description: JSON object containing Juju action parameters
required: false
default: "{}"
unit-role:
description: Unit role to select (non-primary, primary, or any)
required: false
default: non-primary
timeout:
description: Duration passed to Juju's --wait option, e.g. 30m or 6h
required: false
default: 6h
dry-run:
description: Validate model access and unit selection without running the action
required: false
default: "false"

runs:
using: composite
steps:
- name: Run database backup
id: database-backup
shell: bash
env:
MODEL: ${{ inputs.model }}
MODEL_OWNER: ${{ inputs.model-owner }}
APPLICATION: ${{ inputs.application }}
ACTION: ${{ inputs.action }}
PARAMETERS_JSON: ${{ inputs.parameters }}
UNIT_ROLE: ${{ inputs.unit-role }}
TIMEOUT: ${{ inputs.timeout }}
DRY_RUN: ${{ inputs.dry-run }}
run: python3 "${{ github.action_path }}/backup.py"

- name: Write job summary
if: always()
shell: bash
env:
BACKUP_RESULT: ${{ steps.database-backup.outputs.result }}
run: |
set -eo pipefail
[[ -n "${BACKUP_RESULT}" ]] || exit 0
{
echo "## Database backup"
echo
echo "| Operation | Target | Unit | Result | Notes |"
echo "| --------- | ------ | ---- | ------ | ----- |"
jq -Rr 'select(length > 0) | fromjson | [.operation, "`\(.target)`", (.unit // "—" | "`\(.)`"), .result, (.notes // "—")] | "| " + join(" | ") + " |"' <<< "${BACKUP_RESULT}"
} >> "${GITHUB_STEP_SUMMARY}"
Loading
Loading