Skip to content
Snippets Groups Projects
couchdb.sh 7.17 KiB
#!/bin/bash
# ================================================================================
#
# LOCALDUMP :: COUCHDB
# create gzipped plain text backups from each scheme
#
# --------------------------------------------------------------------------------
# ah - Axel Hahn <axel.hahn@iml.unibe.ch>
# ds - Daniel Schueler <daniel.schueler@iml.unibe.ch>
#
# 2017-03-24  ah,ds  v0.9  backup
# 2017-03-27  .....  v1.0  restore
# 2022-01-20         v1.1  fixes with shellcheck
# 2022-03-17         v1.2  WIP: add lines with prefix __DB__
# 2022-10-07  ah     v1.3  unescape regex with space to prevent "grep: warning: stray \ before white space"
# ================================================================================

if [ -z "$BACKUP_TARGETDIR" ]; then
  echo ERROR: you cannot start $(basename $0) directly
  rc=$rc+1
  exit 1
fi

# --------------------------------------------------------------------------------
# CONFIG
# --------------------------------------------------------------------------------

dirPythonPackages=/usr/lib/python2.7/site-packages


# --------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------

# make an couch api request
# param  string  method ... one of GET|POST|DELETE
# param  string  relative url, i.e. _all_dbs or _stats
function _couchapi(){
  method=$1
  apiurl=$2
  outfile=$3

  sParams=
  # sParams="$sParams -u ${couchdbuser}:${couchdbpw}"
  sParams="$sParams -X ${method}"
  sParams="$sParams ${COUCHDB_URL}${apiurl}"
  if [ ! -z "$outfile" ]; then
    sParams="$sParams -o ${outfile}"
  fi
  curl $sParams 2>/dev/null
}

function _couchGet(){
  _couchapi GET $*
}

function _getDblist(){
   _couchGet _all_dbs | sed 's#\"#\n#g' | grep -Ev "^(\[|\,|\])$"
}


# ---------- CONFIG/ INSTANCES

# get valid configured instances
function getInstances(){
 for mycfg in $(ls -1 "~/.iml_backup/couchdb/*.config" )
 do
   if . "$mycfg"; then
     echo $(basename "${mycfg}" | cut -f 1 -d ".")
   fi
 done
}


# load the config of an existing instance
# see getInstances to get valid names
# param  string  name of the instance to load
function loadInstance(){
  COUCHDB_URL=
  if ! . "~/.iml_backup/couchdb/${1}.config" ; then
    color error
    echo ERROR: invalid instance: $1 - the config file cannot be sourced
    color reset
    exit 1
  fi
  if [ -z "${COUCHDB_URL}" ]; then
    color error
    echo "ERROR: invalid instance: $1 - the config file has no COUCHDB_URL"
    color reset
    exit 1
  fi

  # parse ${COUCHDB_URL} ...
  couchdbhost=$(echo "${COUCHDB_URL}" | cut -f 3 -d "/" | cut -f 2 -d "@" | cut -f 1 -d ":")
  couchdbport=$(echo "${COUCHDB_URL}" | cut -f 3 -d "/" | cut -f 2 -d "@" | cut -f 2 -d ":")
  couchdbuser=$(echo "${COUCHDB_URL}" | cut -f 3 -d "/" | cut -f 1 -d "@" | cut -f 1 -d ":")
  couchdbpw=$(  echo "${COUCHDB_URL}" | cut -f 3 -d "/" | cut -f 1 -d "@" | cut -f 2 -d ":")

}


# ---------- BACKUP

# backup with loop over instances
function doBackup(){
  # for mycfg in `ls -1 ~/.iml_backup/couchdb/*.config`
  for COUCHDB_INSTANCE in $(getInstances)
  do
    loadInstance "$COUCHDB_INSTANCE"

      echo "--- instance: $COUCHDB_INSTANCE"
      if curl --head -X GET "$COUCHDB_URL" 2>/dev/null | grep "^HTTP.* 200 "; then

        _doBackupOfSingleInstance

      else
        rc=$rc+1
        color error
        echo ERROR: couch DB instance is not available or canot be accessed with these credentials in config file
        # repeat curl to show the error message
        curl -X GET "$COUCHDB_URL"
        color reset
      fi

    echo
    echo "--- $(date) done."
    echo
  done
}

