#!/bin/bash

cvmfs_test_name="Garbage Collection on Replication Server"
cvmfs_test_autofs_on_startup=false

inflate_file() {
  local destination_file=$1
  local source_file=$2
  local desired_file_size=$3

  touch $destination_file
  while [ $(stat -c %s $destination_file) -lt $desired_file_size ]; do
    cat $source_file >> $destination_file
  done
}

produce_files_1_in() {
  local working_dir=$1
  pushdir $working_dir

  mkdir dir1
  mkdir dir1/sub1
  touch dir1/sub1/.cvmfscatalog
  mkdir dir1/sub2
  touch dir1/sub2/.cvmfscatalog
  mkdir dir1/sub3
  touch dir1/sub3/.cvmfscatalog

  touch dir1/shakespeare
  echo "That thou art blamed shall not be thy defect,"       >> dir1/shakespeare
  echo "For slander's mark was ever yet the fair;"           >> dir1/shakespeare
  echo "The ornament of beauty is suspect,"                  >> dir1/shakespeare
  echo "A crow that flies in heaven's sweetest air."         >> dir1/shakespeare
  echo "So thou be good, slander doth but approve"           >> dir1/shakespeare
  echo "Thy worth the greater, being wooed of time;"         >> dir1/shakespeare
  echo "For canker vice the sweetest buds doth love,"        >> dir1/shakespeare
  echo "And thou present'st a pure unstained prime."         >> dir1/shakespeare
  echo "Thou hast passed by the ambush of young days"        >> dir1/shakespeare
  echo "Either not assailed, or victor being charged;"       >> dir1/shakespeare
  echo "Yet this thy praise cannot be so thy praise,"        >> dir1/shakespeare
  echo "To tie up envy, evermore enlarged,"                  >> dir1/shakespeare
  echo "   If some suspect of ill masked not thy show,"      >> dir1/shakespeare
  echo "   Then thou alone kingdoms of hearts shouldst owe." >> dir1/shakespeare

  touch dir1/sub1/many_shakespeares
  touch dir1/sub2/alotof_shakespeares
  touch dir1/sub3/shakespeare_army
  inflate_file dir1/sub1/many_shakespeares dir1/shakespeare              1000000
  inflate_file dir1/sub2/alotof_shakespeares dir1/sub1/many_shakespeares 10000000
  inflate_file dir1/sub3/shakespeare_army dir1/sub2/alotof_shakespeares  50000000

  popdir
}

produce_files_2_in() {
  local working_dir=$1
  pushdir $working_dir

  mkdir dir2
  mkdir dir2/sub1
  touch dir2/sub1/.cvmfscatalog
  mkdir dir2/sub2
  touch dir2/sub2/.cvmfscatalog
  mkdir dir2/sub3
  touch dir2/sub3/.cvmfscatalog

  cp_bin dir2/sub1
  cp_bin dir2/sub2
  cp_bin dir2/sub3

  touch dir1/sub1/warfield
  echo "Our life is like a thorny rose"                                                   >> dir1/sub1/warfield
  echo "Not perfect, but always beautiful"                                                >> dir1/sub1/warfield
  echo "The thorns represent the hardships in our lives. "                                >> dir1/sub1/warfield
  echo "The delicate red petals represent the fun and beautiful things in our .........." >> dir1/sub1/warfield

  touch dir1/sub2/jackson
  echo "Didn't want a battle,"                                >> dir1/sub2/jackson
  echo "Yet you declared war,"                                >> dir1/sub2/jackson
  echo "Each knock you gave me made me stronger than before," >> dir1/sub2/jackson
  echo "I will not give up,"                                  >> dir1/sub2/jackson
  echo "I will not give in,"                                  >> dir1/sub2/jackson
  echo "You won't make me fall,"                              >> dir1/sub2/jackson
  echo "I won't let you win."                                 >> dir1/sub2/jackson

  popdir
}

