#!/bin/bash
set -euo pipefail

function usrbin() {
  local cmd="$1"
  shift
  LD_LIBRARY_PATH=/sysusr/usr/lib64 /sysusr/usr/bin/"${cmd}" "$@"
}

# Note: don't use as "if download_and_verify" or "download_and_verify || " because that disables set -e error handling
function download_and_verify() {
  # Extracts release artifact to /sysroot/$final_name
  # Expects the env vars: FLATCAR_BOARD, VERSION
  local final_name="$1"
  local extracted_name="${final_name}"
  local name="${final_name/%.raw/.gz}"
  local URL="https://update.release.flatcar-linux.net/${FLATCAR_BOARD}/${VERSION}/${name}"
  # Check for scripts:sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-au-key/files/developer-v1.pub.pem
  if [ "$(usrbin md5sum /sysroot/usr/share/update_engine/update-payload-key.pub.pem | cut -d " " -f 1)" = "7192addf4a7f890c0057d21653eff2ea" ]; then
    URL="https://bincache.flatcar-linux.net/images/${FLATCAR_BOARD/-usr}/${VERSION}/flatcar_test_update-${name}"
    extracted_name="flatcar_test_update-${final_name}"
  fi
  local COUNT=""
  # Workaround: Once curl starts and fails to resolve a DNS name (due to a race or temporary failure),
  # it sticks to it for each retry, making the retry pointless. Therefore, we first have to
  # add a curl waiter that does the DNS retry and won't be stuck (nor waste 30*60 seconds).
  for COUNT in $(usrbin seq 30); do
    if usrbin curl -fsSL --head "${URL}" > /dev/null; then
      break
    fi
    sleep 1
  done
  local tempdir="/sysroot/ue-rs/"
  rm -rf "${tempdir}"
  mkdir -p "${tempdir}"
  usrbin download_sysext -p /sysroot/usr/share/update_engine/update-payload-key.pub.pem -o "${tempdir}" -u "${URL}" || { rm -rf "${tempdir}" ; echo "Failing boot" >&2 ; exit 1; }
  mv "${tempdir}/${extracted_name}" "/sysroot/${final_name}"
  rm -rf "${tempdir}"
  true # Don't leak previous exit code as return code
}

# Manage sysext symlinks to select the active sysext image, and do OEM partition migrations
# (An alternative to symlinks is to implement support for ".v directories" in systemd-sysext
# that could contain multiple versions and the right one gets selected, here not the newest
# but that matching the OS version. However, we would still need the migration logic and
# would not be able to use the OEM partition as dynamically preferred storage.)
VERSION=$(source /sysroot/usr/lib/os-release ; echo "${VERSION}")
FLATCAR_BOARD=$(source /sysroot/usr/lib/os-release ; echo "${FLATCAR_BOARD}")

# Not all OEM partitions have an oem-release file but we require it now
OEMID=$({ grep -m 1 -o "^ID=.*" /sysroot/oem/oem-release || true ; } | cut -d = -f 2)

