Skip to content

Commit

Permalink
feat: allow locking maintenance mode
Browse files Browse the repository at this point in the history
this enables manually turning it on
  • Loading branch information
SilasPeters committed Jan 3, 2025
1 parent 73876eb commit 04a85cd
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 26 deletions.
8 changes: 8 additions & 0 deletions ansible/playbooks/pretix/lock-maintenance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
- hosts: "all"
remote_user: "ansible"
become: true

tasks:
- name: "lock and enable maintenance mode"
include_tasks: "$playbook_dir/../../../roles/pretix/tasks/lock-maintenance.yml"
8 changes: 8 additions & 0 deletions ansible/playbooks/pretix/unlock-maintenance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
- hosts: "all"
remote_user: "ansible"
become: true

tasks:
- name: "unlock and redetermine maintenance mode"
include_tasks: "$playbook_dir/../../../roles/pretix/tasks/unlock-maintenance.yml"
6 changes: 0 additions & 6 deletions ansible/roles/pretix/handlers/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,3 @@
- "pretix-web.service"
- "pretix-worker.service"
listen: "restart pretix"

- name: "ensure maintenance mode is in desired state and reload nginx"
ansible.builtin.service:
name: "pretix-maintenance.service" # This service reloads nginx
state: "started"
listen: "pretix reload nginx"
14 changes: 14 additions & 0 deletions ansible/roles/pretix/tasks/lock-maintenance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
- name: "create pretix's maintenance lock"
ansible.builtin.file:
path: "/var/www/pretix/MAINTENANCE_MODE_LOCK"
state: "touch"
become: true
become_user: "pretix"

- name: "ensure pretix's maintenance state is properly configured"
ansible.builtin.service:
name: "pretix-maintenance.service"
state: "started"
become: true
become_user: "root"
23 changes: 15 additions & 8 deletions ansible/roles/pretix/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,14 @@
group: "pretix"
src: "pretix_maintenance.html.j2"
dest: "/var/www/pretix/pretix-maintenance.html"
become: true
become_user: "pretix"

- name: "install maintenance script to be run my systemd services"
- name: "install maintenance script to be run by systemd services"
ansible.builtin.template:
src: "pretix-maintenance.sh.j2"
dest: "/usr/local/bin/pretix-maintenance.sh"
mode: "755"

- name: "prepare maintenance mode services"
ansible.builtin.template:
Expand All @@ -167,19 +170,23 @@
name: "pretix-maintenance.timer"
state: "started"
enabled: "yes"
# We do not need to start the service itself yet

# We do not simply reload nginx, but also run the previously defined systemd
# service to enable or disable maintenance mode - whichever is currently
# desired. This removes the need to specify during deploy whether we want
# maintenance mode on or off before we expose the service.
- name: "ensure maintenance mode is in right state"
ansible.builtin.service:
name: "pretix-maintenance.service"
state: "started"
changed_when: false
# Note: this should be run before reloading nginx, to prevent creating a
# short window of opportunity where people can buy tickets whilst
# maintenance mode should be on.

- name: "set up nginx for pretix"
block:
- name: "place pretix's nginx configuration"
ansible.builtin.template:
src: "nginx.conf.j2"
dest: "/etc/nginx/sites-available/pretix.{{ canonical_hostname }}.conf"
notify: "pretix reload nginx"
notify: "reload nginx"

- name: "enable pretix's nginx configuration"
ansible.builtin.file:
Expand All @@ -188,4 +195,4 @@
state: "link"
vars:
filename: "pretix.{{ canonical_hostname }}.conf"
notify: "pretix reload nginx"
notify: "reload nginx"
14 changes: 14 additions & 0 deletions ansible/roles/pretix/tasks/unlock-maintenance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
- name: "remove pretix's maintenance lock"
ansible.builtin.file:
path: "/var/www/pretix/MAINTENANCE_MODE_LOCK"
state: "absent"
become: true
become_user: "pretix"

- name: "ensure pretix's maintenance state is properly configured"
ansible.builtin.service:
name: "pretix-maintenance.service"
state: "started"
become: true
become_user: "root"
14 changes: 10 additions & 4 deletions ansible/roles/pretix/templates/nginx.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ server {
# Security header file not included because frames

location / {
if (-f /var/www/pretix/MAINTENANCE_MODE) {
error_page 503 /var/www/pretix/pretix-maintenance.html;
return 503;
}
if (-f /var/www/pretix/MAINTENANCE_MODE) {
error_page 503 =200 /pretix-maintenance.html;
return 503;
}

proxy_pass http://localhost:8345/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Expand Down Expand Up @@ -56,4 +56,10 @@ server {
expires 365d;
add_header Cache-Control "public";
}

# Expose 503 page
location = /pretix-maintenance.html {
root /var/www/pretix/;
internal; # Prevent direct access to the page
}
}
28 changes: 23 additions & 5 deletions ansible/roles/pretix/templates/pretix-maintenance.sh.j2
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
#!/usr/bin/env bash

