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