Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
31 changes: 28 additions & 3 deletions .yamllint
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
---
# Yamllint configuration should be compatible with Ansible,
# see https://ansible.readthedocs.io/projects/lint/rules/yaml/#yamllint-configuration

extends: default

rules:
line-length: disable
comments:
# https://github.com/prettier/prettier/issues/6780
min-spaces-from-content: 1
# https://github.com/adrienverge/yamllint/issues/384
comments-indentation: false
document-start: disable
# 160 chars was the default used by old E204 rule, but
# you can easily change it or disable in your .yamllint file.
line-length:
max: 200
# We are adding an extra space inside braces as that's how prettier does it
# and we are trying not to fight other linters.
braces:
min-spaces-inside: 0 # yamllint defaults to 0
max-spaces-inside: 1 # yamllint defaults to 0
# key-duplicates:
# forbid-duplicated-merge-keys: true # not enabled by default
octal-values:
forbid-implicit-octal: true # yamllint defaults to false
forbid-explicit-octal: true # yamllint defaults to false
# quoted-strings:
# quote-type: double
# required: only-when-needed


ignore: |
venv/
.roles/
.cache/
.github/
venv/
11 changes: 11 additions & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,14 @@ database_root_user: root
opencast_mariadb_max_connections: 512
# set buffer pool size to ~80% of total memory if db runs on its own host
opencast_mariadb_innodb_buffer_pool_size: "{{ ((ansible_memtotal_mb / 1024) * 0.8) | int }}G"


# === Database backup feature (enabled) ===
database_backup_enabled: false
database_backup_output_path: None
database_backup_schedule: "*-*-* 05:00:00" # Systemd OnCalendar format
database_backup_keep: 7
database_backup_dbs: [] # list of databases
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not put database_name to the default DBs list?

Suggested change
database_backup_dbs: [] # list of databases
# list of databases
database_backup_dbs:
- "{{ database_name }}"

database_backup_owner: backup
database_backup_group: backup
database_backup_user: backup
3 changes: 2 additions & 1 deletion molecule/default/converge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
vars:
database_password: "1234"
database_root_password: "5678"
database_backup_user_password: "5432"
tasks:
- name: Include opencast_mariadb
ansible.builtin.include_role:
name: opencast_mariadb
name: elan.opencast_mariadb
56 changes: 56 additions & 0 deletions molecule/default/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
hosts: all
vars:
database_password: "1234"
vars_files:
- ../../defaults/main.yml
tasks:
- name: Check MariaDB socket exists
ansible.builtin.wait_for:
Expand Down Expand Up @@ -42,3 +44,57 @@
ansible.builtin.fail:
msg: Opencast database does not exist
when: "query_databases.rowcount.0 != 1"

# ───────────────────────────────────────────────────────────
# Backup configuration (only when enabled)
# ───────────────────────────────────────────────────────────

- name: Verify backup configuration when enabled
when: database_backup_enabled | default(false)
block:
- name: Assert backups are enabled
ansible.builtin.assert:
that:
- database_backup_enabled | default(false)
fail_msg: "Backups are disabled; skipping backup verification."

- name: Ensure backup directory exists
ansible.builtin.stat:
path: "{{ database_backup_output_path }}"
register: backup_dir_stat

- name: Assert backup directory is present and writable
ansible.builtin.assert:
that:
- backup_dir_stat.stat.exists
- backup_dir_stat.stat.isdir
fail_msg: >
Backup directory {{ database_backup_output_path }}
is missing or not a directory.
- name: Check database-backup.service is installed and enabled
ansible.builtin.systemd:
name: database-backup.service
enabled: true
state: started

- name: Check database-backup.timer is installed and enabled
ansible.builtin.systemd:
name: database-backup.timer
enabled: true
state: started

- name: Slurp timer unit file for inspection
ansible.builtin.slurp:
path: /etc/systemd/system/database-backup.timer
register: timer_unit

- name: Assert OnCalendar line in timer unit matches schedule
ansible.builtin.assert:
that:
- "'OnCalendar={{ database_backup_schedule }}' in (timer_unit.content | b64decode)"
fail_msg: >
database-backup.timer does not contain
OnCalendar={{ database_backup_schedule }}.
success_msg: >
Timer unit file correctly contains
OnCalendar={{ database_backup_schedule }}.
71 changes: 71 additions & 0 deletions tasks/backup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
- name: Fail if backup enabled but no output path given
ansible.builtin.fail:
msg: "database_backup_output_path must be set when database_backup_enabled = true"
when:
- database_backup_enabled
- database_backup_output_path | length == 0

