#!/bin/sh
# Consume test requests from AMQP queue and feed them to debci-test
set -eu

short_options='c:t:'
long_options='count:,tag:,do-request'

usage() {
  cat <<EOF
usage: debci-worker [OPTIONS]

Options:
  -c COUNT, --count COUNT   Exit after processing COUNT requests
  -t TAG, --tag TAG         listen to queues which require this platform tag
                            (can be specified multiple times)

$@
EOF
}

debci_base_dir=$(readlink -f $(dirname $(readlink -f $0))/..)
. $debci_base_dir/lib/environment.sh
. $debci_base_dir/lib/functions.sh

tags=''
count=''

# Process one request. Read the AMQP message from stdin.
do_request() {
  local request
  local pkg
  local suite
  local opts

  read request || true  # we expect EOF and thus read to fail
  set -- $request
  if [ $# -eq 0 ]; then
    return
  fi
  pkg="$1"
  suite="${2:-}"
  if [ -n "$suite" ]; then
    shift 2
  else
    suite="$debci_suite"
    shift 1
  fi

  releases=""
  opts=""
  run_id=0
  for param in $@; do
    case "$param" in
      autopkgtest:*)
        arg=${param#autopkgtest:}
        export debci_autopkgtest="${arg}"
        ;;
      trigger:*)
        opts="$opts --trigger=${param#trigger:}"
        ;;
      pin-packages:*)
        arg=${param#pin-packages:}
        opts="$opts --pin-packages=$arg"
        ;;
      run-id:*)
        arg=${param#run-id:}
        run_id="${arg}"
        opts="$opts --run-id=${run_id}"
        ;;
      extra-apt-source:*)
        arg=${param#extra-apt-source:}
        opts="$opts --extra-apt-source=$arg"
        ;;
      signing-key:*)
        arg=${param#signing-key:}
        opts="$opts --signing-key=$arg"
        ;;
      backend:*)
        arg=${param#backend:}
        if [ -n "$arg" ]; then
          export debci_backend="${arg}"
        fi
        ;;
      *)
        echo "Unknown test parameter: $param" >&2
        ;;
    esac
  done

  if ! (echo "$pkg" | grep -q '^[a-z0-9.+-]\+$'); then
    log "W: invalid package name: $pkg, ignoring"
    return
  fi

  log "$pkg $suite/$debci_arch/$debci_backend (${run_id}) started (autopkgtest: ${debci_autopkgtest})"

  tmp_dir=$(mktemp --directory --tmpdir debci-worker-${run_id}-XXXXXXXXXX)
  trap 'rm -rf ${tmp_dir}' INT TERM EXIT

  # run the test
  tmp_autopkgtest_incoming_dir="$(debci-config --data-dir="${tmp_dir}" --values-only autopkgtest_incoming_dir)"
  result_dir="$(debci_autopkgtest_incoming_dir="${tmp_autopkgtest_incoming_dir}" autopkgtest_incoming_dir_for_package "$pkg")/${run_id}"
  mkdir -p "$(dirname "${result_dir}")"
  rc=
  debci-test \
    --quiet \
    --suite "$suite" \
    --arch "${debci_arch}" \
    --backend "${debci_backend}" \
    --output-directory="${result_dir}" \
    $opts \
    "$pkg" || rc="$?"

  if [ -n "${rc}" ]; then
    log_error "W: $pkg $suite/$debci_arch/$debci_backend (${run_id}): debci-test failed with exit code ${rc}"
  fi

  if [ -z "$result_dir" ]; then
    log_error "W: $pkg $suite/$debci_arch/$debci_backend (${run_id}): no results directory, defining one"
    result_dir="${tmp_dir}/autopkgtest-incoming/${suite}/${debci_arch}/${pkg}/${run_id}"
  fi
  if [ ! -d "${result_dir}" ]; then
    log_error "W: $pkg $suite/$debci_arch/$debci_backend (${run_id}): results directory missing, creating it"
    mkdir -p "${result_dir}"
  fi

  if [ ! -s "$result_dir/exitcode" ]; then
    log_error "W: $pkg $suite/$debci_arch/$debci_backend (${run_id}): no exitcode"
    echo 20 > "$result_dir/exitcode"
  fi

  if [ ! -s "$result_dir/log.gz" ]; then
    # Synthethize a log file
    log_error "W: $pkg $suite/$debci_arch/$debci_backend (${run_id}): no log file"
    cat >> "$result_dir/log" <<EOF
This test did not finish correctly. Maybe there was a crash on the worker,
maybe it was cancelled by an admin.
EOF
    gzip "$result_dir/log"
  fi

  # debci_suite has to passed in to report_status because the worker is running
  # using the "default" suite which is usually unstable, which might be
  # different from the suite in which the test has actually just been executed.
  case $(cat "$result_dir/exitcode") in
      0|2)
        status=pass
        ;;
      4|6|12|14)
        status=fail
        ;;
      8)
        status=neutral
        ;;
      *)
        status=tmpfail
        ;;
  esac
  log "$pkg $suite/$debci_arch/$debci_backend (${run_id}) ${status}"


  # move result into results dir and let debci-publisher send the results
  result_dir=${result_dir##$tmp_dir/}
  result_file="${tmp_dir}/${run_id}.${debci_backend}.tar.gz"
  ( cd $tmp_dir && tar czf "${result_file}" "$result_dir" )
  mkdir -p "${debci_results_dir}"
  mv "${result_file}" "${debci_results_dir}/"
  stamp_file="$(basename "${result_file}" .tar.gz).stamp"
  touch "${debci_results_dir}/${stamp_file}"
}

# parse CLI arguments
while true; do
  case "$1" in
    -t|--tag)
      tags="${tags}_$2"
      shift 2
      ;;
    -c|--count)
      count="--count $2"
      shift 2
      ;;
    --do-request)
      do_request
      exit 0
      ;;
    *)
      break
      ;;
  esac
done

amqp-declare-queue \
  --url="$debci_amqp_server" \
  $debci_amqp_tools_options \
  --durable \
  --queue="$debci_amqp_results_queue" > /dev/null

# if the user calls this, we run forever with consuming messages;
# amqp-consume calls ourselves with the (hidden) --do-request option
amqp_queue="$(debci amqp print-queue --arch="${debci_arch}" --backend="${debci_backend}")"
log "I: Connecting to AMQP queue $amqp_queue on ${debci_amqp_server_display}"
debci amqp declare-queue --arch="${debci_arch}" --backend="${debci_backend}"
exec amqp-consume \
  --url ${debci_amqp_server} \
  $debci_amqp_tools_options \
  --queue=$amqp_queue \
  --prefetch-count 1 \
  $count \
  -- \
  $0 --do-request --arch="${debci_arch}" --backend="${debci_backend}"
