|
1 | 1 | ---
|
2 | 2 |
|
| 3 | +- name: Initialize |
| 4 | + set_fact: |
| 5 | + # Keep in sync with upgrade.rs |
| 6 | + stages: |
| 7 | + - 'None' |
| 8 | + - 'PendingUpdates' |
| 9 | + - 'MigrationCheck' |
| 10 | + - 'DisableApache2' |
| 11 | + - 'Backup' |
| 12 | + - 'BackupIptables' |
| 13 | + - 'Marker' |
| 14 | + - 'SuspendOSSEC' |
| 15 | + - 'ChangeAptSources' |
| 16 | + - 'AptGetUpdate' |
| 17 | + - 'AptGetFullUpgrade' |
| 18 | + - 'AptGetAutoremove' |
| 19 | + - 'RestoreIptables' |
| 20 | + - 'ReenableUnattendedUpdates' |
| 21 | + - 'ReenableOSSEC' |
| 22 | + - 'Reboot' |
| 23 | + - 'SwitchUbuntuSources' |
| 24 | + - 'IntegrityCheck' |
| 25 | + - 'ReenableApache2' |
| 26 | + - 'RemoveBackup' |
| 27 | + - 'Done' |
| 28 | + |
3 | 29 | - name: Check host's migration state
|
4 | 30 | ansible.builtin.slurp:
|
5 | 31 | src: /etc/securedrop-noble-migration-state.json
|
6 | 32 | register: migration_json
|
7 |
| - ignore_errors: yes |
8 | 33 |
|
9 |
| -- name: Skip migration if already done |
| 34 | +# Note: the variable finished_state is only for debugging and human-readable output. |
| 35 | +# The finished_state_index is what must be used for logic in `when` blocks. |
| 36 | + |
| 37 | +- name: Extract current state |
10 | 38 | set_fact:
|
11 |
| - already_finished: "{{ not migration_json.failed and (migration_json.content | b64decode | from_json)['finished'] == 'Done' }}" |
| 39 | + # slurp base64-encodes our file |
| 40 | + finished_state: "{{ (migration_json.content | b64decode | from_json)['finished'] }}" |
12 | 41 |
|
13 |
| -- name: Perform migration |
14 |
| - when: not already_finished |
| 42 | +- name: Extract current state (2) |
| 43 | + set_fact: |
| 44 | + finished_state_index: "{{ stages.index(finished_state) }}" |
| 45 | + |
| 46 | +- name: Kick off migration |
| 47 | + when: finished_state_index|int == 0 |
15 | 48 | block:
|
16 | 49 | - name: Instruct upgrade to begin
|
17 | 50 | ansible.builtin.copy:
|
|
48 | 81 | connect_timeout: 20
|
49 | 82 | sleep: 5
|
50 | 83 | delay: 10
|
51 |
| - timeout: 300 |
| 84 | + timeout: 600 |
| 85 | + |
| 86 | + - name: Recheck host's migration state |
| 87 | + ansible.builtin.slurp: |
| 88 | + src: /etc/securedrop-noble-migration-state.json |
| 89 | + register: migration_json |
| 90 | + |
| 91 | + - name: Extract current state |
| 92 | + set_fact: |
| 93 | + # slurp base64-encodes our file |
| 94 | + finished_state: "{{ (migration_json.content | b64decode | from_json)['finished'] }}" |
| 95 | + |
| 96 | + - debug: |
| 97 | + msg: "The current upgrade state is: {{ finished_state }}" |
| 98 | + |
| 99 | + - name: Extract current state (2) |
| 100 | + set_fact: |
| 101 | + finished_state_index: "{{ stages.index(finished_state) }}" |
| 102 | + # Note: do not add anything after this line in this block - it will not run, because |
| 103 | + # the block is in a `when` that we just made false by updating `finished_state_index`. |
52 | 104 |
|
| 105 | +- name: Phase 2 of migration |
| 106 | + # After PendingUpdates (index 1) but before Reboot (index 15) |
| 107 | + when: finished_state_index|int >= 1 and finished_state_index|int < 15 |
| 108 | + block: |
53 | 109 | # Start the systemd service manually to avoid waiting for the timer to pick it up
|
54 | 110 | - name: Resume upgrade systemd service
|
55 | 111 | ansible.builtin.systemd:
|
|
61 | 117 |
|
62 | 118 | # Same as above, this will most likely fail as unreachable when the server
|
63 | 119 | # actually reboots.
|
64 |
| - - name: Wait for system upgrade to noble |
| 120 | + - name: Wait for system upgrade to noble (phase 2) |
65 | 121 | ansible.builtin.wait_for:
|
66 | 122 | path: /etc/securedrop-noble-migration-state.json
|
67 | 123 | search_regex: '"finished":"Reboot"'
|
|
71 | 127 | ignore_unreachable: yes
|
72 | 128 | ignore_errors: yes
|
73 | 129 |
|
74 |
| - - name: Wait for the second reboot |
| 130 | + - name: Wait for the second reboot (phase 2) |
75 | 131 | ansible.builtin.wait_for_connection:
|
76 | 132 | connect_timeout: 20
|
77 | 133 | sleep: 5
|
78 | 134 | delay: 10
|
79 |
| - timeout: 300 |
| 135 | + timeout: 600 |
| 136 | + |
| 137 | + - name: Recheck host's migration state |
| 138 | + ansible.builtin.slurp: |
| 139 | + src: /etc/securedrop-noble-migration-state.json |
| 140 | + register: migration_json |
| 141 | + |
| 142 | + - name: Extract current state |
| 143 | + set_fact: |
| 144 | + # slurp base64-encodes our file |
| 145 | + finished_state: "{{ (migration_json.content | b64decode | from_json)['finished'] }}" |
| 146 | + |
| 147 | + - debug: |
| 148 | + msg: "The current upgrade state is: {{ finished_state }}" |
80 | 149 |
|
| 150 | + - name: Extract current state (2) |
| 151 | + set_fact: |
| 152 | + finished_state_index: "{{ stages.index(finished_state) }}" |
| 153 | + # Note: do not add anything after this line in this block - it will not run, because |
| 154 | + # the block is in a `when` that we just made false by updating `finished_state_index`. |
| 155 | + |
| 156 | +- name: Phase 2.5 of migration (for SSH-over-Tor users) |
| 157 | + # After PendingUpdates (index 1) but before Reboot (index 15) |
| 158 | + when: enable_ssh_over_tor and finished_state_index|int >= 1 and finished_state_index|int < 15 |
| 159 | + block: |
| 160 | + # Same as above, this will most likely fail as unreachable when the server |
| 161 | + # actually reboots. |
| 162 | + - name: Wait for system upgrade to noble (phase 2.5) |
| 163 | + ansible.builtin.wait_for: |
| 164 | + path: /etc/securedrop-noble-migration-state.json |
| 165 | + search_regex: '"finished":"Reboot"' |
| 166 | + sleep: 5 |
| 167 | + # Should finish in less than 30 minutes |
| 168 | + timeout: 1800 |
| 169 | + ignore_unreachable: yes |
| 170 | + ignore_errors: yes |
| 171 | + |
| 172 | + - name: Wait for the second reboot (phase 2.5) |
| 173 | + ansible.builtin.wait_for_connection: |
| 174 | + connect_timeout: 20 |
| 175 | + sleep: 5 |
| 176 | + delay: 10 |
| 177 | + timeout: 600 |
| 178 | + |
| 179 | + - name: Recheck host's migration state |
| 180 | + ansible.builtin.slurp: |
| 181 | + src: /etc/securedrop-noble-migration-state.json |
| 182 | + register: migration_json |
| 183 | + |
| 184 | + - name: Extract current state |
| 185 | + set_fact: |
| 186 | + # slurp base64-encodes our file |
| 187 | + finished_state: "{{ (migration_json.content | b64decode | from_json)['finished'] }}" |
| 188 | + |
| 189 | + - debug: |
| 190 | + msg: "The current upgrade state is: {{ finished_state }}" |
| 191 | + |
| 192 | + - name: Extract current state (2) |
| 193 | + set_fact: |
| 194 | + finished_state_index: "{{ stages.index(finished_state) }}" |
| 195 | + # Note: do not add anything after this line in this block - it will not run, because |
| 196 | + # the block is in a `when` that we just made false by updating `finished_state_index`. |
| 197 | + |
| 198 | +- name: Phase 3 of migration |
| 199 | + # After Reboot (index 15) but before Done (index 20) |
| 200 | + when: finished_state_index|int >= 15 and finished_state_index|int < 20 |
| 201 | + block: |
81 | 202 | - name: Re-resume upgrade systemd service
|
82 | 203 | ansible.builtin.systemd:
|
83 | 204 | name: securedrop-noble-migration-upgrade
|
|
90 | 211 | search_regex: '"finished":"Done"'
|
91 | 212 | sleep: 5
|
92 | 213 | timeout: 300
|
| 214 | + |
| 215 | + # Instead of slurping the file again, we can set the state to done |
| 216 | + # because we just waited for it to reach that point. |
| 217 | + - name: Extract current state |
| 218 | + set_fact: |
| 219 | + finished_state: "Done" |
| 220 | + finished_state_index: 20 |
| 221 | + |
| 222 | +- fail: |
| 223 | + msg: "Upgrade did not successfully finish; please check the logs and documentation for more details." |
| 224 | + when: finished_state != "Done" |
0 commit comments