Contabo API - automated snapshot creation

Using VPS instances provided by Contabo could be quite easy and not that expensive, but keep in mind to always have some backup at hand and to always keep at least a single snapshot of each instance (a single snapshot is included in the standard plan).

Not forgetting this layer of data preservation could help a lot and speed up recovery processes, so ... why not having this procedures fully automated and run by some Linux system ?

Let's have a look at a possible script ...

#!/bin/bash

# !!! CONFIGURATION SECTION !!!


# list of "INSTANCE ID":::"CLIENT ID":::"CLIENT KEY":::"API ID":::"API PWD":::"SNAPSHOT INTERVAL":::"URL TO CHECK,URL TO CHECK,..."
# --> item separatore is " "
# --> value separator is ":::"
INSTANCES="your.instance:::your.clientid:::your.clientsecret:::your.apiusername:::your.apipassword:::14:::https://your.website1.com,https://your.website2.com"

# !!! PROCEDURES SECTION !!!

export LANG=""

# --> NO CHANGES AFTER THIS COMMENT ... UNLESS YOU KNOW WHAT YOUR ARE DOING 

# status of snapshot creations
CONTABO_OUTPUT=""

# preliminary check to see if 'cut' is present (and also to check if "which" is available) ... 
which cut 2>/dev/null >/dev/null || exit 1

# check required programs and set variables
# list of "VARNAME":"PROGRAM NAME":"ALTERNATIVE PROGRAM NAME"
# --> item separator  is " "
# --> value separator is ":"
PROGRAMS="AWKBIN:awk UUIDBIN:uuid CUTBIN:cut SEDBIN:sed CATBIN:cat JQBIN:jq WGETBIN:wget CURLBIN:curl GREPBIN:grep HEADBIN:head TRBIN:tr SLEEPBIN:sleep DATEBIN:date DATEDIFFBIN:dateutils.ddiff:datediff"
for PROGRAM in $PROGRAMS ;
do
    VAR="$( echo $PROGRAM | cut -d: -f1 2>/dev/null )"
    PRG="$( echo $PROGRAM | cut -d: -f2 2>/dev/null )"
    ALT="$( echo $PROGRAM | cut -d: -f3 2>/dev/null )"
    _PR="$( which $PRG 2>/dev/null )"
    [[ "${_PR}" == "" && "$ALT" != "" ]] && _PR="$( which $ALT 2>/dev/null )"
    [ "${_PR}" = "" ] && exit 2
    declare $VAR="${_PR}"
done

