#!/bin/bash
cvmfs_test_name="Missing Reference Log Ignore and On-Demand Recreation"
cvmfs_test_autofs_on_startup=false


CVMFS_TEST_627_REPLICA_NAME=
cleanup() {
  [ -z $CVMFS_TEST_627_REPLICA_NAME ] || sudo cvmfs_server rmfs -f $CVMFS_TEST_627_REPLICA_NAME
}

check_tmp_dir_emptiness() {
  local tmp_dir="$1"
  local tmp_dir_entries
  echo "check stratum1 tmp directory"
  tmp_dir_entries=$(ls -a $tmp_dir | grep -v '^\.\+$' | wc -l)
  echo "$tmp_dir contains: $tmp_dir_entries"
  [ $tmp_dir_entries -eq 0 ]
}

check_reflog_contains() {
  local repo_name="$1"
  local needles="$2"
  local sqlite_file=$(mktemp "${repo_name}.sqlite.XXXXXX")
  download_from_backend $repo_name ".cvmfsreflog" $sqlite_file || return 1
  local refs="$(sqlite3 $sqlite_file "SELECT hash FROM refs")" || return 2
  local found_all=0
  for n in $needles; do
    echo -n "checking ${n}... "
    if ! contains "$refs" "$n"; then
      echo "not found"
      found_all=1
    else
      echo "found"
    fi
  done
  return $found_all
}

