Files
michaelschiemer/deployment/ansible/playbooks/rollback.yml

167 lines
5.8 KiB
YAML

---
- name: Rollback Application Deployment
hosts: production
gather_facts: yes
become: no
vars:
rollback_to_version: "{{ rollback_to_version | default('previous') }}"
app_stack_path: "{{ deploy_user_home }}/deployment/stacks/application"
pre_tasks:
- name: Optionally load registry credentials from encrypted vault
include_vars:
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
no_log: yes
ignore_errors: yes
delegate_to: localhost
become: no
- name: Derive docker registry credentials from vault when not provided
set_fact:
docker_registry_username: "{{ docker_registry_username | default(vault_docker_registry_username | default(omit)) }}"
docker_registry_password: "{{ docker_registry_password | default(vault_docker_registry_password | default(omit)) }}"
- name: Check Docker service
systemd:
name: docker
state: started
register: docker_service
become: yes
- name: Fail if Docker is not running
fail:
msg: "Docker service is not running - cannot perform rollback"
when: docker_service.status.ActiveState != 'active'
- name: Get list of available backups
find:
paths: "{{ backups_path }}"
file_type: directory
register: available_backups
- name: Fail if no backups available
fail:
msg: "No backup versions available for rollback"
when: available_backups.matched == 0
- name: Sort backups by date (newest first)
set_fact:
sorted_backups: "{{ available_backups.files | sort(attribute='mtime', reverse=true) }}"
tasks:
- name: Determine rollback target
set_fact:
rollback_backup: "{{ sorted_backups[1] if rollback_to_version == 'previous' else sorted_backups | selectattr('path', 'search', rollback_to_version) | first }}"
when: sorted_backups | length > 1
- name: Fail if rollback target not found
fail:
msg: "Cannot determine rollback target. Available backups: {{ sorted_backups | map(attribute='path') | list }}"
when: rollback_backup is not defined
- name: Load rollback metadata
slurp:
src: "{{ rollback_backup.path }}/deployment_metadata.txt"
register: rollback_metadata
ignore_errors: yes
- name: Parse rollback image from metadata
set_fact:
rollback_image: "{{ rollback_metadata.content | b64decode | regex_search('Deployed Image: ([^\\n]+)', '\\1') | first }}"
when: rollback_metadata is succeeded
- name: Alternative: Parse rollback image from docker-compose config backup
set_fact:
rollback_image: "{{ rollback_metadata.content | b64decode | regex_search('image:\\s+([^:]+):([^\\n]+)', '\\1:\\2') | first }}"
when:
- rollback_metadata is succeeded
- rollback_image is not defined
- name: Fail if cannot determine rollback image
fail:
msg: "Cannot determine image to rollback to from backup: {{ rollback_backup.path }}"
when: rollback_image is not defined or rollback_image == ''
- name: Display rollback information
debug:
msg:
- "Rolling back to previous version"
- "Backup: {{ rollback_backup.path }}"
- "Image: {{ rollback_image }}"
- name: Login to Docker registry (if credentials provided)
community.docker.docker_login:
registry_url: "{{ docker_registry_url }}"
username: "{{ docker_registry_username }}"
password: "{{ docker_registry_password }}"
no_log: yes
when:
- docker_registry_username is defined
- docker_registry_password is defined
- name: Pull rollback image
community.docker.docker_image:
name: "{{ rollback_image.split(':')[0] }}"
tag: "{{ rollback_image.split(':')[1] }}"
source: pull
force_source: yes
register: image_pull
- name: Update docker-compose.yml with rollback image
replace:
path: "{{ app_stack_path }}/docker-compose.yml"
regexp: '^(\s+image:\s+){{ app_image }}:.*$'
replace: '\1{{ rollback_image }}'
register: compose_updated
- name: Restart application stack with rollback image
community.docker.docker_compose_v2:
project_src: "{{ app_stack_path }}"
state: present
pull: always
recreate: always
remove_orphans: yes
register: stack_rollback
when: compose_updated.changed
- name: Wait for services to be healthy after rollback
wait_for:
timeout: 60
changed_when: false
- name: Get current running image
shell: |
docker compose -f {{ app_stack_path }}/docker-compose.yml config | grep -E "^\s+image:" | head -1 | awk '{print $2}' || echo "unknown"
args:
executable: /bin/bash
register: current_image
changed_when: false
- name: Record rollback event
copy:
content: |
Rollback Timestamp: {{ ansible_date_time.iso8601 }}
Rolled back from: {{ sorted_backups[0].path | basename }}
Rolled back to: {{ rollback_backup.path | basename }}
Rollback Image: {{ rollback_image }}
Current Image: {{ current_image.stdout }}
dest: "{{ backups_path }}/rollback_{{ ansible_date_time.epoch }}.txt"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
post_tasks:
- name: Display rollback summary
debug:
msg:
- "✅ Rollback completed successfully!"
- "Rolled back to: {{ rollback_image }}"
- "From backup: {{ rollback_backup.path }}"
- "Current running image: {{ current_image.stdout }}"
- "Health check URL: {{ health_check_url }}"
- name: Recommend health check
debug:
msg: "⚠️ Please verify application health at {{ health_check_url }}"