Files
ansible_fencl/update_bettershift.yml
T

165 lines
6.0 KiB
YAML

# update_bettershift.yml
---
- name: Update BetterShift
hosts: pve2_vm
gather_facts: false
become: true
become_user: root
vars:
compose_local_dir: "{{ playbook_dir }}/docker-compose"
compose_remote_base: "/home/howard/.ansible-compose/docker-compose"
bettershift_project: bettershift
bettershift_compose_filename: docker-compose-bettershift.yml
bettershift_service: bettershift
bettershift_port: 3067
bettershift_data_dir: /data/compose/bettershift/data
bettershift_version: v3.3.0
environment:
BETTERSHIFT_VERSION: "{{ bettershift_version }}"
tasks:
- name: Check existing database and env file
ansible.builtin.stat:
path: "{{ item }}"
loop:
- "{{ bettershift_data_dir }}/sqlite.db"
- "{{ compose_remote_base }}/.env.bettershift"
register: bettershift_files
- name: Refuse missing or empty files
ansible.builtin.assert:
that:
- item.stat.isreg | default(false)
- item.stat.size | default(0) > 0
fail_msg: "Missing or empty file: {{ item.item }}"
loop: "{{ bettershift_files.results }}"
loop_control:
label: "{{ item.item }}"
- name: Check database before changing deployment
ansible.builtin.command:
argv:
- python3
- -c
- |
import pathlib, sqlite3, sys
path = pathlib.Path(sys.argv[1])
with sqlite3.connect(path.as_uri() + '?mode=ro', uri=True) as src:
with sqlite3.connect(':memory:') as db:
src.backup(db)
integrity = db.execute('PRAGMA integrity_check').fetchall()
errors = db.execute('PRAGMA foreign_key_check').fetchall()
print('Integrity:', integrity, 'Foreign key violations:', errors)
known_errors = {
('audit_logs', 1, 'user', 0),
('audit_logs', 2, 'user', 0),
('audit_logs', 3, 'user', 0),
('audit_logs', 4, 'user', 0),
('audit_logs', 5, 'user', 0),
('calendars', 1, 'user', 0),
}
missing_user = 'RnqG0DGnnCOVgL3iWRRUAvHlMhLcdm6Y'
unexpected = []
for error in errors:
if error not in known_errors:
unexpected.append(error)
continue
table, rowid, _, _ = error
column = 'owner_id' if table == 'calendars' else 'user_id'
value = db.execute(
f'SELECT "{column}" FROM "{table}" WHERE rowid = ?',
(rowid,)
).fetchone()
if value != (missing_user,):
unexpected.append(error)
elif table == 'calendars':
calendar = db.execute(
'SELECT id FROM calendars WHERE rowid = ?', (rowid,)
).fetchone()
if calendar != ('60a3f0e7-c8f6-4f53-92f9-7088dff4656a',):
unexpected.append(error)
print('Unexpected foreign key violations:', unexpected)
sys.exit(0 if integrity == [('ok',)] and not unexpected else 1)
- "{{ bettershift_data_dir }}/sqlite.db"
changed_when: false
- name: Upload BetterShift compose file
ansible.builtin.copy:
src: "{{ compose_local_dir }}/{{ bettershift_compose_filename }}"
dest: "{{ compose_remote_base }}/{{ bettershift_compose_filename }}"
mode: '0644'
backup: true
- name: Pull image without starting it
community.docker.docker_compose_v2_pull:
project_name: "{{ bettershift_project }}"
project_src: "{{ compose_remote_base }}"
files: ["{{ bettershift_compose_filename }}"]
- name: Create private backup directory
ansible.builtin.tempfile:
state: directory
path: /data/compose/bettershift
prefix: backup-before-upgrade-
register: bettershift_backup
- name: Show backup location
ansible.builtin.debug:
msg: "Backup: {{ bettershift_backup.path }}"
- name: Stop service and create a verified backup
block:
- name: Stop BetterShift
ansible.builtin.command:
argv: [docker, stop, --timeout, '60', bettershift]
changed_when: true
- name: Copy data and compare files
ansible.builtin.shell: |
set -eu
test "$(docker inspect --format '{% raw %}{{.State.Running}}{% endraw %}' bettershift)" = false
cp -a -- "$DATA_DIR" "$BACKUP_DIR/data"
diff -qr -- "$DATA_DIR" "$BACKUP_DIR/data"
environment:
DATA_DIR: "{{ bettershift_data_dir }}"
BACKUP_DIR: "{{ bettershift_backup.path }}"
changed_when: true
rescue:
- name: Restart original container if backup fails
ansible.builtin.command:
argv: [docker, start, bettershift]
changed_when: true
- name: Abort update
ansible.builtin.fail:
msg: Backup failed. Original container restarted; update aborted.
- name: Recreate BetterShift once
community.docker.docker_compose_v2:
project_name: "{{ bettershift_project }}"
project_src: "{{ compose_remote_base }}"
files: ["{{ bettershift_compose_filename }}"]
services: ["{{ bettershift_service }}"]
state: present
pull: never
recreate: always
- name: Check BetterShift HTTP endpoint
ansible.builtin.uri:
url: "http://127.0.0.1:{{ bettershift_port }}/"
status_code: [200, 307, 308]
follow_redirects: none
register: bettershift_http
retries: 30
delay: 3
until: bettershift_http.status | default(0) in [200, 307, 308]
changed_when: false