#!/bin/bash

cvmfs_test_name="Metalink"
cvmfs_test_autofs_on_startup=false
cvmfs_test_suites="quick"

get_content_hash() {
  local full_file_path="$1"
  attr -qg hash "$full_file_path"
}

CVMFS_TEST_691_METALINK1_PID=
CVMFS_TEST_691_METALINK2_PID=
CVMFS_TEST_691_INTERNAL2_PID=
CVMFS_TEST_691_EXTERNAL1_PID=
CVMFS_TEST_691_EXTERNAL2_PID=
cleanup() {
  echo "running cleanup()"
  [ -z $CVMFS_TEST_691_METALINK1_PID ] || sudo kill $CVMFS_TEST_691_METALINK1_PID
  [ -z $CVMFS_TEST_691_METALINK2_PID ] || sudo kill $CVMFS_TEST_691_METALINK2_PID
  [ -z $CVMFS_TEST_691_INTERNAL2_PID ] || sudo kill $CVMFS_TEST_691_INTERNAL2_PID
  [ -z $CVMFS_TEST_691_EXTERNAL1_PID ] || sudo kill $CVMFS_TEST_691_EXTERNAL1_PID
  [ -z $CVMFS_TEST_691_EXTERNAL2_PID ] || sudo kill $CVMFS_TEST_691_EXTERNAL2_PID
}