##
## ABSTRACT:
## This script checks the current UTC time, and based on that time
## enables or disables the pretix maintenance mode.
## This script also respects a maintenance lock, meaning that if the lock exists,
## maintenance mode will be enabled regardless of anything.
## Enable the lock by simply creating the marker at MARKER_PATH defined below.
##
## PURPOSE:
## The goal is to compensate for the report cut-off being different for Mollie and
## Pretix, as described in https://github.com/svsticky/sadserver/issues/487
## This improves the treasurer's quality of life.
##
## USAGE: pretix_maintenance.sh
##
Expand All @@ -12,13 +21,22 @@ set -eEfuo pipefail
IFS=$'\n\t'

MARKER_PATH='/var/www/pretix/MAINTENANCE_MODE'
MARKER_LOCK_PATH="${MARKER_PATH}_LOCK"


# If the maintenance lock exists, just ensure maintenance mode is enabled
if [[ -f "$MARKER_LOCK_PATH" ]]; then
touch "$MARKER_PATH" # Enable maintenance mode
return
fi


HOUR=$(date -u +%H) # UTC hour
DAY=$(date -u +%a) # UTC day
HOUR=$(date -u +%H) # UTC hour

if [[ "$DAY" == "su" && $HOUR -gt 21 || "$DAY" == "mo" && $HOUR -lt 5 ]]
then touch "$MARKER_PATH"; # Enable maintenance mode
else rm "$MARKER_PATH"; # Disable maintenance mode
if [[ "$DAY" == "su" && $HOUR -gt 21 || "$DAY" == "mo" && $HOUR -lt 5 ]]; then
touch "$MARKER_PATH"; # Enable maintenance mode
else
rm -f "$MARKER_PATH"; # Disable maintenance mode
fi

systemctl reload nginx # Apply changes
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

[Unit]
Description=Configure pretix maintenance mode
# If, compared to UTC, we are between sunday 22:00 and monday 05:00, we want
# maintenance mode enabled and otherwise not.
OnFailure=failure-notificator@%n.service

[Service]
# If, compared to UTC, we are between sunday 22:00 and monday 05:00, we want
# maintenance mode and otherwise not.
ExecStart=/usr/local/bin/pretix-maintenance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[Unit]
Description=Weekly pretix maintenance
#During week transitions, enable maintenance mode to make the treasurer's life easier
# During week transitions, enable maintenance mode to make the treasurer's life easier

[Timer]
OnCalendar=Sun *-*-* 22:00:00 UTC
Expand Down
44 changes: 44 additions & 0 deletions docs/pretix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Pretix

## Maintenance mode

Pretix has a weekly routine in enabling and disabling maintenance mode.
The treasurer benefits from this, for the weekly reports from Mollie have a
different cut-off then Pretix has, which can't be reconfigured. As such, Pretix
will be brought in maintenance mode during this overlapping period, to make the
book works way easier.

Maintenance mode can be manually enabled for an arbitrarily long duration, but
not disabled for an arbitrarily long duration, on purpose. This can be done
with the two playbooks:

```bash
$ sadserver/ansible
./deploy.py --to staging --playbook=playbooks/pretix/lock-maintenance.yml
```

```bash
$ sadserver/ansible
./deploy.py --to staging --playbook=playbooks/pretix/unlock-maintenance.yml
```

### How the maintenance mode works

Maintenance mode is enabled when a marker file is present, indicating it is on.
Nginx will respect this and return a 503 error page, preventing users from
buying tickets. Admin panels and such are still available.

This state is managed with a single script: `/usr/local/bin/pretix-maintenance.sh`.
This script can be run as the systemd service `pretix-maintenance`, which is
automatically run two times every week (at the start and end of the overlapping
reporting period). Taking all things into account, this script ensures the
maintenance mode is in the right state.

If you wish to enable maintenance mode manually - and locking it, preventing
the weekly service from touching the marker file - a playbook is available,
placing a second marker file (lock) and running the same systemd service to
ensure the maintenance mode is in the desired state. Running the other
playbook, this lock file is removed and the systemd script is run again. Note
that when this is done during the overlapping reporting period - when
maintenance mode should be on - the maintenance marker still persists, but the
lock is gone.

0 comments on commit 04a85cd

Please sign in to comment.