- name: Ensure backup OS user exists
ansible.builtin.user:
name: "{{ database_backup_owner }}"
state: present
shell: /usr/sbin/nologin
system: true

- name: Ensure MariaDB backup user exists for each database
community.mysql.mysql_user:
name: "{{ database_backup_user }}"
password: "{{ database_backup_user_password }}"
host: "localhost"
priv: "{{ item }}.*:SELECT,LOCK TABLES,SHOW VIEW,EVENT,TRIGGER"
state: present
login_user: "{{ database_root_user }}"
login_password: "{{ database_root_password }}"
loop: "{{ database_backup_dbs }}"
when: database_backup_enabled
no_log: true

- name: Ensure backup output directory exists
ansible.builtin.file:
path: "{{ database_backup_output_path }}"
state: directory
owner: "{{ database_backup_owner }}"
group: "{{ database_backup_group }}"
mode: "0750"
when: database_backup_enabled

- name: Install backup script
ansible.builtin.template:
src: database-backup.sh.j2
dest: "{{ database_backup_output_path }}/database-backup.sh"
owner: "{{ database_backup_owner }}"
group: "{{ database_backup_group }}"
mode: "0750"
when: database_backup_enabled

- name: Install systemd service unit
ansible.builtin.template:
src: database-backup.service.j2
dest: /etc/systemd/system/database-backup.service
mode: "0644"
when: database_backup_enabled

- name: Install systemd timer unit
ansible.builtin.template:
src: database-backup.timer.j2
dest: /etc/systemd/system/database-backup.timer
mode: "0644"
when: database_backup_enabled

- name: Reload systemd daemon (if timers changed)
ansible.builtin.systemd:
daemon_reload: true
when: database_backup_enabled

- name: Ensure backup timer is enabled and running
ansible.builtin.systemd:
name: database-backup.timer
enabled: true
state: started
when: database_backup_enabled
7 changes: 7 additions & 0 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,10 @@
- "localhost"
- "{{ ansible_fqdn }}"
no_log: true

###############################################################################
# database backup
###############################################################################
- name: Include backup setup tasks
ansible.builtin.include_tasks: backup.yml
when: database_backup_enabled
14 changes: 14 additions & 0 deletions templates/database-backup.service.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[Unit]
Description=Opencast Database Backup
After=network.target
After=local-fs.target
After=remote-fs.target

[Service]
Type=oneshot
User={{ database_backup_owner }}
Group={{ database_backup_group }}
ExecStart={{ database_backup_output_path }}/database-backup.sh

[Install]
WantedBy=multi-user.target
22 changes: 22 additions & 0 deletions templates/database-backup.sh.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

DBUSER="{{ database_backup_user }}"
DBPASS="{{ database_backup_user_password }}"
OUTDIR="{{ database_backup_output_path }}"
KEEP={{ database_backup_keep }}
DBS=({% for db in database_backup_dbs %}{{ db }} {% endfor %})
Copy link
Contributor

Choose a reason for hiding this comment

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

It's cleaner to use the join filter (see docs)

Suggested change
DBS=({% for db in database_backup_dbs %}{{ db }} {% endfor %})
DBS=({ database_backup_dbs | join(' ') })

TS=$(date +%Y%m%d-%H%M%S)

# Loop through each database name
for DB in "${DBS[@]}"; do
echo "Backing up $DB$OUTDIR/db-backup-${DB}-${TS}.dump.gz"
Copy link
Contributor

Choose a reason for hiding this comment

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

It's generally fine using unicode characters, replacing it with asci will avoid issues in some circumstances. It's up to you to accept this suggestion or not.

But please use .sql.gz file extension.

Suggested change
echo "Backing up $DB $OUTDIR/db-backup-${DB}-${TS}.dump.gz"
echo "Backing up $DB database to $OUTDIR/db-backup-${DB}-${TS}.sql.gz"


# Run pg_dump and compress into a .gz file
mysqldump -F c "$DBUSER" -p"$DBPASS" \
| gzip > "${OUTDIR}/db-backup-${DB}-${TS}.dump.gz"

# Remove older dumps, keep only the newest $KEEP
ls -1t "${OUTDIR}/db-backup-${DB}-"*.dump.gz \
| tail -n +$((KEEP + 1)) \
| xargs -r rm --
done
9 changes: 9 additions & 0 deletions templates/database-backup.timer.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Unit]
Description=Run database backup daily

[Timer]
OnCalendar={{ database_backup_schedule }}
Persistent=true

[Install]
WantedBy=timers.target