cvmfs_run_test() {
  logfile=$1
  local repo_dir=/cvmfs/$CVMFS_TEST_REPO
  local reflog_checksum=/var/spool/cvmfs/${CVMFS_TEST_REPO}/reflog.chksum

  local scratch_dir=$(pwd)
  mkdir reference_dir
  local reference_dir=$scratch_dir/reference_dir

  local mnt_point="$(pwd)/mountpount"
  local replica_name="$(get_stratum1_name $CVMFS_TEST_REPO)"

  echo -n "*** {0} check that we have 'sqlite3'... "
  which sqlite3 > /dev/null 2>&1 && echo "done" || { echo "fail"; return 1; }

  echo "*** {0} create a fresh repository named $CVMFS_TEST_REPO with user $CVMFS_TEST_USER"
  create_empty_repo $CVMFS_TEST_REPO $CVMFS_TEST_USER NO -g || return $?

  echo -n "*** {0} get Stratum 0 spool directory: "
  load_repo_config $CVMFS_TEST_REPO
  local s0_spool_tmp_dir="${CVMFS_SPOOL_DIR}/tmp"
  echo "$s0_spool_tmp_dir"

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "*** {1} install a cleanup function"
  trap cleanup EXIT HUP INT TERM

  echo "*** {1} create Stratum1 repository on the same machine"
  load_repo_config $CVMFS_TEST_REPO
  CVMFS_TEST_627_REPLICA_NAME=$replica_name
  create_stratum1 $replica_name                          \
                  $CVMFS_TEST_USER                       \
                  $CVMFS_STRATUM0                        \
                  /etc/cvmfs/keys/${CVMFS_TEST_REPO}.pub || return 1

  echo -n "*** {1} get Stratum 1 spool directory: "
  load_repo_config $replica_name
  local s1_spool_tmp_dir="${CVMFS_SPOOL_DIR}/tmp"
  load_repo_config $CVMFS_TEST_REPO
  echo "$s1_spool_tmp_dir"

  echo "*** {1} create a snapshot of the Stratum0 repository in the just created Stratum1 replica"
  sudo cvmfs_server snapshot $replica_name || return 2

  echo "*** {1} check that Stratum1 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s1_spool_tmp_dir || return 3

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "*** {2} extracting root hashes from Stratum0"
  local root_hash_1=$(get_manifest_field $CVMFS_TEST_REPO 'C')
  local history_1=$(get_manifest_field $CVMFS_TEST_REPO 'H')
  local meta_info_1=$(get_manifest_field $CVMFS_TEST_REPO 'M')
  local certificate_1=$(get_manifest_field $CVMFS_TEST_REPO 'X')
  echo "--> C: $root_hash_1"
  echo "--> H: $history_1"
  echo "--> M: $meta_info_1"
  echo "--> X: $certificate_1"

  echo "*** {2} check Reflog from Stratum0"
  check_reflog_contains $CVMFS_TEST_REPO "$root_hash_1 $history_1 $meta_info_1 $certificate_1" || return 4

  echo "*** {2} check Reflog from Stratum1"
  check_reflog_contains $replica_name "$root_hash_1 $history_1 $meta_info_1 $certificate_1" || return 5

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "*** {3} check that Reflog is available on Stratum0"
  peek_backend_raw $CVMFS_TEST_REPO ".cvmfsreflog" || return 71

  echo "*** {3} removing Reflog from Stratum0"
  delete_from_backend $CVMFS_TEST_REPO ".cvmfsreflog" || return 72
  echo -n "0000000000000000000000000000000000000000" > $reflog_checksum

  echo "*** {3} creating a tag in Stratum0"
  cvmfs_server tag -a "foobar"                          \
                   -m "new tag to create a new history" \
                   -h "$root_hash_1" $CVMFS_TEST_REPO || return 6

  echo "*** {3} check that Stratum0 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s0_spool_tmp_dir || return 120

  echo "*** {3} extracting root hashes from Stratum0"
  local root_hash_2=$(get_manifest_field $CVMFS_TEST_REPO 'C')
  local history_2=$(get_manifest_field $CVMFS_TEST_REPO 'H')
  local meta_info_2=$(get_manifest_field $CVMFS_TEST_REPO 'M')
  local certificate_2=$(get_manifest_field $CVMFS_TEST_REPO 'X')
  echo "--> C: $root_hash_2"
  echo "--> H: $history_2"
  echo "--> M: $meta_info_2"
  echo "--> X: $certificate_2"

  echo "*** {3} check hash sanity"
  [ x"$root_hash_2"    = x"$root_hash_1"   ] || return  7
  [ x"$history_2"     != x"$history_1"     ] || return  8
  [ x"$meta_info_2"    = x"$meta_info_1"   ] || return  9
  [ x"$certificate_2"  = x"$certificate_1" ] || return 10

  echo "*** {3} check that Reflog is gone on Stratum0"
  peek_backend_raw $CVMFS_TEST_REPO ".cvmfsreflog" && return 11

  echo "*** {3} check that Reflog from Stratum1 doesn't contain new history"
  check_reflog_contains $replica_name "$history_2" && return 12

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "*** {4} Update metainfo in Stratum 0"
  local json_file="test_json_file.json"
  cat >> $json_file << EOF
{
  "administrator" : "Rene Meusel",
  "email"         : "dont.send.me.spam@cern.ch",
  "organisation"  : "CERN",
  "description"   : "This is just a test repository"
}
EOF

  cvmfs_server update-repoinfo -f $json_file $CVMFS_TEST_REPO || return 13

  echo "*** {4} check that Stratum0 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s0_spool_tmp_dir || return 121

  echo "*** {4} extracting root hashes from Stratum0"
  local root_hash_3=$(get_manifest_field $CVMFS_TEST_REPO 'C')
  local history_3=$(get_manifest_field $CVMFS_TEST_REPO 'H')
  local meta_info_3=$(get_manifest_field $CVMFS_TEST_REPO 'M')
  local certificate_3=$(get_manifest_field $CVMFS_TEST_REPO 'X')
  echo "--> C: $root_hash_3"
  echo "--> H: $history_3"
  echo "--> M: $meta_info_3"
  echo "--> X: $certificate_3"

  echo "*** {4} check hash sanity"
  [ x"$root_hash_3"    = x"$root_hash_1"   ] || return 13
  [ x"$history_3"     != x"$history_1"     ] || return 14
  [ x"$history_3"      = x"$history_2"     ] || return 15
  [ x"$meta_info_3"   != x"$meta_info_1"   ] || return 16
  [ x"$certificate_3"  = x"$certificate_1" ] || return 17

  echo "*** {4} check that Reflog is still gone on Stratum0"
  peek_backend_raw $CVMFS_TEST_REPO ".cvmfsreflog" && return 18

  echo "*** {4} check that Reflog from Stratum1 doesn't contain new meta info"
  check_reflog_contains $replica_name "$meta_info_3" && return 19

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "*** {5} Update revision in Stratum0"
  start_transaction $CVMFS_TEST_REPO || return 20
  publish_repo $CVMFS_TEST_REPO      || return 21

  echo "*** {5} check that Stratum0 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s0_spool_tmp_dir || return 122

  echo "*** {5} extracting root hashes from Stratum0"
  local root_hash_4=$(get_manifest_field $CVMFS_TEST_REPO 'C')
  local history_4=$(get_manifest_field $CVMFS_TEST_REPO 'H')
  local meta_info_4=$(get_manifest_field $CVMFS_TEST_REPO 'M')
  local certificate_4=$(get_manifest_field $CVMFS_TEST_REPO 'X')
  echo "--> C: $root_hash_4"
  echo "--> H: $history_4"
  echo "--> M: $meta_info_4"
  echo "--> X: $certificate_4"

  echo "*** {5} check hash sanity"
  [ x"$root_hash_4"   != x"$root_hash_1"   ] || return 22
  [ x"$history_4"     != x"$history_3"     ] || return 23
  [ x"$meta_info_3"    = x"$meta_info_3"   ] || return 24
  [ x"$certificate_4"  = x"$certificate_1" ] || return 25

  echo "*** {5} check that Reflog is still gone on Stratum0"
  peek_backend_raw $CVMFS_TEST_REPO ".cvmfsreflog" && return 26

  echo "*** {5} check that Reflog from Stratum1 doesn't contain new root catalog"
  check_reflog_contains $replica_name "$root_hash_4" && return 27

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "*** {6} Update revision in Stratum0"
  start_transaction $CVMFS_TEST_REPO || return 28
  publish_repo $CVMFS_TEST_REPO      || return 29

  echo "*** {6} check that Stratum0 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s0_spool_tmp_dir || return 123

  echo "*** {6} extracting root hashes from Stratum0"
  local root_hash_5=$(get_manifest_field $CVMFS_TEST_REPO 'C')
  local history_5=$(get_manifest_field $CVMFS_TEST_REPO 'H')
  local meta_info_5=$(get_manifest_field $CVMFS_TEST_REPO 'M')
  local certificate_5=$(get_manifest_field $CVMFS_TEST_REPO 'X')
  echo "--> C: $root_hash_5"
  echo "--> H: $history_5"
  echo "--> M: $meta_info_5"
  echo "--> X: $certificate_5"

  echo "*** {6} check hash sanity"
  [ x"$root_hash_5"   != x"$root_hash_4"   ] || return 30
  [ x"$history_5"     != x"$history_4"     ] || return 31
  [ x"$meta_info_5"    = x"$meta_info_3"   ] || return 32
  [ x"$certificate_5"  = x"$certificate_1" ] || return 33

  echo "*** {6} check that Reflog is still gone on Stratum0"
  peek_backend_raw $CVMFS_TEST_REPO ".cvmfsreflog" && return 34

  echo "*** {6} check that Reflog from Stratum1 doesn't contain new root catalog"
  check_reflog_contains $replica_name "$root_hash_5" && return 35

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "*** {7} enable garbage collection for Stratum0"
  toggle_gc         $CVMFS_TEST_REPO || return 38
  start_transaction $CVMFS_TEST_REPO || return 39
  publish_repo      $CVMFS_TEST_REPO || return 40

  echo "*** {7} check that Stratum0 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s0_spool_tmp_dir || return 124

  echo "*** {7} extracting root hashes from Stratum0"
  local root_hash_6=$(get_manifest_field $CVMFS_TEST_REPO 'C')
  local history_6=$(get_manifest_field $CVMFS_TEST_REPO 'H')
  local meta_info_6=$(get_manifest_field $CVMFS_TEST_REPO 'M')
  local certificate_6=$(get_manifest_field $CVMFS_TEST_REPO 'X')
  echo "--> C: $root_hash_6"
  echo "--> H: $history_6"
  echo "--> M: $meta_info_6"
  echo "--> X: $certificate_6"

  echo "*** {7} check hash sanity"
  [ x"$root_hash_6"   != x"$root_hash_5"   ] || return 41
  [ x"$history_6"     != x"$history_5"     ] || return 42
  [ x"$meta_info_6"    = x"$meta_info_3"   ] || return 43
  [ x"$certificate_6"  = x"$certificate_1" ] || return 44

  echo "*** {7} remove repo tag 'foobar' (creating a new history db)"
  cvmfs_server tag -fr "foobar" $CVMFS_TEST_REPO || return 80

  echo "*** {7} check that Stratum0 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s0_spool_tmp_dir || return 125

  echo "*** {7} extracting root hashes from Stratum0"
  local root_hash_7=$(get_manifest_field $CVMFS_TEST_REPO 'C')
  local history_7=$(get_manifest_field $CVMFS_TEST_REPO 'H')
  local meta_info_7=$(get_manifest_field $CVMFS_TEST_REPO 'M')
  local certificate_7=$(get_manifest_field $CVMFS_TEST_REPO 'X')
  echo "--> C: $root_hash_7"
  echo "--> H: $history_7"
  echo "--> M: $meta_info_7"
  echo "--> X: $certificate_7"

  echo "*** {7} check hash sanity"
  [ x"$root_hash_7"    = x"$root_hash_6"   ] || return 81
  [ x"$history_7"     != x"$history_6"     ] || return 82
  [ x"$meta_info_7"    = x"$meta_info_3"   ] || return 83
  [ x"$certificate_7"  = x"$certificate_1" ] || return 84

  local gc_log_1="${scratch_dir}/gc_s0_1.log"
  local gc_stdout_log_1="${scratch_dir}/gc_s0_stdout_1.log"
  echo "*** {7} run garbage collection on Stratum0 (deleting as much as possible | logging to: $gc_log_1 and $gc_stdout_log_1)"
  cvmfs_server gc -r0 -fL "$gc_log_1" $CVMFS_TEST_REPO > $gc_stdout_log_1 2>&1 || return 45

  echo "*** {7} check that Stratum0 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s0_spool_tmp_dir || return 126

  echo "*** {7} extracting root hashes from Stratum0"
  local root_hash_8=$(get_manifest_field $CVMFS_TEST_REPO 'C')
  local history_8=$(get_manifest_field $CVMFS_TEST_REPO 'H')
  local meta_info_8=$(get_manifest_field $CVMFS_TEST_REPO 'M')
  local certificate_8=$(get_manifest_field $CVMFS_TEST_REPO 'X')
  echo "--> C: $root_hash_8"
  echo "--> H: $history_8"
  echo "--> M: $meta_info_8"
  echo "--> X: $certificate_8"

  echo "*** {7} check hash sanity"
  [ x"$root_hash_8"    = x"$root_hash_6"   ] || return 46
  [ x"$history_8"      = x"$history_7"     ] || return 47
  [ x"$meta_info_8"    = x"$meta_info_7"   ] || return 48
  [ x"$certificate_8"  = x"$certificate_7" ] || return 49

  echo "*** {7} check that Reflog is recreated on Stratum0"
  peek_backend_raw $CVMFS_TEST_REPO ".cvmfsreflog" || return 50

  echo "*** {7} check garbage collection log output"
  cat $gc_stdout_log_1 | grep 'Needs Reflog reconstruct.*yes' || return 51
  cat $gc_stdout_log_1 | grep 'reconstructing reference log'  || return 52

  echo "*** {7} check that the reflog contains sane entries"
  check_reflog_contains $CVMFS_TEST_REPO "$root_hash_6 $root_hash_5 $history_8 $history_7 $history_6 $history_5 $history_4 $history_3 $history_2 $history_1 $meta_info_3 $certificate_1" || return 53

  echo "*** {7} check that the deletion log contains sane entries"
  cat $gc_log_1 | grep "$root_hash_4" || return 54
  cat $gc_log_1 | grep "$root_hash_1" || return 55

  echo "*** {7} check that the right root catalogs are in Stratum0"
  peek_backend $CVMFS_TEST_REPO ${root_hash_1}C && return 90
  peek_backend $CVMFS_TEST_REPO ${root_hash_4}C && return 91
  peek_backend $CVMFS_TEST_REPO ${root_hash_5}C || return 92
  peek_backend $CVMFS_TEST_REPO ${root_hash_6}C || return 93

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "*** {8} create a Stratum1 snapshot"
  cvmfs_server snapshot $replica_name || return 56

  echo "*** {8} check that Stratum1 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s1_spool_tmp_dir || return 127

  echo "*** {8} check Reflog from Stratum1"
  check_reflog_contains $replica_name "$root_hash_6 $root_hash_5 $root_hash_1 $history_8 $history_1 $meta_info_3 $meta_info_1 $certificate_1" || return 104

  echo "*** {8} enable garbage collection on Stratum1"
  toggle_gc $replica_name || return 57

  local gc_log_2="${scratch_dir}/gc_s1_2.log"
  local gc_stdout_log_2="${scratch_dir}/gc_s1_stdout_2.log"
  echo "*** {8} run garbage collection on Stratum1 (deleting as much as possible | logging to: $gc_log_2 and $gc_stdout_log_2)"
  cvmfs_server gc -r0 -fL "$gc_log_2" $replica_name > $gc_stdout_log_2 2>&1 || return 58

  echo "*** {8} check that Stratum1 spooler tmp dir is empty"
  check_tmp_dir_emptiness $s1_spool_tmp_dir || return 128

  echo "*** {8} check that the reflog was not recreated"
  cat $gc_stdout_log_2 | grep    'Needs Reflog reconstruct.*no' || return 59
  cat $gc_stdout_log_2 | grep -v 'reconstructing reference log' || return 60

  echo "*** {8} check that the right root catalogs are in Stratum1"
  peek_backend $replica_name ${root_hash_1}C && return 100
  peek_backend $replica_name ${root_hash_4}C && return 101
  peek_backend $replica_name ${root_hash_5}C || return 102
  peek_backend $replica_name ${root_hash_6}C || return 103

  echo "*** {8} check Reflog from Stratum1"
  check_reflog_contains $replica_name "$root_hash_6 $root_hash_5 $history_8 $meta_info_3 $certificate_1" || return 105

  return 0
}
