github.com/canonical/ubuntu-image@v0.0.0-20240430122802-2202fe98b290/tests/lib/external/snapd-testing-tools/remote/remote.refresh (about) 1 #!/bin/bash -e 2 3 show_help() { 4 echo "usage: remote.refresh snap [--channel CHANNEL] <SNAPNAME>" 5 echo "usage: remote.refresh full [--channel CHANNEL]" 6 echo "usage: remote.refresh disable-refreshes" 7 echo "" 8 echo "SNAPNAME: allowed options are: kernel, core, base, gadget, snapd or the snap name" 9 echo "" 10 echo "Available options:" 11 echo " -h --help show this help message." 12 echo "" 13 } 14 15 check_refresh_after_reboot() { 16 local refresh_channel=$1 17 local snap=$2 18 19 echo "remote.refresh: checking snap \"$snap\" refresh after reboot" 20 remote.wait-for reboot 21 remote.wait-for snap-command 22 remote.retry -n 10 --wait 2 "snap info $snap | grep -q -E \"tracking: +(latest/${refresh_channel}|${refresh_channel})\"" 23 echo "remote.refresh: snap \"$snap\" refreshed correctly" 24 } 25 26 check_refresh() { 27 local refresh_channel=$1 28 local snap=$2 29 30 echo "remote.refresh: checking snap \"$snap\" refresh with no reboot" 31 remote.wait-for ssh -n 60 --wait 2 32 remote.wait-for snap-command 33 remote.retry -n 10 --wait 2 "snap info $snap | grep -q -E \"tracking: +(latest/${refresh_channel}|${refresh_channel})\"" 34 echo "remote.refresh: snap \"$snap\" refreshed correctly" 35 } 36 37 check_waiting_for_reboot() { 38 remote.exec "sudo journalctl -n 30" | grep -q "Waiting for system reboot" 39 } 40 41 check_ready_to_refresh() { 42 remote.wait-for ssh 43 if check_waiting_for_reboot; then 44 echo "remote.refresh: waiting for system reboot" 45 remote.wait-for reboot 46 fi 47 remote.wait-for refresh 48 } 49 50 do_refresh() { 51 local snap_name=$1 52 local refresh_channel=$2 53 54 echo "remote.refresh: triggering refresh for snap \"$snap_name\" on channel \"$refresh_channel\"" 55 output=$(remote.exec "sudo snap refresh --channel ${refresh_channel} $snap_name 2>&1" || echo "snapd is about to reboot the system") 56 if echo "$output" | grep -E "(no updates available|cannot refresh \"$snap_name\"|is not installed)"; then 57 echo "remote.refresh: snap \"$snap_name\" has no updates available" 58 elif echo "$output" | grep -E "snapd is about to reboot the system"; then 59 remote.exec --timeout 3 "sudo reboot" || true 60 check_refresh_after_reboot "$refresh_channel" "$snap_name" 61 else 62 check_refresh "$refresh_channel" "$snap_name" 63 fi 64 } 65 66 process_refresh() { 67 local snap_name=$1 68 local refresh_channel=$2 69 70 if [ -z "$refresh_channel" ]; then 71 # Tracking is retrieved from snap info command because in old versions of 72 # snapd the tracking is not included in the snap list output 73 refresh_channel="$(remote.exec "snap info $snap_name" | grep -E '^tracking:' | awk '{ print $2 }')" 74 fi 75 76 echo "remote.refresh: Refreshing $snap_name snap from $refresh_channel channel" 77 do_refresh "$snap_name" "$refresh_channel" 78 } 79 80 refresh_fundamental() { 81 snap_name=$1 82 regex=$2 83 refresh_channel=$3 84 85 echo "remote.refresh: starting $snap_name refresh process" 86 snap_name="$(remote.exec "snap list" | grep -E "$regex" | awk '{ print $1 }')" 87 if [ -z "$snap_name" ]; then 88 echo "remote.refresh: no $snap_name snap to update" 89 return 90 fi 91 92 if [ "$(echo "$snap_name" | wc -l)" -gt 1 ]; then 93 echo "remote.refresh: there is more than 1 $snap_name snap to update, skipping" 94 return 0 95 fi 96 process_refresh "$snap_name" "$refresh_channel" 97 } 98 99 refresh_kernel() { 100 local refresh_channel=$1 101 local snap_name regex 102 103 snap_name='kernel' 104 regex='(kernel$|kernel,)' 105 refresh_fundamental "$snap_name" "$regex" "$refresh_channel" 106 } 107 108 refresh_gadget() { 109 local refresh_channel=$1 110 local snap_name regex 111 112 snap_name='gadget' 113 regex='(gadget$|gadget,)' 114 refresh_fundamental "$snap_name" "$regex" "$refresh_channel" 115 } 116 117 refresh_snapd() { 118 local refresh_channel=$1 119 local snap_name regex 120 121 snap_name='snapd' 122 regex='^snapd.*(snapd$|snapd,)' 123 refresh_fundamental "$snap_name" "$regex" "$refresh_channel" 124 } 125 126 refresh_core() { 127 local refresh_channel=$1 128 local snap_name regex 129 130 snap_name='core' 131 regex='^core.*(core$|core,)' 132 refresh_fundamental "$snap_name" "$regex" "$refresh_channel" 133 } 134 135 refresh_core_base() { 136 local refresh_channel=$1 137 local snap_name regex 138 139 snap_name='core base' 140 regex='^core.* (base$|base,)' 141 refresh_fundamental "$snap_name" "$regex" "$refresh_channel" 142 } 143 144 refresh_snap() { 145 local snapname=$1 146 local refresh_channel=$2 147 148 if [ -z "$snapname" ]; then 149 echo "remote.refresh: snap name to refresh is not specified" 150 return 1 151 fi 152 153 if ! remote.exec "snap list $snapname"; then 154 echo "remote.refresh: no $snapname snap to update" 155 return 0 156 fi 157 158 snap_line="$(remote.exec "snap list $snapname" | tail -1)" 159 process_refresh "$snap_line" "$refresh_channel" 160 } 161 162 refresh_all() { 163 # Run update and make "|| true" to continue when the connection is closed by remote host or not any snap to update 164 remote.exec "sudo snap refresh" || true 165 remote.wait-for ssh 166 } 167 168 169 get_boot_id() { 170 remote.exec "cat /proc/sys/kernel/random/boot_id" 171 } 172 173 prevent_autorefresh() { 174 remote.wait-for ssh 175 state_path="$(remote.exec 'find /writable -name state.json 2>/dev/null' || true)" 176 if [ -z "$state_path" ]; then 177 echo "remote.refresh: state file not found in writable" 178 state_path="$(remote.exec 'find / -name state.json 2>/dev/null' || true)" 179 fi 180 if [ -z "$state_path" ]; then 181 echo "remote.refresh: state.json file not found" 182 exit 1 183 fi 184 185 remote.exec "sudo cp $state_path /tmp/state.json" 186 remote.exec "sudo chmod 644 /tmp/state.json" 187 remote.pull /tmp/state.json state.json 188 echo "remote.refresh: state file retrieved" 189 190 jq ".data[\"last-refresh\"] = \"$(date +%Y-%m-%dT%H:%M:%S%:z)\"" state.json > state.json.new 191 echo "remote.refresh: state file just updated" 192 193 remote.push state.json.new /tmp/state.json.new 194 remote.exec "sudo cp /tmp/state.json.new $state_path" 195 remote.exec "sudo chmod 600 $state_path" 196 echo "remote.refresh: updated state file restored" 197 rm -f state.json state.json.new 198 } 199 200 disable_refreshes() { 201 if ! command -v jq &>/dev/null; then 202 snap install --devmode jq 203 fi 204 205 echo "remote.refresh: modifying state to make it look like the last refresh just happened" 206 remote.exec "sudo systemctl stop snapd.socket snapd.service" 207 prevent_autorefresh 208 remote.exec "sudo systemctl start snapd.socket snapd.service" 209 210 echo "remote.refresh: minimizing risk of hitting refresh schedule" 211 remote.exec "sudo snap set core refresh.schedule=00:00-23:59" 212 remote.exec "sudo snap refresh --time --abs-time" | grep -Eq "last: 2[0-9]{3}" 213 214 } 215 216 full_refresh() { 217 echo "remote.refresh: starting full refresh" 218 219 check_ready_to_refresh 220 disable_refreshes 221 222 check_ready_to_refresh 223 refresh_core 224 225 check_ready_to_refresh 226 refresh_core_base 227 228 check_ready_to_refresh 229 refresh_snapd 230 231 check_ready_to_refresh 232 refresh_kernel 233 234 check_ready_to_refresh 235 refresh_all 236 237 check_ready_to_refresh 238 } 239 240 snap_refresh() { 241 local channel snapname 242 243 while [ $# -gt 0 ]; do 244 case "$1" in 245 --channel) 246 channel=$2 247 shift 2 248 ;; 249 *) 250 snapname=$1 251 shift 252 ;; 253 esac 254 done 255 256 if [ -z "$snapname" ]; then 257 echo "remote.refresh: snap name to refresh is not specified" 258 return 1 259 fi 260 261 case "$snapname" in 262 core) 263 refresh_core "$channel" 264 ;; 265 base) 266 refresh_core_base "$channel" 267 ;; 268 snapd) 269 refresh_snapd "$channel" 270 ;; 271 kernel) 272 refresh_kernel "$channel" 273 ;; 274 gadget) 275 refresh_gadget "$channel" 276 ;; 277 *) 278 refresh_snap "$snapname" "$channel" 279 ;; 280 esac 281 } 282 283 main() { 284 if [ $# -eq 0 ]; then 285 show_help 286 exit 287 fi 288 289 local action 290 case "$1" in 291 -h|--help) 292 show_help 293 exit 294 ;; 295 full) 296 action=full_refresh 297 shift 298 ;; 299 snap) 300 action=snap_refresh 301 shift 302 ;; 303 disable-refreshes) 304 action=disable_refreshes 305 shift 306 ;; 307 *) 308 echo "remote.refresh: unsupported parameter $1" >&2 309 exit 1 310 ;; 311 esac 312 313 if [ -z "$(declare -f "$action")" ]; then 314 echo "remote.refresh: no such command: $action" 315 show_help 316 exit 1 317 fi 318 319 "$action" "$@" 320 } 321 322 main "$@"