Skip to content

feat: pre/post/error/finally lifecycle hooks and ordered backup execution (#108)#109

Open
muebau wants to merge 1 commit intolawndoc:mainfrom
muebau:main
Open

feat: pre/post/error/finally lifecycle hooks and ordered backup execution (#108)#109
muebau wants to merge 1 commit intolawndoc:mainfrom
muebau:main

Conversation

@muebau
Copy link

@muebau muebau commented Mar 17, 2026

Closes #108

Summary

Adds support for user-defined lifecycle hooks and ordered backup execution,
as proposed in #108.

Hooks

Labels on any container define commands that run around the backup process:

stack-back.hooks.<stage>.<N>.cmd        # command to run
stack-back.hooks.<stage>.<N>.context    # service to exec into (optional)
stack-back.hooks.<stage>.<N>.on-error   # "abort" (default) | "continue"

Stages follow try/catch/finally semantics:

Stage Runs when
pre Before backup starts
post Only if all pre hooks + backup succeeded
error Only on failure
finally Always, regardless of outcome

Context resolution: When context is omitted, the hook runs in the
container where the label is defined — backup-container hooks run locally,
target-container hooks run via docker exec. An explicit context overrides
this and execs into the named service.

Symmetric ordering: Pre hooks run backup-container first, then targets
(setup). Post/error/finally hooks reverse this: targets first, then
backup-container (teardown).

Ordered backup

Labels on the backup container control execution order:

stack-back.ordered: true
stack-back.order.1: "nextcloud-app"
stack-back.order.2: "nextcloud-database"

Containers not listed are appended after the ordered ones. Duplicates,
unknown services, and gaps in numbering are handled with warnings.

Lifecycle change

The backup lifecycle in start_backup_process() was restructured so that
stopped containers are always restarted before any post/error/finally
hooks execute. This ensures hooks can exec into previously-stopped services.

pre hooks → stop containers → backup volumes & DB dumps → restart containers →
post hooks (on success) / error hooks (on failure) → finally hooks (always)

Changed files

File Change
hooks.py New — hook parsing, collection, ordering, execution
cli.py Lifecycle restructured with try/finally, hook integration, label forwarding
containers.py Ordered backup via _parse_backup_order()
enums.py LABEL_ORDERED, LABEL_ORDER_PREFIX constants
test_hooks.py New — hook parsing, ordering, execution, context resolution
test_backup_lifecycle.py New — operation sequencing with mocked hooks
test_end_to_end_lifecycle.py New — full pipeline with real objects, I/O mocked

…doc#108)

Add user-defined hook commands that run around the backup process,
following try/catch/finally semantics:

  - pre:     runs before backup starts
  - post:    runs only if pre hooks + backup both succeeded
  - error:   runs only on failure
  - finally: always runs regardless of outcome

Labels on any container:
  stack-back.hooks.<stage>.<N>.cmd        command to run
  stack-back.hooks.<stage>.<N>.context    service to exec into (optional)
  stack-back.hooks.<stage>.<N>.on-error   "abort" (default) | "continue"

When context is omitted, the hook runs in the container where the label
is defined: backup-container hooks run locally via sh -c, target-
container hooks run via docker exec into that target container.
An explicit context overrides this, exec-ing into the named service.

For pre hooks (setup), backup-container hooks execute first, then
target-container hooks.  For post/error/finally hooks (teardown), the
order is reversed: target-container hooks first, then backup-container
hooks — giving symmetric stack-like semantics.

The backup lifecycle in start_backup_process() is restructured so that
stopped containers are always restarted before any post/error/finally
hooks execute, ensuring hooks can exec into previously-stopped services.

Also adds ordered backup execution via labels on the backup container:
  stack-back.ordered: true
  stack-back.order.<N>: "service-name"

Containers are backed up in the defined sequence.  Containers not listed
are appended after the ordered ones.  Duplicate, unknown, and missing
entries are handled with warnings.

Hook and ordering labels are forwarded to the spawned backup process
container so the inner process can read them.

New modules:
  - hooks.py: hook parsing, collection, ordering, and execution

Changed modules:
  - cli.py: lifecycle restructured with try/finally, hook integration,
    label forwarding, hook summary in status output
  - containers.py: ordered backup via _parse_backup_order()
  - enums.py: LABEL_ORDERED, LABEL_ORDER_PREFIX constants
  - fixtures.py: forward "env" field in test container definitions

New test files:
  - test_hooks.py: hook parsing, collection ordering, execution,
    context resolution, abort/continue, backup ordering
  - test_backup_lifecycle.py: operation sequencing with mocked hooks
  - test_end_to_end_lifecycle.py: full pipeline with real Container
    and Hook objects, mocked only at the I/O boundary
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Pre/Post-Backup Hooks and Ordered Backup Execution

1 participant