forgejo-autohebergement/ansible/playbooks/restore.yml
Horacio Duran c09bf58ea7 Backport fixes from version in production
Per file detail:
backup.yml
      - Added role defaults loading with proper precedence (inventory > vars > defaults)
      - Fixed pg_dump permissions: now dumps to /tmp first, then moves to backup directory
update.yml
      - Added role defaults loading with proper precedence
      - Fixed docker exec commands to use --user {{ forgejo_user }}
      - Added monitoring compose file detection and handling
restore.yml
      - Added role defaults loading with proper precedence
      - Added monitoring compose file detection and handling
      - Fixed docker exec for doctor command to use --user {{ forgejo_user }}
Makefile
       - Updated .PHONY with new targets
       - Replaced auto-generated help with structured categorized help
       - Added backup-cron and backup-cron-s3 targets for non-interactive backups
       - Added cron job example in help output
2026-01-15 22:26:27 +01:00

253 lines
8.3 KiB
YAML

---
# Restore Forgejo from backup
# Restores database, repositories, configuration, and data
- name: Restore Forgejo from Backup
hosts: forgejo
become: yes
gather_facts: yes
vars_files:
- vars/main.yml
- vars/secrets.yml
vars:
# Must be provided via --extra-vars
backup_timestamp: ""
backup_source: "local" # local or s3
force_restore: false
pre_tasks:
- name: Load role defaults as fallback
ansible.builtin.include_vars:
file: ../roles/forgejo/defaults/main.yml
name: role_defaults
- name: Apply all role defaults for undefined variables
ansible.builtin.set_fact:
"{{ item.key }}": "{{ vars[item.key] | default(item.value) }}"
loop: "{{ role_defaults | dict2items }}"
loop_control:
label: "{{ item.key }}"
- name: Validate backup timestamp
ansible.builtin.fail:
msg: "Please provide backup_timestamp via --extra-vars 'backup_timestamp=20240115T120000'"
when: backup_timestamp == ""
- name: Display restore information
ansible.builtin.debug:
msg: |
========================================
WARNING: This will restore Forgejo data
========================================
Backup timestamp: {{ backup_timestamp }}
Source: {{ backup_source }}
This operation will:
1. Stop Forgejo service
2. Restore database
3. Restore repositories
4. Restore configuration
5. Restart services
Current data will be backed up first.
- name: Confirm restore operation
ansible.builtin.pause:
prompt: "Type 'yes' to continue with restore"
register: restore_confirm
when: not force_restore
- name: Validate confirmation
ansible.builtin.fail:
msg: "Restore cancelled by user"
when: not force_restore and restore_confirm.user_input != 'yes'
tasks:
- name: Create pre-restore backup
ansible.builtin.include_tasks: backup.yml
vars:
backup_filename: "pre-restore-{{ ansible_date_time.iso8601_basic_short }}.tar.gz"
- name: Download backup from S3 if needed
when: backup_source == 's3'
block:
- name: Create temporary download directory
ansible.builtin.file:
path: "{{ forgejo_backup_path }}/restore-temp"
state: directory
mode: '0750'
- name: Download backups from S3
ansible.builtin.command:
cmd: >
aws s3 cp s3://{{ forgejo_backup_s3_bucket }}/backups/{{ item }}-{{ backup_timestamp }}.tar.gz
{{ forgejo_backup_path }}/{{ item }}-{{ backup_timestamp }}.tar.gz
--endpoint-url {{ forgejo_s3_endpoint }}
environment:
AWS_ACCESS_KEY_ID: "{{ forgejo_s3_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ forgejo_s3_secret_key }}"
loop:
- database
- repositories
- config
- data
no_log: yes
- name: Verify backup files exist
ansible.builtin.stat:
path: "{{ forgejo_backup_path }}/{{ item }}-{{ backup_timestamp }}.tar.gz"
register: backup_files
loop:
- repositories
- config
- data
failed_when: not backup_files.results | map(attribute='stat.exists') | list | min
- name: Verify database backup exists
ansible.builtin.stat:
path: "{{ forgejo_backup_path }}/database-{{ backup_timestamp }}.sql.gz"
register: db_backup
failed_when: not db_backup.stat.exists
- name: Check if monitoring compose file exists
ansible.builtin.stat:
path: "{{ forgejo_base_path }}/docker-compose.monitoring.yml"
register: monitoring_compose
- name: Stop Forgejo service
community.docker.docker_compose_v2:
project_src: "{{ forgejo_base_path }}"
files: "{{ ['docker-compose.yml', 'docker-compose.monitoring.yml'] if monitoring_compose.stat.exists else ['docker-compose.yml'] }}"
state: stopped
- name: Restore PostgreSQL database
when: forgejo_db_type == 'postgres'
block:
- name: Drop existing database
community.postgresql.postgresql_db:
name: "{{ forgejo_db_name }}"
state: absent
become_user: postgres
- name: Recreate database
community.postgresql.postgresql_db:
name: "{{ forgejo_db_name }}"
encoding: UTF8
lc_collate: en_US.UTF-8
lc_ctype: en_US.UTF-8
template: template0
state: present
become_user: postgres
- name: Decompress database backup
ansible.builtin.command:
cmd: gunzip -c {{ forgejo_backup_path }}/database-{{ backup_timestamp }}.sql.gz
register: db_dump
- name: Restore database
community.postgresql.postgresql_db:
name: "{{ forgejo_db_name }}"
state: restore
target: "{{ forgejo_backup_path }}/database-{{ backup_timestamp }}.sql"
become_user: postgres
- name: Clear existing repositories
ansible.builtin.file:
path: "{{ forgejo_data_path }}/git"
state: absent
- name: Restore repositories
ansible.builtin.unarchive:
src: "{{ forgejo_backup_path }}/repositories-{{ backup_timestamp }}.tar.gz"
dest: "{{ forgejo_data_path }}"
remote_src: yes
owner: "{{ forgejo_user }}"
group: "{{ forgejo_group }}"
- name: Restore configuration
ansible.builtin.unarchive:
src: "{{ forgejo_backup_path }}/config-{{ backup_timestamp }}.tar.gz"
dest: "{{ forgejo_base_path }}"
remote_src: yes
owner: "{{ forgejo_user }}"
group: "{{ forgejo_group }}"
- name: Restore data files
ansible.builtin.unarchive:
src: "{{ forgejo_backup_path }}/data-{{ backup_timestamp }}.tar.gz"
dest: "{{ forgejo_data_path }}"
remote_src: yes
owner: "{{ forgejo_user }}"
group: "{{ forgejo_group }}"
- name: Set correct permissions
ansible.builtin.file:
path: "{{ item }}"
owner: "{{ forgejo_user }}"
group: "{{ forgejo_group }}"
recurse: yes
loop:
- "{{ forgejo_data_path }}/git"
- "{{ forgejo_data_path }}/attachments"
- "{{ forgejo_data_path }}/lfs"
- "{{ forgejo_config_path }}"
- name: Start Forgejo service
community.docker.docker_compose_v2:
project_src: "{{ forgejo_base_path }}"
files: "{{ ['docker-compose.yml', 'docker-compose.monitoring.yml'] if monitoring_compose.stat.exists else ['docker-compose.yml'] }}"
state: present
- name: Wait for Forgejo to be ready
ansible.builtin.uri:
url: "http://localhost:{{ forgejo_http_port }}"
status_code: 200
register: health_check
until: health_check.status == 200
retries: 30
delay: 2
- name: Run integrity checks
ansible.builtin.command:
cmd: docker exec --user {{ forgejo_user }} forgejo forgejo doctor check --all
register: integrity_check
failed_when: false
- name: Display integrity check results
ansible.builtin.debug:
msg: "{{ integrity_check.stdout_lines }}"
post_tasks:
- name: Verify Forgejo health
ansible.builtin.uri:
url: "http://localhost:{{ forgejo_http_port }}/api/healthz"
status_code: 200
register: health
- name: Clean up temporary files
ansible.builtin.file:
path: "{{ forgejo_backup_path }}/restore-temp"
state: absent
when: backup_source == 's3'
- name: Display completion message
ansible.builtin.debug:
msg: |
========================================
Restore Complete!
========================================
Restored from backup: {{ backup_timestamp }}
Forgejo is now running with restored data.
Please verify:
1. Login works correctly
2. Repositories are accessible
3. All data is present
Original data was backed up before restore.
========================================