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 "$@"