cvmfs_run_test() {
  logfile=$1
  src_location=$2
  local repo_dir="/cvmfs/${CVMFS_TEST_REPO}"
  local scratch_dir="$(pwd)"

  echo "*** create a fresh repository named $CVMFS_TEST_REPO with user $CVMFS_TEST_USER"
  echo "*** Note: cvmfs_server mkfs -X --> enabled external files"
  create_empty_repo $CVMFS_TEST_REPO $CVMFS_TEST_USER NO -g -z -X -Z none || return $?

  echo "*** Disable auto gc"
  sudo sed -i -e /^CVMFS_AUTO_GC=/d /etc/cvmfs/repositories.d/${CVMFS_TEST_REPO}/server.conf
  echo "CVMFS_AUTO_GC=false" | sudo tee -a /etc/cvmfs/repositories.d/${CVMFS_TEST_REPO}/server.conf

  echo "*** get some global base paths and configs"
  load_repo_config $CVMFS_TEST_REPO
  local cvmfs_mnt="${CVMFS_SPOOL_DIR}/rdonly"
  local cvmfs_cache="${CVMFS_CACHE_BASE}/$CVMFS_TEST_REPO"
  local metalink_internal_port=8691
  local metalink_external_port=9691
  local refused_port=10691
  local internal_port2=11691
  local external_port1=12691
  local external_port2=13691
  local metalink_internal_http_base="http://localhost:$metalink_internal_port"
  local metalink_external_http_base="http://localhost:$metalink_external_port"
  local refused_base="http://localhost:$refused_port"
  local internal_http_base1="http://localhost"
  local internal_http_base2="http://localhost:$internal_port2"
  local external_http_base1="http://localhost:$external_port1"
  local external_http_base2="http://localhost:$external_port2"
  local client_config="/etc/cvmfs/repositories.d/${CVMFS_TEST_REPO}/client.conf"

  echo "*** install a disaster cleanup"
  trap cleanup EXIT HUP INT TERM || return $?

  echo "*** fill repository with external files"
  start_transaction $CVMFS_TEST_REPO                             || return $?
  mkdir -p ${repo_dir}/external                                  || return 1
  echo "Hello ext1" >${repo_dir}/external/file1                  || return 2
  echo "Hello ext2" >${repo_dir}/external/file2                  || return 3

  echo "*** creating CVMFS revision with external files"
  publish_repo $CVMFS_TEST_REPO -v || return $?

  echo "*** fill repository with internal files"
  start_transaction $CVMFS_TEST_REPO                             || return $?
  mkdir -p ${repo_dir}/internal                                  || return 4
  echo "Hello int1" >${repo_dir}/internal/file1                  || return 5
  echo "Hello int2" >${repo_dir}/internal/file2                  || return 6

  echo "*** creating CVMFS revision with native/internal files"
  publish_repo $CVMFS_TEST_REPO -v -N || return $?

  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

  local internal_storage2="${scratch_dir}/internal_files2"
  echo "*** Creating internal storage directory2 '$internal_storage2'"
  mkdir -p $internal_storage2 || return 33

  local external_storage1="${scratch_dir}/external_files1"
  echo "*** Creating external storage directory1 '$external_storage1'"
  mkdir -p $external_storage1 || return 34

  local external_storage2="${scratch_dir}/external_files2"
  echo "*** Creating external storage directory2 '$external_storage2'"
  mkdir -p $external_storage2 || return 35

  echo "*** Copying all repo files to second internal server area"
  cp -r /srv/cvmfs/$CVMFS_TEST_REPO/* $internal_storage2            || return 36

  echo "*** Move files to separate storage areas"
  # after this section file1 will be in all 4 storage areas
  # but file2 will be only in the storage2 areas, internal & external
  local object_hash2=$(get_content_hash ${cvmfs_mnt}/internal/file2)
  delete_from_backend $CVMFS_TEST_REPO $(make_path $object_hash2)   || return 37
  mkdir -p ${external_storage1}/external
  echo "Hello ext1" >${external_storage1}/external/file1            || return 38
  mkdir -p ${external_storage2}/external
  echo "Hello ext1" >${external_storage2}/external/file1            || return 39
  echo "Hello ext2" >${external_storage2}/external/file2            || return 40

  echo "*** Configure $CVMFS_TEST_REPO metalinks and remount"
  (
  cat <<!EOF!
  CVMFS_METALINK_URL="$metalink_internal_http_base/cvmfs/@fqrn@"
  CVMFS_EXTERNAL_METALINK="$refused_base;$metalink_external_http_base"
  CVMFS_METALINK_RESET_AFTER=3
  #CVMFS_DEBUGLOG="$scratch_dir/cvmfs-debug.log"
!EOF!
  ) | sudo tee --append $client_config
  cvmfs_suid_helper rw_umount     $CVMFS_TEST_REPO       || return 42
  cvmfs_suid_helper rdonly_umount $CVMFS_TEST_REPO       || return 43
  # the next two lines along with CVMFS_DEBUGLOG enable debugging
  #sudo sed -i '/^cvmfs2/s/allow_other/debug,allow_other/' /etc/fstab
  #sudo systemctl daemon-reload
  sudo cvmfs_server mount $CVMFS_TEST_REPO               || return 44

  local internal2_log="${scratch_dir}/internal2.log"
  echo "*** Start second HTTP server to serve internal files (logging to $internal2_log)"
  CVMFS_TEST_691_INTERNAL2_PID="$(open_http_server $internal_storage2 $internal_port2 $internal2_log)"
  [ ! -z $CVMFS_TEST_691_INTERNAL2_PID ] && kill -0 $CVMFS_TEST_691_INTERNAL2_PID || { echo "fail"; return 45; }

  echo "*** HTTP server running with PID $CVMFS_TEST_691_INTERNAL2_PID"

  local external1_log="${scratch_dir}/external1.log"
  echo "*** Start an HTTP server to serve external files (logging to $external1_log)"
  CVMFS_TEST_691_EXTERNAL1_PID="$(open_http_server $external_storage1 $external_port1 $external1_log)"
  [ ! -z $CVMFS_TEST_691_EXTERNAL1_PID ] && kill -0 $CVMFS_TEST_691_EXTERNAL1_PID || { echo "fail"; return 46; }
  echo "*** HTTP server running with PID $CVMFS_TEST_691_EXTERNAL1_PID"

  local external2_log="${scratch_dir}/external2.log"
  echo "*** Start another HTTP server to serve external files (logging to $external2_log)"
  CVMFS_TEST_691_EXTERNAL2_PID="$(open_http_server $external_storage2 $external_port2 $external2_log)"
  [ ! -z $CVMFS_TEST_691_EXTERNAL2_PID ] && kill -0 $CVMFS_TEST_691_EXTERNAL2_PID || { echo "fail"; return 47; }
  echo "*** HTTP server running with PID $CVMFS_TEST_691_EXTERNAL2_PID"

  local metalink_internal_log="${scratch_dir}/metalink_internal.log"
  echo "*** Start internal Metalink server (logging to $metalink_internal_log)"
  CVMFS_TEST_691_METALINK1_PID="$(open_metalink_server "$internal_http_base1 $internal_http_base2" $metalink_internal_port $metalink_internal_log)"
  [ ! -z $CVMFS_TEST_691_METALINK1_PID ] && kill -0 $CVMFS_TEST_691_METALINK1_PID || { echo "fail"; return 48; }
  echo "*** Metalink server running with PID $CVMFS_TEST_691_METALINK1_PID"

  local metalink_external_log="${scratch_dir}/metalink_external.log"
  echo "*** Start external Metalink server (logging to $metalink_external_log)"
  CVMFS_TEST_691_METALINK2_PID="$(open_metalink_server "-r $external_http_base2 $external_http_base1" $metalink_external_port $metalink_external_log)"
  [ ! -z $CVMFS_TEST_691_METALINK2_PID ] && kill -0 $CVMFS_TEST_691_METALINK2_PID || { echo "fail"; return 49; }
  echo "*** Metalink server running with PID $CVMFS_TEST_691_METALINK2_PID"

  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

  local test_pipe=$(grep ^CVMFS_TALK_SOCKET= $client_config | cut -d= -f2)

  echo "*** Read first file from first internal server"
  cat ${repo_dir}/internal/file1                             || return 51
  [ "$(attr -qg host $cvmfs_mnt)" = "$internal_http_base1" ] || return 52

  # This causes an error on some machines but not others.
  # It's not a vital check so comment it out.
  #echo "*** Try reading second internal file, should fail"
  #! cat ${repo_dir}/internal/file2                           || return 53
  #
  #echo "*** Switch to next host and try again"
  echo "*** Switch to next host to read next file"
  sudo cvmfs_talk -p $test_pipe host switch                  || return 54
  cat ${repo_dir}/internal/file2                             || return 55
  [ "$(attr -qg host $cvmfs_mnt)" = "$internal_http_base2" ] || return 56

  echo "*** Read first file from first external server"
  cat ${repo_dir}/external/file1                                      || return 61
  [ "$(attr -qg external_host $cvmfs_mnt)" = "$external_http_base1" ] || return 62

  # This one usually works but sometimes failed on Fedora 40
  # so comment it out too.
  #echo "*** Try reading second external file, should fail"
  #! cat ${repo_dir}/external/file2                                    || return 63
  #
  #echo "*** Switch to next host and try again"
  echo "*** Switch to next external host to read next file"
  sudo cvmfs_talk -p $test_pipe external host switch                  || return 64
  cat ${repo_dir}/external/file2                                      || return 65
  [ "$(attr -qg external_host $cvmfs_mnt)" = "$external_http_base2" ] || return 66

  echo "*** Remove the first internal file from the cache"
  local object_hash1=$(get_content_hash ${cvmfs_mnt}/internal/file1)
  local cache_object1="${cvmfs_cache}/$(get_hash_path $object_hash1)"
  sudo rm $cache_object1                                     || return 70

  echo "*** Wait four seconds for the metalink query to expire"
  sleep 4

  echo "*** Read first internal file again and verify it came from first server"
  cat ${repo_dir}/internal/file1                             || return 71
  [ "$(attr -qg host $cvmfs_mnt)" = "$internal_http_base1" ] || return 72


  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

  # This part has to be last

  echo "*** test cvmfs_talk metalink commands"
  local test_dom="example.com"
  local test_dom2="cern.ch"
  local test_hosts="http://$test_dom:80;http://$test_dom2:80"
  local test_proxy="http://$test_dom:3128"

  echo "*** test external host set"
  sudo cvmfs_talk -p $test_pipe external metalink set "$test_hosts"        || return 80
  echo "*** test external metalink info"
  sudo cvmfs_talk -p $test_pipe external metalink info                     || return 81
  sudo cvmfs_talk -p $test_pipe external metalink info | grep "$test_dom"  || return 82
  echo "*** test external host switch"
  sudo cvmfs_talk -p $test_pipe external metalink switch                   || return 83
  sudo cvmfs_talk -p $test_pipe external metalink info | grep "Active.*$test_dom2"  || return 84
  echo "*** test external proxy set"
  sudo cvmfs_talk -p $test_pipe metalink set "$test_proxy"       || return 85
  echo "*** test metalink info"
  sudo cvmfs_talk -p $test_pipe metalink info                    || return 86
  sudo cvmfs_talk -p $test_pipe metalink info | grep "$test_dom" || return 87

  return 0
}
