github.com/canonical/ubuntu-image@v0.0.0-20240430122802-2202fe98b290/tests/lib/external/snapd-testing-tools/remote/remote.wait-for (about) 1 #!/bin/bash -e 2 3 # The default values have been selected trying to match with most of 4 # the wait times in the tests and also trying to follow common sense. 5 DEFAULT_WAIT_FOR_SSH_ATTEMPTS=800 6 DEFAULT_WAIT_FOR_SSH_WAIT=1 7 DEFAULT_WAIT_FOR_NO_SSH_ATTEMPTS=200 8 DEFAULT_WAIT_FOR_NO_SSH_WAIT=1 9 DEFAULT_WAIT_FOR_SNAP_COMMAND_ATTEMPTS=200 10 DEFAULT_WAIT_FOR_SNAP_COMMAND_WAIT=1 11 DEFAULT_WAIT_FOR_DEV_INIT_ATTEMPTS=60 12 DEFAULT_WAIT_FOR_DEV_INIT_WAIT=1 13 DEFAULT_WAIT_FOR_REBOOT_ATTEMPTS=120 14 DEFAULT_WAIT_FOR_REBOOT_WAIT=5 15 16 show_help() { 17 echo "usage: remote.wait-for ssh [--wait WAIT] [-n|--attempts ATTEMPTS]" 18 echo " remote.wait-for no-ssh [--wait WAIT] [-n|--attempts ATTEMPTS]" 19 echo " remote.wait-for snap-command [--wait WAIT] [-n|--attempts ATTEMPTS]" 20 echo " remote.wait-for reboot [--wait WAIT] [-n|--attempts ATTEMPTS]" 21 echo " remote.wait-for device-initialized [--wait WAIT] [-n|--attempts ATTEMPTS]" 22 echo " remote.wait-for refresh [--wait WAIT] [-n|--attempts ATTEMPTS]" 23 echo "" 24 echo "Available options:" 25 echo " -h --help show this help message." 26 echo "" 27 } 28 29 wait_for_ssh() { 30 local attempts=${1:-$DEFAULT_WAIT_FOR_SSH_ATTEMPTS} 31 local wait=${2:-$DEFAULT_WAIT_FOR_SSH_WAIT} 32 echo "remote.wait-for: waiting for ssh connection" 33 34 until remote.exec "true" &>/dev/null; do 35 echo -n '.' 36 attempts=$(( attempts - 1 )) 37 if [ $attempts -le 0 ]; then 38 echo "" 39 echo "remote.wait-for: timed out waiting for ssh connection to succeed" 40 return 1 41 fi 42 sleep "$wait" 43 done 44 echo "" 45 echo "remote.wait-for: ssh connection stablished" 46 } 47 48 wait_for_no_ssh() { 49 local attempts=${1:-$DEFAULT_WAIT_FOR_NO_SSH_ATTEMPTS} 50 local wait=${2:-$DEFAULT_WAIT_FOR_NO_SSH_WAIT} 51 52 echo "remote.wait-for: waiting for no ssh connection" 53 54 while remote.exec "true" &>/dev/null; do 55 echo -n '.' 56 attempts=$(( attempts - 1 )) 57 if [ $attempts -le 0 ]; then 58 echo "" 59 echo "remote.wait-for: timed out waiting for ssh connection to fail" 60 return 1 61 fi 62 sleep "$wait" 63 done 64 echo "" 65 echo "remote.wait-for: ssh connection lost" 66 } 67 68 69 wait_for_snap_command() { 70 local attempts=${1:-$DEFAULT_WAIT_FOR_SNAP_COMMAND_ATTEMPTS} 71 local wait=${2:-$DEFAULT_WAIT_FOR_SNAP_COMMAND_WAIT} 72 73 echo "remote.wait-for: waiting for snap command" 74 75 while ! remote.exec "command -v snap" &>/dev/null; do 76 echo -n '.' 77 attempts=$(( attempts - 1 )) 78 if [ $attempts -le 0 ]; then 79 echo "" 80 echo "remote.wait-for: timed out waiting for snap command to succeed" 81 return 1 82 fi 83 sleep "$wait" 84 done 85 echo "" 86 echo "remote.wait-for: snap command ready" 87 } 88 89 get_boot_id() { 90 remote.exec "cat /proc/sys/kernel/random/boot_id" 91 } 92 93 wait_for_reconnect_ssh() { 94 echo "remote.wait-for: waiting for ssh is recoonected" 95 wait_for_no_ssh "$DEFAULT_WAIT_FOR_NO_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_NO_SSH_WAIT" 96 wait_for_ssh "$DEFAULT_WAIT_FOR_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_SSH_WAIT" 97 } 98 99 wait_for_reboot() { 100 local attempts=${1:-$DEFAULT_WAIT_FOR_REBOOT_ATTEMPTS} 101 local wait=${2:-$DEFAULT_WAIT_FOR_REBOOT_WAIT} 102 local initial_boot_id=$3 103 local last_boot_id 104 105 echo "remote.wait-for: waiting for reboot" 106 107 if [ -z "$initial_boot_id" ]; then 108 echo "remote.wait-for: initial boot id not set" 109 wait_for_reconnect_ssh 110 return 111 fi 112 113 while [ "$attempts" -ge 0 ]; do 114 echo -n '.' 115 attempts=$(( attempts - 1 )) 116 # The get_boot_id could fail because the connection is broken due to the reboot 117 last_boot_id="$(get_boot_id)" || true 118 # The boot_id is a GUID, i.e. 450d12a1-9811-464e-8c9e-cec1c60e8684 119 if [[ "$last_boot_id" =~ .*-.*-.*-.*-.* ]] && [ "$last_boot_id" != "$initial_boot_id" ]; then 120 break 121 fi 122 sleep "$wait" 123 done 124 125 echo "" 126 if [ "$last_boot_id" != "$initial_boot_id" ]; then 127 echo "remote.wait-for: reboot completed" 128 else 129 echo "remote.wait-for: boot id did not change" 130 return 1 131 fi 132 133 } 134 135 wait_for_device_initialized() { 136 local attempts=${1:-$DEFAULT_WAIT_FOR_DEV_INIT_ATTEMPTS} 137 local wait=${2:-$DEFAULT_WAIT_FOR_DEV_INIT_WAIT} 138 139 echo "remote.wait-for: waiting for device initialized" 140 141 while ! remote.exec "snap changes" | grep -Eq "Done.*Initialize device"; do 142 echo -n '.' 143 attempts=$(( attempts - 1 )) 144 if [ $attempts -le 0 ]; then 145 echo "" 146 echo "remote.wait-for: timed out waiting for device to be fully initialized. Aborting!" 147 return 1 148 fi 149 sleep "$wait" 150 done 151 echo "" 152 echo "remote.wait-for: device initialized" 153 } 154 155 wait_for_refresh_reboot() { 156 echo "remote.wait-for: waiting for refresh reboot" 157 158 local change_id=${1:-} 159 local boot_id=${2:-} 160 if [ -z "$change_id" ] || [ -z "$boot_id" ]; then 161 echo "remote.wait-for: either change_id or boot_id not provided" 162 return 1 163 fi 164 165 wait_for_ssh "$DEFAULT_WAIT_FOR_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_SSH_WAIT" 166 for _ in $(seq 5); do 167 # The refresh is being executed 168 if remote.exec "snap changes" | grep -Eq "$change_id.*Doing.*(Auto-refresh|Refresh)"; then 169 # The systems is waiting for reboot 170 if remote.exec "sudo journalctl -u snapd -n 30" | grep -q "Waiting for system reboot"; then 171 echo "remote.wait-for: waiting for system reboot" 172 remote.exec "sudo reboot" || true 173 wait_for_no_ssh "$DEFAULT_WAIT_FOR_NO_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_NO_SSH_WAIT" 174 break 175 fi 176 echo "remote.wait-for: either auto-refresh or refresh in progress" 177 fi 178 179 # when the refresh has finished and no reboot needed, the function returns 180 if remote.exec "snap changes" | grep -Eq "$change_id.*Done.*(Auto-refresh|Refresh)"; then 181 echo "remote.wait-for: refresh completed, reboot not required" 182 return 183 fi 184 185 if remote.exec "snap changes" | grep -Eq "$change_id.*Error.*(Auto-refresh|Refresh)"; then 186 echo "remote.wait-for: refresh finished with error" 187 return 1 188 fi 189 190 echo "remote.wait-for: system reboot not detected" 191 sleep 1 192 done 193 wait_for_ssh "$DEFAULT_WAIT_FOR_SSH_ATTEMPTS" "$DEFAULT_WAIT_FOR_SSH_WAIT" 194 if [ "$(remote.exec "cat /proc/sys/kernel/random/boot_id")" == "$boot_id" ]; then 195 echo "remote.wait-for: boot id did not change" 196 return 1 197 else 198 echo "remote.wait-for: boot id changed, refresh completed with reboot" 199 fi 200 } 201 202 wait_for_refresh(){ 203 echo "remote.wait-for: waiting for either auto-refresh or refresh" 204 205 change_line="$(remote.exec 'snap changes' | grep -E 'Doing.*(Auto-refresh|Refresh)' || true)" 206 if [ -n "$change_line" ]; then 207 echo "remote.wait-for: refresh in progress" 208 change_id="$(echo "$change_line" | awk '{ print $1 }')" 209 boot_id="$(remote.exec "cat /proc/sys/kernel/random/boot_id")" 210 211 for _ in $(seq 20); do 212 if wait_for_refresh_reboot "$change_id" "$boot_id"; then 213 break 214 fi 215 done 216 echo "" 217 218 changes="$(remote.exec 'snap changes')" 219 if echo "$changes" | grep -Eq "$change_id.*Doing.*(Auto-refresh|Refresh)"; then 220 echo "remote.wait-for: still doing refresh, exiting" 221 elif echo "$changes" | grep -Eq "$change_id.*Error.*(Auto-refresh|Refresh)"; then 222 echo "remote.wait-for: refresh failed" 223 elif echo "$changes" | grep -Eq "$change_id.*Done.*(Auto-refresh|Refresh)"; then 224 echo "remote.wait-for: refresh completed" 225 else 226 echo "remote.wait-for: refresh results unknown" 227 echo "$changes" 228 return 1 229 fi 230 else 231 echo "remote.wait-for: no refresh in progress" 232 fi 233 } 234 235 main() { 236 if [ $# -eq 0 ]; then 237 show_help 238 exit 239 fi 240 241 local action wait attempts others 242 case "$1" in 243 -h|--help) 244 show_help 245 exit 246 ;; 247 ssh) 248 action=wait_for_ssh 249 shift 250 ;; 251 no-ssh) 252 action=wait_for_no_ssh 253 shift 254 ;; 255 snap-command) 256 action=wait_for_snap_command 257 shift 258 ;; 259 reboot) 260 action=wait_for_reboot 261 shift 262 ;; 263 device-initialized) 264 action=wait_for_device_initialized 265 shift 266 ;; 267 refresh) 268 action=wait_for_refresh 269 shift 270 ;; 271 *) 272 echo "remote.wait-for: unsupported parameter $1" >&2 273 exit 1 274 ;; 275 esac 276 277 if [ -z "$(declare -f "$action")" ]; then 278 echo "remote.wait-for: no such command: $action" 279 show_help 280 exit 1 281 fi 282 283 while [ $# -gt 0 ]; do 284 case "$1" in 285 --wait) 286 wait=$2 287 shift 2 288 ;; 289 --attempts|-n) 290 attempts=$2 291 shift 2 292 ;; 293 *) 294 if [ -z "$others" ]; then 295 others=$1 296 else 297 others="$others $1" 298 fi 299 shift 300 ;; 301 esac 302 done 303 304 "$action" "$attempts" "$wait" "$others" 305 } 306 307 main "$@"