One of my current issues is that I do not notice my NextCloud install requires updating until it is no longer supported and the clients stops working with it. I already have icinga setup as my monitoring solution, so it has been on my to-do list for a while to create a plugin to check the version of my server against the latest release.

This is made somewhat harder by NextCloud providing no official mechanism for finding out the latest version number, for which there are several complaints on their forums. The easiest suggestion I found is to scrape it from their website’s source code on GitHub.

The plugin

The first step is to write a plugin that will check the version of NextCloud against the current release. I wrote this, which can selectively use curl or wget (depending on what is available) to scrape the versions and will distinguish between being out of date by a minor version (warning) or a major version (critical):

#!/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 [[ $1 == "--help" ]]
then
    echo "Usage: $0 [-v [-v [-v]]] target_server_to_check"
    echo "This plugin requires either curl or wget to be installed and"
    echo "internet access to check upstream version."
    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

CURRENT_RELEASE=$(
    $CURL_CMD https://github.com/nextcloud/nextcloud.com/raw/master/strings.php |
    sed -n "s/^\$VERSIONS_SERVER_FULL_STABLE[^']*'\([^'*]\+\)'.*$/\1/p"
)

if [[ $VERBOSITY_LEVEL -ge 2 ]]
then
    echo "Got current release $CURRENT_RELEASE from NextCloud's 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

Using the plugin

I copied it to the standard nagios plugin directory on my Debian systems, /usr/lib/nagios/plugins/check_nextcloud_version.

The next thing I did was to define a command, which can be used to check the service. On my install (with satellites etc., so utilising the zones.d directory) this went in /etc/icinga2/zones.d/global-templates/commands-nextcloud-version.conf:

object CheckCommand "nextcloud-version" {
        import "plugin-check-command"

        command = [ PluginContribDir + "/check_nextcloud_version", "$nextcloud_server$" ]
}

Finally, the service - again, for my setup, this went in /etc/icinga2/zones.d/global-templates/services-nextcloud-version.conf:

apply Service for (nextcloud_server in host.vars.nextcloud_servers) {
  import "generic-service"

  check_command = "nextcloud-version"

  vars.nextcloud_server = nextcloud_server
}

To monitor, add a list of servers to a host (I attached them to the host that they are served from - as that makes clear where the update needs applying):

object Host "myhost.home.domain.tld" {
    // ...
    vars.nextcloud_servers = ["cloud.home.mydomain.tld"]
    // ...
}