|
#!/bin/bash |
|
# Create Hetzner virtual server snapshots |
|
# Version 1.2 (build 20190723) |
|
# |
|
# Copyright (C) 2017-2019 Daniel Rudolf <www.daniel-rudolf.de> |
|
# |
|
# This program is free software: you can redistribute it and/or modify |
|
# it under the terms of the GNU General Public License as published by |
|
# the Free Software Foundation, version 3 of the License only. |
|
# |
|
# This program is distributed in the hope that it will be useful, |
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
# GNU General Public License for more details. |
|
# |
|
# See <http://www.gnu.org/licenses/> to receive a full-text-copy of |
|
# the GNU General Public License. |
|
|
|
APP_NAME="$(basename "$0")" |
|
set -e |
|
|
|
VERSION="1.2" |
|
BUILD="20190723" |
|
|
|
if [ ! -e /etc/hetzner_api ]; then |
|
echo "$APP_NAME: Unable to read Hetzner API credentials from '/etc/hetzner_api': No such file or directory" >&2 |
|
exit 1 |
|
elif [ ! -f /etc/hetzner_api ]; then |
|
echo "$APP_NAME: Unable to read Hetzner API credentials from '/etc/hetzner_api': Not a regular file" >&2 |
|
exit 1 |
|
elif [ ! -r /etc/hetzner_api ]; then |
|
echo "$APP_NAME: Unable to read Hetzner API credentials from '/etc/hetzner_api': Permission denied" >&2 |
|
exit 1 |
|
fi |
|
|
|
. /etc/hetzner_api |
|
. /usr/local/lib/hetzner_api/lib.sh |
|
|
|
print_usage() { |
|
echo "Usage:" |
|
echo " $APP_NAME [--latest|--oldest] IP_ADDRESS" |
|
} |
|
|
|
# read parameters |
|
SERVER_IP="" |
|
SNAPSHOT_SELECT="latest" |
|
|
|
while [ $# -gt 0 ]; do |
|
if [ "$1" == "--oldest" ] || [ "$1" == "-o" ]; then |
|
SNAPSHOT_SELECT="oldest" |
|
elif [ "$1" == "--latest" ] || [ "$1" == "-l" ]; then |
|
SNAPSHOT_SELECT="latest" |
|
elif [ "$1" == "--help" ] || [ "$1" == "-h" ]; then |
|
print_usage |
|
echo |
|
echo "Application options:" |
|
echo " -l, --latest replace the server's latest snapshot (default)" |
|
echo " -o, --oldest replace the server's oldest snapshot" |
|
echo |
|
echo "Help options:" |
|
echo " -h, --help display this help and exit" |
|
echo " --version output version information and exit" |
|
exit 0 |
|
elif [ "$1" == "--version" ]; then |
|
echo "hetzner-snapshot.sh $VERSION ($BUILD)" |
|
echo "Copyright (C) 2017-2019 Daniel Rudolf" |
|
echo "License GPLv3: GNU GPL version 3 only <http://gnu.org/licenses/gpl.html>." |
|
echo "This is free software: you are free to change and redistribute it." |
|
echo "There is NO WARRANTY, to the extent permitted by law." |
|
echo |
|
echo "Written by Daniel Rudolf <http://www.daniel-rudolf.de/>" |
|
exit 0 |
|
elif [ -z "$SERVER_IP" ]; then |
|
if ! [[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then |
|
echo "$APP_NAME: Invalid IP address specified" >&2 |
|
exit 1 |
|
fi |
|
|
|
SERVER_IP="$1" |
|
else |
|
print_usage >&2 |
|
exit 1 |
|
fi |
|
|
|
shift |
|
done |
|
|
|
if [ -z "$SERVER_IP" ]; then |
|
print_usage >&2 |
|
exit 1 |
|
fi |
|
|
|
# function: returns the latest or oldest snapshot |
|
get_snapshot() { |
|
local SNAPSHOT="" |
|
local SNAPSHOT_RESPOSNE="" |
|
|
|
hetzner_api_call --output-var SNAPSHOT_RESPONSE --show-error "GET" "/snapshot/$SERVER_IP" >&2 |
|
|
|
SNAPSHOT="$(echo "$SNAPSHOT_RESPONSE" | php -r ' |
|
$json = json_decode(stream_get_contents(STDIN), true); |
|
if (is_array($json)) { |
|
if (empty($json)) { |
|
exit(0); |
|
} |
|
|
|
$json = (isset($_SERVER["argv"]) && ($_SERVER["argv"] === "--latest")) ? array_reverse($json) : $json; |
|
foreach ($json as $item) { |
|
if (isset($item["snapshot"]) && ($item["snapshot"]["type"] === "snapshot")) { |
|
echo $item["snapshot"]["id"]; |
|
exit(0); |
|
} |
|
} |
|
} |
|
|
|
exit(1); |
|
' -- "--$SNAPSHOT_SELECT")" |
|
|
|
if [ $? -ne 0 ]; then |
|
echo "$APP_NAME: Hetzner API call 'GET /snapshot/$SERVER_IP' failed: Invalid JSON response: $SNAPSHOT_RESPONSE" >&2 |
|
exit 1 |
|
fi |
|
|
|
if [ -n "$SNAPSHOT" ]; then |
|
echo "$SNAPSHOT" |
|
return 0 |
|
fi |
|
|
|
return 1 |
|
} |
|
|
|
# get list of existing snapshots |
|
echo "Retrieving list of snapshots..." |
|
SNAPSHOT="$(get_snapshot || true)" |
|
|
|
# delete the latest snapshot |
|
if [ -n "$SNAPSHOT" ]; then |
|
echo -n "Deleting $SNAPSHOT_SELECT snapshot" |
|
hetzner_api_call --show-error "DELETE" "/snapshot/$SERVER_IP/$SNAPSHOT" |
|
|
|
# deleting a snapshot might take some time |
|
for (( i=1 ; i < 100 ; i++ )); do |
|
echo -n "." |
|
if [ $(($i % 3)) -ne 0 ]; then |
|
sleep 3 |
|
elif [ "$(get_snapshot || true)" != "$SNAPSHOT" ]; then |
|
break |
|
fi |
|
done |
|
echo |
|
|
|
if [ $i -eq 100 ]; then |
|
echo "$APP_NAME: Unable to delete latest snapshot" >&2 |
|
exit 1 |
|
fi |
|
fi |
|
|
|
# create a new snapshot |
|
echo "Creating snapshot..." |
|
hetzner_api_call --show-error "POST" "/snapshot/$SERVER_IP" |
Hi,
I'm trying to use your script, but I get the "Invalid HTTP response" error. When I create a custom curl request for the snapshot, the response header is 200 which is the correct status reply.
I've created and moved the files according to your documentation. Is there anyway I can "debug" or have additional information on why I am getting a wrong header response?