NextCloud version tracking
Back in 2021, I added a check for the latest NextCloud version to Icinga based on a forum post from 2019 that suggested scraping the version from a file their GitHub website repository. Today I was looking at something on Icinga and it occurred to me I have not installed any NextCloud updates for a while, a quick check and I determined that the latest version in that file stalled at 23.0.3
(released 21 March 2022, now unsupported and not the last of the 23.x
line - 23.0.12
is the last) and I am now 3 major versions behind - the current release is 26.0.0
on 21 March 2023.
I, obviously, upgraded but need to revisit my check to make it work again. I will reiterate, as I did in my last post, it is very frustrating NextCloud don’t publish any “latest version number” endpoint for self-hosters to check.
This time, I looked at the source for the Updater Server and found that there is a file, config.php
that contains all of the release numbers and the version to which they should upgrade next (generally speaking, the last version of a release line is a special case that points to the next major release). As my monitoring server, which is where the check command runs (as it can remotely get the version from my instance), has PHP for other monitoring tools (including Icinga Web) I can actually use PHP to parse this file properly and output the latest version.
This is the code to fetch, parse and output the latest version:
workdir=$(mktemp -d)
cd $workdir
curl -O -L https://github.com/nextcloud/updater_server/raw/master/config/config.php
if [[ $? -ne 0 ]]
then
echo "Unable to fetch config.php from upstream git repository"
exit 3 # Internal error
fi
# Quoting the HEREDOC marker disables variable interpolation
php <<'EOF'
<?php
$nc_versions = require 'config.php';
$stable_versions = array_keys($nc_versions['stable']);
// Sort versions according to semantic versioning
usort($stable_versions, 'version_compare');
$latest_release = end($stable_versions);
// There may be multiple next versions, keyed by percentage chance of
// the updater server giving that version to a client. Search the list
// for the largest 'latest' version.
$latest_version = null;
foreach ($nc_versions['stable'][$latest_release] as $percent => $info) {
if (is_null($latest_version) or version_compare($latest_version, $info['latest'], '<')) {
$latest_version = $info['latest'];
}
}
// Output the latest version number
echo($latest_version);
?>
EOF
# Tidy up
cd -
rm -rf $workdir
This is the revised nagios plugin script, in full:
#!/bin/bash
if which curl &>/dev/null
then
CURL_CMD="curl -Ls"
elif which wget &>/dev/null
then
CURL_CMD="wget -q -O-"
else
echo "Unable to locate a webscraper (curl or wget) - cannot continue" >&1
exit 3 # Usage/internal error
fi
if ! which php &>/dev/null
then
echo "Unable to locate php (to parse upstream version) - cannot continue" >&1
exit 3 # Usage/internal error
fi
if [[ $1 == "--help" ]]
then
echo "Usage: $0 [-v [-v [-v]]] target_server_to_check"
echo "This plugin requires either curl or wget to be installed, internet"
echo "access to fetch, and php to parse, the upstream version file."
echo "-v: increase the verbosity level one level for each '-v' (up to 3)"
exit 0
fi
VERBOSITY_LEVEL=0
if [[ $1 == "-v" ]]
then
while [[ $1 == "-v" ]]
do
VERBOSITY_LEVEL=$(( VERBOSITY_LEVEL + 1 ))
shift
done
if [[ $VERBOSITY_LEVEL -gt 3 ]]
then
echo "Cannot increase verbosity beyond 3!"
# Could just set it down to 3 but this is a usage error...
exit 3 # Usage/internal error
fi
fi
TARGET_SERVER=$1
# BEGIN fetch and parse upstream updater's configuration file.
# Create temporary directory
workdir=$(mktemp -d)
# Go to that directory
cd $workdir
# Fetch the updater's configuration file (which has all the versions)
$CURL_CMD https://github.com/nextcloud/updater_server/raw/master/config/config.php > config.php
if [[ $? -ne 0 ]]
then
echo "Unable to fetch config.php from upstream git repository"
exit 3 # Internal error
fi
# Parse the file
CURRENT_RELEASE=$(
# Quoting the HEREDOC marker disables variable interpolation and allows
# us to indent it (by including spaces inside the quotes).
# I used tr to delete spaces, rather than HEREDOC's '-' and tab indentation
# due to problems getting editors to retain tabs and not replace them with
# spaces.
php <<' EOF' | tr -d ' '
<?php
$nc_versions = require 'config.php';
$stable_versions = array_keys($nc_versions['stable']);
// Sort versions according to semantic versioning
usort($stable_versions, 'version_compare');
$latest_release = end($stable_versions);
// There may be multiple next versions, keyed by percentage chance of
// the updater server giving that version to a client. Search the list
// for the largest 'latest' version.
$latest_version = null;
foreach ($nc_versions['stable'][$latest_release] as $percent => $info) {
if (is_null($latest_version) or version_compare($latest_version, $info['latest'], '<')) {
$latest_version = $info['latest'];
}
}
// Output the latest version number
echo($latest_version);
?>
EOF
)
# Tidy up
cd - >/dev/null
rm -rf $workdir
# END fetch and parse upstream updater's configuration file.
if [[ $VERBOSITY_LEVEL -ge 2 ]]
then
echo "Got current release $CURRENT_RELEASE from NextCloud's updater github source."
fi
# We could use jq to parse the json properly (jq -r '.versionstring')
# however that introduces a dependency that we do not need if we use
# sed. Just a note for the future, if the sed method breaks down.
INSTALLED_RELEASE=$(
$CURL_CMD https://$TARGET_SERVER/status.php |
sed -n 's/^.*,"versionstring":"\([^"]\+\)",.*$/\1/p'
)
if [[ $VERBOSITY_LEVEL -ge 2 ]]
then
echo "Got release $INSTALLED_RELEASE from server's status.php response."
fi
if [[ $CURRENT_RELEASE == $INSTALLED_RELEASE ]]
then
echo "OK: $INSTALLED_RELEASE = $CURRENT_RELEASE"
exit 0 # Ok
else
# Version mismatch - try to find if it's the major version or minor
if [[ ${CURRENT_RELEASE%%.*} == ${INSTALLED_RELEASE%%.*} ]]
then
# Major versions match
echo "WARNING: Minor version mismatch" \
"($INSTALLED_RELEASE != $CURRENT_RELEASE)"
# Only do multi-line output with verbosity 1 or higher
# see: https://nagios-plugins.org/doc/guidelines.html#PLUGOUTPUT
[[ $VERBOSITY_LEVEL -ge 1 ]] && echo "Major version of both is ${CURRENT_RELEASE%%.*}"
exit 1 # Warning
else
# Major version mismatch
echo "CRITICAL: Major version mismatch" \
"($INSTALLED_RELEASE != $CURRENT_RELEASE)"
exit 2 # Critical
fi
fi