# make backup of all databases in a couchdb instance
# global: COUCHDB_URL
# global: COUCHDB_INSTANCE
function _doBackupOfSingleInstance(){

  create_targetdir
  mkdir -p "${BACKUP_TARGETDIR}/${COUCHDB_INSTANCE}" 2>/dev/null

  echo
  echo "    DUMP databases of instance ${COUCHDB_INSTANCE}"
  echo "    couchdbhost $couchdbhost on port $couchdbport with user $couchdbuser"
  echo

  for dbname in $(_getDblist)
  do
    echo -n "__DB__${SERVICENAME} ${COUCHDB_INSTANCE} $_dbname backup "
    OUTFILE=${BACKUP_TARGETDIR}/${COUCHDB_INSTANCE}/$(get_outfile "${dbname}").couchdbdump
    python ${dirPythonPackages}/couchdb/tools/dump.py "${COUCHDB_URL}/${dbname}" >"${OUTFILE}"

    fetchrc
    db._compressDumpfile "${OUTFILE}"

    # $myrc is last returncode - set in fetchrc
    # if [ $myrc -eq 0 ]; then
    #   echo -n "gzip ... "
    #   compress_file "$OUTFILE"
    # else
    #   echo "ERROR occured - no gzip"
    # fi
    # ls -l "$OUTFILE"*
    # echo
  done

}

# ---------- RESTORE

# restore a single backup file; the instance and db name will be detected from file
# param  string  filename of db dump (full path or relative to BACKUP_TARGETDIR)
function restoreByFile(){
  sMyfile=$1
  sMyDb=$2
  echo
  h2 "analyze dump $sMyfile"

  COUCHDB_INSTANCE=$(echo "$sMyfile" | sed "s#${BACKUP_TARGETDIR}##g" | sed "s#\./##g" | sed "s#^/##g" | cut -f 1 -d "/")
  echo "detected COUCHDB_INSTANCE   : [${COUCHDB_INSTANCE}]"
  if [ -z "$sMyDb" ]; then
    sMyDb=$(guessDB "$sMyfile")
    echo "detected db schema from file: [${sMyDb}]"
  else
    echo "db schema from param 2: [${sMyDb}]"
  fi

  echo

  loadInstance "$COUCHDB_INSTANCE"

  echo "connect $couchdbhost on port $couchdbport with user $couchdbuser"
  if ! curl --head -X GET "$COUCHDB_URL" 2>/dev/null | grep "^HTTP.* 200 " >/dev/null; then
    color error
    echo ERROR: couch DB instance is not available
    curl -X GET "$COUCHDB_URL"
    color reset
    exit 1
  fi
  color ok
  echo OK
  color reset


  echo

  # _getDblist | grep "^${sMyDb}$"
  # if [ $? -eq 0 ]; then
  #   echo DB exists ... need to drop it first
  # fi

  h2 "deleting database [$sMyDb] ..."
  color cmd
  _couchapi DELETE "$sMyDb"
  fetchrc
  color reset

  h2 "creating database [$sMyDb] ..."
  color cmd
  _couchapi PUT "$sMyDb"
  fetchrc
  color reset

  h2 import file ...
  color cmd
  zcat "${sMyfile}" | python ${dirPythonPackages}/couchdb/tools/load.py "$COUCHDB_URL/$sMyDb"
  fetchrc
  color reset
  echo

}

# --------------------------------------------------------------------------------
# MAIN
# --------------------------------------------------------------------------------


# ----- check requirements

# --- is a couchd here
j_requireProcess "couchdb"   1

# --- very specific :-/ ... check available config files
ls -1 ~/.iml_backup/couchdb/* >/dev/null 2>&1
rc=$rc+$?


if [ $rc -eq 0 ]; then
  echo OK: couchdb was found on this system ... checking requirements for backup ...

  j_requireBinary  "curl"     1

  ls ${dirPythonPackages}/couchdb/tools/dump.py ${dirPythonPackages}/couchdb/tools/load.py >/dev/null && echo "OK: python couchdb tools were found"
  rc=$rc+$?


  if [ $rc -eq 0 ]; then
    echo

    if [ "$1" = "restore" ]; then
      echo
      shift 1
      restoreByFile $*

    else
      doBackup
    fi

  else
    color error
    echo ERROR: Couchdb is here but I am missing things for the backup :-/
    color reset
  fi

else
  rc=0
  echo "__DB__$SERVICENAME SKIP: couchdb seems not to be here"
fi


echo "__DB__$SERVICENAME INFO: $0 $* [couchdb] final returncode rc=$rc"

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