produce_files_3_in() {
  local working_dir=$1
  pushdir $working_dir

  mkdir dir3
  cp_bin dir3

  mkdir dir3/sub1
  touch dir3/sub1/.cvmfscatalog
  mkdir dir3/sub2
  touch dir3/sub2/.cvmfscatalog
  mkdir dir3/sub3
  touch dir3/sub3/.cvmfscatalog

  touch dir3/kafka
  echo "Deeply lost in the night."                                   >> dir3/kafka
  echo ""                                                            >> dir3/kafka
  echo "Just as one sometimes lowers one's head to reflect, "        >> dir3/kafka
  echo "thus to be utterly lost in the night. "                      >> dir3/kafka
  echo "All around people are asleep. It's just play acting, "       >> dir3/kafka
  echo "an innocent self-deception, that they sleep in houses, "     >> dir3/kafka
  echo "in safe beds, under a safe roof, stretched out or "          >> dir3/kafka
  echo "curled up on mattresses, in sheets, under blankets; "        >> dir3/kafka
  echo "in reality they have flocked together as they had once "     >> dir3/kafka
  echo "upon a time and again later in a deserted region, a camp "   >> dir3/kafka
  echo "in the open, a countless number of men, an army, a people, " >> dir3/kafka
  echo "under a cold sky on cold earth, collapsed where once they "  >> dir3/kafka
  echo "had stood, forehead pressed on the arm, face to the "        >> dir3/kafka
  echo "ground, breathing quietly."                                  >> dir3/kafka
  echo ""                                                            >> dir3/kafka
  echo "And you are watching, are one of the watchmen, you find "    >> dir3/kafka
  echo "the next one by brandishing a burning stick from the "       >> dir3/kafka
  echo "brushwood pile beside you."                                  >> dir3/kafka
  echo ""                                                            >> dir3/kafka
  echo "Why are you watching?"                                       >> dir3/kafka
  echo ""                                                            >> dir3/kafka
  echo "Someone must watch, it is said. Someone must be there."      >> dir3/kafka

  touch dir3/sub1/many_kafkas
  touch dir3/sub2/alotof_kafkas
  touch dir3/sub3/kafka_army
  inflate_file dir3/sub1/many_kafkas dir3/kafka              1000000
  inflate_file dir3/sub2/alotof_kafkas dir3/sub1/many_kafkas 10000000
  inflate_file dir3/sub3/kafka_army dir3/sub2/alotof_kafkas  50000000

  rm -f dir2/sub1/*

  touch dir2/sub1/tharrington
  echo "Forever we remain oblivious to the future, "    >> dir2/sub1/tharrington
  echo "lost to the past and enduring our torture. "    >> dir2/sub1/tharrington
  echo "Forever we take chances to settle our scores, " >> dir2/sub1/tharrington
  echo "losing some battles and winning some wars. "    >> dir2/sub1/tharrington
  echo "Forever .........."                             >> dir2/sub1/tharrington

  rm -fR dir1

  popdir
}

produce_files_4_in() {
  local working_dir=$1
  pushdir $working_dir

  rm -fR dir3

  popdir
}

CVMFS_TEST_576_REPLICA_NAME=""
cleanup() {
  echo "running cleanup()"
  if [ ! -z $CVMFS_TEST_576_REPLICA_NAME ]; then
    sudo cvmfs_server rmfs -f $CVMFS_TEST_576_REPLICA_NAME
  fi
}

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

  mkdir reference_dir1
  mkdir reference_dir2
  mkdir reference_dir3
  mkdir reference_dir4
  local reference_dir1=$scratch_dir/reference_dir1
  local reference_dir2=$scratch_dir/reference_dir2
  local reference_dir3=$scratch_dir/reference_dir3
  local reference_dir4=$scratch_dir/reference_dir4

  local condemned_clgs=""
  local preserved_clgs=""

  echo "create a fresh repository named $CVMFS_TEST_REPO with user $CVMFS_TEST_USER and disabled auto-tagging"
  create_empty_repo $CVMFS_TEST_REPO $CVMFS_TEST_USER NO -g -z || return $?
  condemned_clgs="$condemned_clgs $(get_current_root_catalog $CVMFS_TEST_REPO)"

  echo "disable automatic garbage collection"
  disable_auto_garbage_collection $CVMFS_TEST_REPO || return $?

  # ============================================================================

  # echo "install a desaster cleanup function"
  trap cleanup EXIT HUP INT TERM || return $?

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

  local gc_log="${scratch_dir}/gc.log"
  echo "disable automatic garbage collection and configure deletion log $gc_log"
  disable_auto_garbage_collection $replica_name || return $?
  echo "CVMFS_GC_DELETION_LOG=$gc_log" | sudo tee -a "/etc/cvmfs/repositories.d/${replica_name}/server.conf" || return 100

  echo "create a Snapshot of the Stratum0 repository in the just created Stratum1 replica"
  cvmfs_server snapshot $replica_name || return 2

  echo -n "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 "check that tmp directory is empty after replicating"
  local s1_tmp_no=
  s1_tmp_no=$(ls "$s1_spool_tmp_dir" | wc -l)
  echo "$s1_spool_tmp_dir contains: $s1_tmp_no entries"
  [ $s1_tmp_no -eq 0 ] || return 101

  # ============================================================================

  echo "starting transaction to edit repository (1)"
  start_transaction $CVMFS_TEST_REPO || return $?

  echo "putting some stuff in the new repository"
  produce_files_1_in $repo_dir || return 3

  echo "putting exactly the same stuff in the scratch spaces for comparison"
  produce_files_1_in $reference_dir1 || return 4

  echo "creating CVMFS snapshot"
  publish_repo $CVMFS_TEST_REPO > publish_1.log 2>&1 || return $?
  condemned_clgs="$condemned_clgs $(get_current_root_catalog $CVMFS_TEST_REPO)"

  echo "compare the results of cvmfs to our reference copy"
  compare_directories $repo_dir $reference_dir1 || return $?

  # ============================================================================

  echo "starting transaction to edit repository (2)"
  start_transaction $CVMFS_TEST_REPO || return $?

  echo "putting some stuff in the new repository"
  produce_files_2_in $repo_dir || return 5

  echo "putting exactly the same stuff in the scratch spaces for comparison"
  produce_files_1_in $reference_dir2 || return 6
  produce_files_2_in $reference_dir2 || return 6

  echo "creating CVMFS snapshot"
  publish_repo $CVMFS_TEST_REPO > publish_2.log 2>&1 || return $?
  condemned_clgs="$condemned_clgs $(get_current_root_catalog $CVMFS_TEST_REPO)"

  echo "compare the results of cvmfs to our reference copy"
  compare_directories $repo_dir $reference_dir2 || return $?

  # ============================================================================

  echo "starting transaction to edit repository (3)"
  start_transaction $CVMFS_TEST_REPO || return $?

  echo "putting some stuff in the new repository"
  produce_files_3_in $repo_dir || return 7

  echo "putting exactly the same stuff in the scratch spaces for comparison"
  produce_files_1_in $reference_dir3 || return 8
  produce_files_2_in $reference_dir3 || return 8
  produce_files_3_in $reference_dir3 || return 8

  echo "creating CVMFS snapshot"
  publish_repo $CVMFS_TEST_REPO > publish_3.log 2>&1 || return $?
  preserved_clgs="$preserved_clgs $(get_current_root_catalog $CVMFS_TEST_REPO)"

  echo "compare the results of cvmfs to our reference copy"
  compare_directories $repo_dir $reference_dir3 || return $?

  # ============================================================================

  echo "starting transaction to edit repository (4)"
  start_transaction $CVMFS_TEST_REPO || return $?

  echo "putting some stuff in the new repository"
  produce_files_4_in $repo_dir || return 9

  echo "putting exactly the same stuff in the scratch spaces for comparison"
  produce_files_1_in $reference_dir4 || return 10
  produce_files_2_in $reference_dir4 || return 10
  produce_files_3_in $reference_dir4 || return 10
  produce_files_4_in $reference_dir4 || return 10

  echo "creating CVMFS snapshot"
  publish_repo $CVMFS_TEST_REPO > publish_4.log 2>&1 || return $?
  preserved_clgs="$preserved_clgs $(get_current_root_catalog $CVMFS_TEST_REPO)"

  echo "compare the results of cvmfs to our reference copy"
  compare_directories $repo_dir $reference_dir4 || return $?

  # ============================================================================

  echo "update the stratum 1"
  cvmfs_server snapshot $replica_name

  echo "check that tmp directory is empty after replicating"
  s1_tmp_no=$(ls "$s1_spool_tmp_dir" | wc -l)
  echo "$s1_spool_tmp_dir contains: $s1_tmp_no entries"
  [ $s1_tmp_no -eq 0 ] || return 102

  # ============================================================================

  local shakespeare_object="5ccb03a6d732ff70310ba5db25756a6cd4675710"
  local chopped_shakespeare_1="f58287c6c5a608d70491519d4f3dc3b3436b607aP"
  local chopped_shakespeare_2="1e3618d857695eab0e30da35fcd9ea5e3f52f266P"
  local chopped_shakespeare_3="c3cc8c92d5ea75da3207498fae45123b8a172d21P"
  local kafka_object="fd8370662e701313534bfc2d2b860a7bf0fbf5da"
  local chopped_kafka_1="1d4964dc2146c16f2f351b153347890e2513a8eaP"
  local chopped_kafka_2="78d66a3880097d4b4bcd5145fea11498c4306ed0P"
  local chopped_kafka_3="ed021be42b51c299560b9617c6585a4c40607158P"

  echo "check if poem objects are there (stratum 0)..."
  peek_backend $CVMFS_TEST_REPO $shakespeare_object || return 11
  peek_backend $CVMFS_TEST_REPO $kafka_object       || return 11

  echo "check if poem objects are there (stratum 1)..."
  peek_backend $replica_name $shakespeare_object || return 12
  peek_backend $replica_name $kafka_object       || return 12

  echo "check if the file chunks are there (stratum 0)"
  peek_backend $CVMFS_TEST_REPO $chopped_shakespeare_1 || return 13
  peek_backend $CVMFS_TEST_REPO $chopped_shakespeare_2 || return 13
  peek_backend $CVMFS_TEST_REPO $chopped_shakespeare_3 || return 13
  peek_backend $CVMFS_TEST_REPO $chopped_kafka_1       || return 13
  peek_backend $CVMFS_TEST_REPO $chopped_kafka_2       || return 13
  peek_backend $CVMFS_TEST_REPO $chopped_kafka_3       || return 13

  echo "check if the file chunks are there (stratum 1)"
  peek_backend $replica_name $chopped_shakespeare_1 || return 14
  peek_backend $replica_name $chopped_shakespeare_2 || return 14
  peek_backend $replica_name $chopped_shakespeare_3 || return 14
  peek_backend $replica_name $chopped_kafka_1       || return 14
  peek_backend $replica_name $chopped_kafka_2       || return 14
  peek_backend $replica_name $chopped_kafka_3       || return 14

  echo "check if all catalog revisions are still there (stratum 0)"
  for clg_hash in $condemned_clgs $preserved_clgs; do
    peek_backend $CVMFS_TEST_REPO ${clg_hash}C || return 15
  done

  echo "check if all catalog revisions are still there (stratum 1)"
  for clg_hash in $condemned_clgs $preserved_clgs; do
    peek_backend $replica_name ${clg_hash}C || return 16
  done

  echo "perform basic garbage collection (stratum 0)"
  cvmfs_server gc -r0 -f $CVMFS_TEST_REPO || return 17

  echo "check if shakespeare is gone (stratum 0)"
  peek_backend $CVMFS_TEST_REPO $shakespeare_object    && return 18
  peek_backend $CVMFS_TEST_REPO $chopped_shakespeare_1 && return 18
  peek_backend $CVMFS_TEST_REPO $chopped_shakespeare_2 && return 18
  peek_backend $CVMFS_TEST_REPO $chopped_shakespeare_3 && return 18

  echo "check if shakespeare is still there (stratum 1)"
  peek_backend $replica_name $shakespeare_object    || return 19
  peek_backend $replica_name $chopped_shakespeare_1 || return 19
  peek_backend $replica_name $chopped_shakespeare_2 || return 19
  peek_backend $replica_name $chopped_shakespeare_3 || return 19

  echo "check if kafka is still there (stratum 0)"
  peek_backend $CVMFS_TEST_REPO $kafka_object    || return 20
  peek_backend $CVMFS_TEST_REPO $chopped_kafka_1 || return 20
  peek_backend $CVMFS_TEST_REPO $chopped_kafka_2 || return 20
  peek_backend $CVMFS_TEST_REPO $chopped_kafka_3 || return 20

  echo "check if kafka is still there (stratum 1)"
  peek_backend $replica_name $kafka_object    || return 21
  peek_backend $replica_name $chopped_kafka_1 || return 21
  peek_backend $replica_name $chopped_kafka_2 || return 21
  peek_backend $replica_name $chopped_kafka_3 || return 21

  echo "check if the catalog revisions 1-4 are gone (stratum 0)"
  for clg_hash in $condemned_clgs; do
    peek_backend $CVMFS_TEST_REPO ${clg_hash}C && return 22
  done

  echo "check if the catalog revisions 1-4 are still there (stratum 1)"
  for clg_hash in $condemned_clgs; do
    peek_backend $replica_name ${clg_hash}C || return 23
  done

  echo "check if the catalog revisions 5-6 are preserved (stratum 0)"
  for clg_hash in $preserved_clgs; do
    peek_backend $CVMFS_TEST_REPO ${clg_hash}C || return 24
  done

  echo "check if the catalog revisions 5-6 are preserved (stratum 1)"
  for clg_hash in $preserved_clgs; do
    peek_backend $replica_name ${clg_hash}C || return 25
  done

  # ============================================================================

  echo "check if the repository is still sane (stratum 0)"
  check_repository $CVMFS_TEST_REPO -i || return 26

  echo "check if the repository is still sane (stratum 1)"
  check_repository $CVMFS_TEST_REPO -i || return 27

  echo "check if the repository's previous revision is still sane (stratum 0)"
  check_repository $CVMFS_TEST_REPO -i -t trunk-previous || return 28

  echo "check if the repository's previous revision is still sane (stratum 1)"
  check_repository $CVMFS_TEST_REPO -i -t trunk-previous || return 29

  # ============================================================================

  echo "perform basic garbage collection (stratum 1)"
  cvmfs_server gc -r0 -f -l $replica_name || return 30

  echo "check if shakespeare is gone (stratum 0)"
  peek_backend $CVMFS_TEST_REPO $shakespeare_object    && return 31
  peek_backend $CVMFS_TEST_REPO $chopped_shakespeare_1 && return 31
  peek_backend $CVMFS_TEST_REPO $chopped_shakespeare_2 && return 31
  peek_backend $CVMFS_TEST_REPO $chopped_shakespeare_3 && return 31

  echo "check if shakespeare is gone (stratum 1)"
  peek_backend $replica_name $shakespeare_object    && return 32
  peek_backend $replica_name $chopped_shakespeare_1 && return 32
  peek_backend $replica_name $chopped_shakespeare_2 && return 32
  peek_backend $replica_name $chopped_shakespeare_3 && return 32

  echo "check that deletion log for stratum1 contains shakespeare"
  cat $gc_log | grep -q $shakespeare_object    || return 32
  cat $gc_log | grep -q $chopped_shakespeare_1 || return 32
  cat $gc_log | grep -q $chopped_shakespeare_2 || return 32
  cat $gc_log | grep -q $chopped_shakespeare_3 || return 32

  echo "check if kafka is still there (stratum 0)"
  peek_backend $CVMFS_TEST_REPO $kafka_object    || return 33
  peek_backend $CVMFS_TEST_REPO $chopped_kafka_1 || return 33
  peek_backend $CVMFS_TEST_REPO $chopped_kafka_2 || return 33
  peek_backend $CVMFS_TEST_REPO $chopped_kafka_3 || return 33

  echo "check if kafka is still there (stratum 1)"
  peek_backend $replica_name $kafka_object    || return 34
  peek_backend $replica_name $chopped_kafka_1 || return 34
  peek_backend $replica_name $chopped_kafka_2 || return 34
  peek_backend $replica_name $chopped_kafka_3 || return 34

  echo "check that deletion log doesn't contain kafka"
  cat $gc_log | grep -q $kafka_object    && return 34
  cat $gc_log | grep -q $chopped_kafka_1 && return 34
  cat $gc_log | grep -q $chopped_kafka_2 && return 34
  cat $gc_log | grep -q $chopped_kafka_3 && return 34

  echo "check if the catalog revisions 1-4 are gone (stratum 0)"
  for clg_hash in $condemned_clgs; do
    peek_backend $CVMFS_TEST_REPO ${clg_hash}C && return 35
  done

  echo "check if the catalog revisions 1-4 are gone (stratum 0)"
  for clg_hash in $condemned_clgs; do
    peek_backend $replica_name ${clg_hash}C && return 36
    cat $gc_log | grep -q ${clg_hash}C      || return 136
  done

  echo "check if the catalog revisions 5-6 are preserved (stratum 0)"
  for clg_hash in $preserved_clgs; do
    peek_backend $CVMFS_TEST_REPO ${clg_hash}C || return 37
  done

  echo "check if the catalog revisions 5-6 are preserved (stratum 1)"
  for clg_hash in $preserved_clgs; do
    peek_backend $replica_name ${clg_hash}C || return 38
    cat $gc_log | grep -q ${clg_hash}C      && return 138
  done

  # ============================================================================

  echo "check if the repository is still sane (stratum 0)"
  check_repository $CVMFS_TEST_REPO -i || return 39

  echo "check if the repository is still sane (stratum 1)"
  check_repository $CVMFS_TEST_REPO -i || return 40

  echo "check if the repository's previous revision is still sane (stratum 0)"
  check_repository $CVMFS_TEST_REPO -i -t trunk-previous || return 41

  echo "check if the repository's previous revision is still sane (stratum 1)"
  check_repository $CVMFS_TEST_REPO -i -t trunk-previous || return 42

  return 0
}
