167 lines
5.8 KiB
YAML
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 }}"
|