# loop over instances
for INSTANCE in $INSTANCES ;
do

    ID="$(         echo "$INSTANCE" | $AWKBIN -F::: '{ print $1}' )"
    CLIENT_ID="$(  echo "$INSTANCE" | $AWKBIN -F::: '{ print $2}' )"
    CLIENT_KEY="$( echo "$INSTANCE" | $AWKBIN -F::: '{ print $3}' )"
    API_ID="$(     echo "$INSTANCE" | $AWKBIN -F::: '{ print $4}' )"
    API_PWD="$(    echo "$INSTANCE" | $AWKBIN -F::: '{ print $5}' )"
    INTERVAL="$(   echo "$INSTANCE" | $AWKBIN -F::: '{ print $6}' )"
    URLS="$(       echo "$INSTANCE" | $AWKBIN -F::: '{ print $7}' | $TRBIN ',' ' ' )"

    OUTPUT_ID=$( echo $ID | $TRBIN '-' '_' )

    declare CONTABO_OUTPUT_${OUTPUT_ID}=""
    
    OK=1
    for URL in $URLS ;
    do
        $SLEEPBIN 1
        if [ "$( $WGETBIN -qO- --server-response "$URL" 2>&1 | $GREPBIN ' HTTP/' | $HEADBIN -n1 | $GREPBIN ' 200 OK' )" = "" ]; then
            OK=""
        fi
    done
    
    if [ "$OK" = "" ]; then
        declare CONTABO_OUTPUT_${OUTPUT_ID}="ERROR: URLs NOT OK"
        continue
    fi

    ATOKEN=$( $CURLBIN -d "client_id=${CLIENT_ID}"            \
                       -d "client_secret=${CLIENT_KEY}"       \
                       -d 'grant_type=password'            \
                       --data-urlencode "username=${API_ID}"  \
                       --data-urlencode "password=${API_PWD}" \
                       'https://auth.contabo.com/auth/realms/contabo/protocol/openid-connect/token' 2>/dev/null \
              | $JQBIN -r '.access_token' 2>/dev/null )

    if [ "$ATOKEN" = "" ]; then
        declare CONTABO_OUTPUT_${OUTPUT_ID}="ERROR: TOKEN KO"
        continue
    fi

    INSTANCE=$( $CURLBIN  -X GET -H "Authorization: Bearer $ATOKEN" -H "x-request-id: ${ID}" "https://api.contabo.com/v1/compute/instances" 2>/dev/null \
                | $JQBIN 2>/dev/null | $GREPBIN "instanceId" | $CUTBIN -d: -f2 | $SEDBIN -e 's/,//' | $SEDBIN -e 's/ //g' )

    if [ "$INSTANCE" = "" ]; then
        declare CONTABO_OUTPUT_${OUTPUT_ID}="ERROR: INSTANCE KO"
        continue
    fi

    IDTRACE=$( $UUIDBIN )

    SNAPSHOT=$( $CURLBIN -X GET "https://api.contabo.com/v1/compute/instances/${INSTANCE}/snapshots" \
                                       -H 'Content-Type: application/json' -H "Authorization: Bearer ${ATOKEN}" \
                                       -H "x-request-id: $( $UUIDBIN )" -H "x-trace-id: ${IDTRACE}" 2>/dev/null )

    if [ "$SNAPSHOT" = "" ]; then
        declare CONTABO_OUTPUT_${OUTPUT_ID}="ERROR: SNAPSHOTS UNAVAILABLE"
        continue
    fi

    S_DATE=$( echo $SNAPSHOT | $JQBIN -r .data[0].createdDate | $GREPBIN -iv "null" )
    S_ID=$(   echo $SNAPSHOT | $JQBIN -r .data[0].snapshotId  | $GREPBIN -iv "null" )

    CREATE=""

    if [[ "${S_DATE}" != "" && "${S_ID}" != "" ]]; then
        DAYS=$( $DATEDIFFBIN --format="%d" $S_DATE now 2>/dev/null )
        if [ "$DAYS" = "" ]; then
            declare CONTABO_OUTPUT_${OUTPUT_ID}="ERROR: UNABLE TO GET LAST SNAPSHOT DATE"
            continue
        fi
        [ $DAYS -gt $INTERVAL ] && CREATE=1
        [ $DAYS -gt $INTERVAL ] || declare CONTABO_OUTPUT_${OUTPUT_ID}="WARNING: NO SNAPSHOT REQUIRED ($DAYS DAYS OLD)"
    else 
        CREATE="1"
    fi

    if [ "$CREATE" = "1" ]; then

        DELOK=$( $CURLBIN -i -X DELETE "https://api.contabo.com/v1/compute/instances/${INSTANCE}/snapshots/${S_ID}" \
                          -H 'Content-Type: application/json' -H "Authorization: Bearer ${ATOKEN}" -H "x-request-id: $( $UUIDBIN )" -H "x-trace-id: ${IDTRACE}" -d '{}' 2>/dev/null \
                      | $GREPBIN -e '^HTTP/' | $GREPBIN ' 20' )

        if [ "$DELOK" = "" ]; then
            declare CONTABO_OUTPUT_${OUTPUT_ID}="ERROR: UNABLE TO DELETE OLD SNAPSHOT"
            continue
        fi

    fi

    if [ "$CREATE" = "1" ]; then

        NEWDATE=$( $DATEBIN +%Y%m%d )

        OK=$( $CURLBIN -i -X POST "https://api.contabo.com/v1/compute/instances/${INSTANCE}/snapshots" \
                       -H 'Content-Type: application/json' \
                       -H "Authorization: Bearer ${ATOKEN}" \
                       -H "x-request-id: $( $UUIDBIN )" -H "x-trace-id: ${IDTRACE}" \
                       -d "{\"name\":\"$NEWDATE\",\"description\":\"$NEWDATE\"}" \
    	           2>/dev/null | $GREPBIN -e '^HTTP/' | $GREPBIN -E ' (20|30)' )

        if [ "$OK" = "" ]; then
            declare CONTABO_OUTPUT_${OUTPUT_ID}="ERROR: UNABLE TO CREATE NEW SNAPSHOT"
            continue
        fi

        declare CONTABO_OUTPUT_${OUTPUT_ID}="SUCCESS: SNAPSHOT CREATED"

    fi

done

set | $GREPBIN CONTABO_OUTPUT | $GREPBIN -v 'CONTABO_OUTPUT=' | $SEDBIN -e 's/^CONTABO_OUTPUT_/INSTANCE /g' | $SEDBIN -e 's/=/ = /g' | $TRBIN '_' '-'

exit 0