# The active-oem-OEMID file gets created by the update-engine postinst action if both old and new /usr partitions have a sysext
if [ "${OEMID}" != "" ] && [ -e "/sysroot/oem/sysext/active-oem-${OEMID}" ]; then
  SYSEXT_OEM_PART="/oem/sysext/oem-${OEMID}-${VERSION}.raw"
  SYSEXT_ROOT_PART="/etc/flatcar/oem-sysext/oem-${OEMID}-${VERSION}.raw"
  SYMLINK="/sysroot/etc/extensions/oem-${OEMID}.raw"
  ACTIVE_OEM=""

  if [ -e "/sysroot/${SYSEXT_OEM_PART}" ]; then
    ACTIVE_OEM="${SYSEXT_OEM_PART}"
  elif [ -e "/sysroot/${SYSEXT_ROOT_PART}" ]; then
    if [ -L "${SYMLINK}" ]; then
      # Move the inactive sysext to the root partition to free space on the OEM partition
      PREV_SYSEXT="/"$(realpath -m --relative-base=/sysroot "${SYMLINK}" | sed 's#^/##')
      echo "Found ${PREV_SYSEXT} for possible move" >&2
      if [ "${PREV_SYSEXT}" != "" ] && [ -e "/sysroot/${PREV_SYSEXT}" ] && [[ "${PREV_SYSEXT}" != "/etc/flatcar/oem-sysext/"* ]]; then
        mkdir -p /sysroot/etc/flatcar/oem-sysext/
        echo "Moving ${PREV_SYSEXT}"
        # If it was an unexpected symlink target that can't be moved, go on
        mv "/sysroot/${PREV_SYSEXT}" /sysroot/etc/flatcar/oem-sysext/ || true
      fi
    fi
    # Try to store the active sysext in the OEM partition if there is enough space, this helps to support clearing the root partition
    mkdir -p /sysroot/oem/sysext/
    echo "Trying to move ${SYSEXT_ROOT_PART} to OEM partition" >&2
    if mv "/sysroot/${SYSEXT_ROOT_PART}" /sysroot/oem/sysext/; then
      ACTIVE_OEM="${SYSEXT_OEM_PART}"
    else
      rm -f "/sysroot${SYSEXT_OEM_PART}"
      echo "That failed, keeping it on root partition" >&2
      ACTIVE_OEM="${SYSEXT_ROOT_PART}"
    fi
  else
    echo "Did not find ${SYSEXT_OEM_PART} nor ${SYSEXT_ROOT_PART}, downloading" >&2
    systemctl start --quiet systemd-networkd systemd-resolved
    download_and_verify "oem-${OEMID}.raw"
    mkdir -p /sysroot/oem/sysext/
    echo "Trying to place /sysroot/oem-${OEMID}.raw on OEM partition" >&2
    if mv "/sysroot/oem-${OEMID}.raw" "/sysroot${SYSEXT_OEM_PART}"; then
      ACTIVE_OEM="${SYSEXT_OEM_PART}"
    else
      rm -f "/sysroot${SYSEXT_OEM_PART}"
      echo "That failed, moving it to right location on root partition" >&2
      mkdir -p /sysroot/etc/flatcar/oem-sysext/
      mv "/sysroot/oem-${OEMID}.raw" "/sysroot${SYSEXT_ROOT_PART}"
      ACTIVE_OEM="${SYSEXT_ROOT_PART}"
    fi
  fi
  if [ "${ACTIVE_OEM}" != "" ] && [ -e "/sysroot/${ACTIVE_OEM}" ]; then
    mkdir -p "/sysroot/etc/extensions"
    ln -fs "${ACTIVE_OEM}" "${SYMLINK}"
  else
    rm -f "${SYMLINK}"
  fi

  # Flag file created by the update-engine postinst action if both /usr partitions have a sysext and active-oem-OEMID didn't exist
  if [ -e "/sysroot/oem/sysext/migrate-oem-${OEMID}" ]; then
    echo "Found migration flag, deleting known old OEM partition files" >&2
    # For each OEMID, delete known old files under /oem/ and /etc/ based on the contents of the flag file
    # (The list is maintained in the coreos-base/misc-files package)
    while IFS="" read -r entry; do
      if [ "${entry}" = "/etc/systemd/system/multi-user.target.wants/oem-cloudinit.service" ] && [ -L "/sysroot/etc/systemd/system/multi-user.target.wants/oem-cloudinit.service" ]; then
        ln -fs /usr/lib/systemd/system/oem-cloudinit.service /sysroot/etc/systemd/system/multi-user.target.wants/oem-cloudinit.service || true
      elif [ "${entry}" != "" ]; then
        rm -rf "/sysroot${entry}" || true
      fi
    done < "/sysroot/usr/share/flatcar/oems/${OEMID}"
    rm -f "/sysroot/oem/sysext/migrate-oem-${OEMID}"
  fi
fi

# Manage optional Flatcar extensions that are coupled to the OS version.
# They are only stored on the root partition but not directly in /etc/extensions/ because there
# can only be one file for the extension name (this could be covered by the ".v directory" proposal).
# The enabled-sysext.conf file is read from /etc and /usr and contains one name per line,
# and when the name is prefixed with a "-" it means that the extension should be disabled if enabled by default in the file from /usr.
# It may contain comments starting with "#" at the beginning of a line or after a name.
# The file is also used in update-engine to know which extensions to download.
# Note that we don't need "{ grep || true ; }" to suppress the match return code because in for _ in $(grep...) return codes are ignored
for NAME in $(grep -h -o '^[^#]*' /sysroot/etc/flatcar/enabled-sysext.conf /sysroot/usr/share/flatcar/enabled-sysext.conf | grep -v -x -f <(grep '^-' /sysroot/etc/flatcar/enabled-sysext.conf | cut -d - -f 2-) | grep -v -P '^(-).*'); do
  ACTIVE_EXT="/etc/flatcar/sysext/flatcar-${NAME}-${VERSION}.raw"
  if [ ! -e "/sysroot/${ACTIVE_EXT}" ]; then
    echo "Did not find ${ACTIVE_EXT}" >&2
    systemctl start --quiet systemd-networkd systemd-resolved
    download_and_verify "flatcar-${NAME}.raw"
    # Note: This folder is created opaque since it does not exist in the underlay
    # For cases where we ship it later, it's best to add a "attr" call in flatcar-postinst
    # because there are many ways how the folder might have been created
    mkdir -p "/sysroot/etc/flatcar/sysext/"
    mv "/sysroot/flatcar-${NAME}.raw" "/sysroot/${ACTIVE_EXT}"
  fi
  if [ -e "/sysroot/${ACTIVE_EXT}" ]; then
    mkdir -p "/sysroot/etc/extensions"
    ln -fs "${ACTIVE_EXT}" "/sysroot/etc/extensions/flatcar-${NAME}.raw"
  else
    rm -f "/sysroot/etc/extensions/flatcar-${NAME}.raw"
  fi
done
