Commit 14b519ec authored by Geoff Simmons's avatar Geoff Simmons

Initial commit

parents
Pipeline #273 skipped
((nil . ((indent-tabs-mode . t)))
(c-mode . ((c-file-style . "BSD"))))
Makefile
Makefile.in
.deps/
.libs/
*.o
*.lo
*.la
*~
*.[1-9]
/aclocal.m4
/autom4te.cache/
/compile
/config.guess
/config.h
/config.h.in
/config.log
/config.status
/config.sub
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/missing
/stamp-h1
/m4/libtool.m4
/m4/lt*.m4
/src/vcc_if.c
/src/vcc_if.h
/src/vmod_*rst
CONTRIBUTING
============
To contribute code or documentation, submit a pull request at the
`source repository website
<https://code.uplex.de/uplex-varnish/libvmod-health>`_.
If you have a problem or discover a bug, you can post an `issue
<https://code.uplex.de/uplex-varnish/libvmod-health/issues>`_ at
the website. You can also write to <varnish-support@uplex.de>.
For developers
--------------
The build specifies C99 conformance, all compiler warnings turned on,
and all warnings considered errors (compiler options
``-std=c99 -Werror -Wall -Wextra``).
The VMOD has been tested with both the gcc and clang compilers, and
MUST always compile and test successfully with both of them.
By default, ``CFLAGS`` is set to ``-g -O2``, so that symbols are
included in the shared library, and optimization is at level
``O2``. To change or disable these options, set ``CFLAGS`` explicitly
before calling ``make`` (it may be set to the empty string).
For development/debugging cycles, the ``configure`` option
``--enable-debugging`` is recommended (off by default). This will turn
off optimizations and function inlining, so that a debugger will step
through the code as expected.
See LICENSE for details.
INSTALLATION
============
The VMOD is built against a Varnish installation, and the autotools
use ``pkg-config(1)`` to locate the necessary header files and other
resources. This sequence will install the VMOD::
> ./autogen.sh # for builds from the git repo
> ./configure
> make
> make check # to run unit tests in src/tests/*.vtc
> sudo make install
If you have installed Varnish in a non-standard directory, call
``autogen.sh`` and ``configure`` with the ``PKG_CONFIG_PATH``
environment variable pointing to the appropriate path. For example,
when varnishd configure was called with ``--prefix=$PREFIX``, use::
> PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig
> export PKG_CONFIG_PATH
By default, the vmod ``configure`` script installs the vmod in
the same directory as Varnish, determined via ``pkg-config(1)``. The
vmod installation directory can be overridden by passing the
``VMOD_DIR`` variable to ``configure``.
Other files such as the man-page are installed in the locations
determined by ``configure``, which inherits its default ``--prefix``
setting from Varnish.
Copyright (c) 2017 UPLEX Nils Goroll Systemoptimierung
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
ACLOCAL_AMFLAGS = -I m4 -I ${LIBVARNISHAPI_DATAROOTDIR}/aclocal
SUBDIRS = src
DISTCHECK_CONFIGURE_FLAGS = \
VMOD_DIR='$${libdir}/varnish/vmods'
EXTRA_DIST = README.rst LICENSE COPYING
doc_DATA = README.rst LICENSE COPYING
dist_man_MANS = vmod_health.3
MAINTAINERCLEANFILES = $(dist_man_MANS)
vmod_health.3: README.rst
README.rst: src/vmod_health.man.rst
cp src/vmod_health.man.rst README.rst
src/vmod_health.man.rst:
make -C src vmod_health.man.rst
%.1 %.2 %.3 %.4 %.5 %.6 %.7 %.8 %.9:
if HAVE_RST2MAN
${RST2MAN} $< $@
else
@echo "========================================"
@echo "You need rst2man installed to make dist"
@echo "========================================"
@false
endif
..
.. NB: This file is machine generated, DO NOT EDIT!
..
.. Edit vmod.vcc and run make instead
..
.. role:: ref(emphasis)
.. _vmod_health(3):
===========
vmod_health
===========
----------------------
backend health details
----------------------
:Manual section: 3
SYNOPSIS
========
import health [from "path"] ;
::
BOOL health.has_probe(BACKEND)
BOOL health.probe_healthy(BACKEND)
STRING health.admin_health(BACKEND)
TIME health.health_changed(BACKEND)
STRING version()
DESCRIPTION
===========
This Varnish Module (VMOD) makes details about the health of a backend
available in VCL. This is the information that is shown as the reult the
``backend.list`` command in the Varnish CLI, which is commonly invoked
via ``varnishadm``.
If a health probe has been defined for a backend, then its health or
sickness may be determined by the results of the probe. But if the
``backend.set_health`` command has been invoked to set the backend
manually to healthy or sick, then that health state will apply in the
decisions that Varnish makes to choose backends. The ``std.healthy()``
method from the ``std`` VMOD returns the result of that decision, but
does not distinguish whether it resulted from a probe or from the use
of ``backend.set_health``.
The functions in this VMOD make that distinction possible -- it can be
determined, for example, that a backend is healthy according to its
probe, but has been set to sick via the CLI. The states of
administrative health are the same as those seen in the output for the
``backend.list`` command: ``probe``, ``healthy`` or ``sick`` if the
backend health is determined by the probe, manually set to healthy, or
manually set to unhealthy, respectively.
Examples::
sub vcl_recv {
# If backend b has a health probe, log the state of the probe.
if (health.has_probe(b)) {
if (health.probe_healthy(b)) {
std.log("Backend b probe result is good");
}
else {
std.log("Backend b probe result is bad");
}
}
# Log whether the health of backend b was manually set.
if (health.admin_health(b) == "sick") {
std.log("Backend b manually set to unhealthy");
}
elsif (health.admin_health(b) == "healthy") {
std.log("Backend b manually set to healthy");
}
else {
# admin_health() returns "probe"
std.log("Backend b health not manually set");
}
std.log("Backend b health last changed at "
+ health.health_changed(b));
}
CONTENTS
========
* BOOL has_probe(BACKEND)
* BOOL probe_healthy(BACKEND)
* STRING admin_health(BACKEND)
* TIME health_changed(BACKEND)
* STRING version()
.. _func_has_probe:
has_probe
---------
::
BOOL has_probe(BACKEND)
Return true if a probe has been defined for the backend. If no probe has
been defined, then the ``probe_healthy`` function below cannot be used,
so this function makes it possible to check for that.
Example::
backend b { .host = "1.2.3.4";
.probe = { .url = "/"; .interval = 1s; timeout = 1s; }
}
sub vcl_recv {
if (health.has_probe(b)) {
call do_if_a_probe_exists;
}
}
.. _func_probe_healthy:
probe_healthy
-------------
::
BOOL probe_healthy(BACKEND)
Return whether a backend is healthy according to the results of its probe,
independent of any manual state set by ``backend.set_health``.
This function MUST be invoked only if a probe is assigned to the backend,
which is not necessarily true of every backend. If it is called for a
backend without a probe, then VCL failure is invoked: response status
503 is returned, and an error message with the tag ``VCL_Error`` is
emitted to the log.
To avoid that, you can call the ``has_probe()`` function before calling
this function.
Example::
sub vcl_recv {
if (health.has_probe(b)) {
if (health.probe_healthy(b)) {
return(synth(200, "Health checks for b are good"));
}
else {
return(synth(503, "Health checks for b are failing"));
}
}
}
.. _func_admin_health:
admin_health
------------
::
STRING admin_health(BACKEND)
Return a string indicating the administrative state of the backend's health.
These are the same strings displayed in the output of the ``backend.list``
command:
* ``"healthy"``: backend set to healthy via ``backend.set_health``,
regardless of the results of any probe
* ``"sick"``: backend set to sick regardless of any probe
* ``"probe"``: backend health is not manually set, and hence is either
determined by its probe, or there is no probe
* ``"deleted"``: the backend has been deleted and will be removed
The ``"deleted"`` state is rarely seen in the output of
``backend.list``, but it is possible, for example, if the backend has
been marked for deletion by the action of a VMOD but has not yet been
fully de-allocated.
If no probe has been defined for a backend, and its health has not
been manually set, then the result is ``"probe"``. This may seem
contradictory, but it simply means that neither the ``"healthy"`` nor
the ``"sick"`` states apply, which would indicate that health was
manually set. The backend is always regarded as healthy in this
situation -- that is, requests are always sent to it, since no health
probe and no manual health state indicate that they should not be.
Example::
sub vcl_recv {
if (health.admin_health(b) == "sick") {
return(synth(503, "Backend b manually set to sick"));
}
}
.. _func_health_changed:
health_changed
--------------
::
TIME health_changed(BACKEND)
Return the time at which the health state of the backend last changed.
Example::
sub vcl_recv {
if (health.has_probe(b)) {
if (health.probe_healthy(b)) {
std.log("Backend b healthy since: "
+ health.health_changed(b));
}
}
}
.. _func_version:
version
-------
::
STRING version()
Return the version string for this VMOD.
Example::
std.log("Using VMOD health version: " + health.version());
REQUIREMENTS
============
This VMOD has been tested with Varnish version 5.1.2.
INSTALLATION
============
See `INSTALL.rst <INSTALL.rst>`_ in the source repository.
SEE ALSO
========
* varnishd(1)
* vcl(7)
* varnish-cli(7)
* varnishadm(1)
* vmod_std(3)
* VMOD source repository: https://code.uplex.de/uplex-varnish/libvmod-health
COPYRIGHT
=========
::
This document is licensed under the same conditions
as the libvmod-health project. See LICENSE for details.
Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
#!/bin/sh
warn() {
echo "WARNING: $@" 1>&2
}
case `uname -s` in
Darwin)
LIBTOOLIZE=glibtoolize
;;
FreeBSD)
LIBTOOLIZE=libtoolize
;;
Linux)
LIBTOOLIZE=libtoolize
;;
SunOS)
LIBTOOLIZE=libtoolize
;;
*)
warn "unrecognized platform:" `uname -s`
LIBTOOLIZE=libtoolize
esac
automake_version=`automake --version | tr ' ' '\n' | egrep '^[0-9]\.[0-9a-z.-]+'`
if [ -z "$automake_version" ] ; then
warn "unable to determine automake version"
else
case $automake_version in
0.*|1.[0-8]|1.[0-8][.-]*)
warn "automake ($automake_version) detected; 1.9 or newer recommended"
;;
*)
;;
esac
fi
# check for varnishapi.m4 in custom paths
dataroot=$(pkg-config --variable=datarootdir varnishapi 2>/dev/null)
if [ -z "$dataroot" ] ; then
cat >&2 <<'EOF'
Package varnishapi was not found in the pkg-config search path.
Perhaps you should add the directory containing `varnishapi.pc'
to the PKG_CONFIG_PATH environment variable
EOF
exit 1
fi
set -ex
aclocal -I m4 -I ${dataroot}/aclocal
$LIBTOOLIZE --copy --force
autoheader
automake --add-missing --copy --foreign
autoconf
AC_PREREQ(2.59)
AC_COPYRIGHT([Copyright (c) 2017 UPLEX - Nils Goroll Systemoptimierung])
AC_INIT([VMOD health], [trunk], [varnish-support@uplex.de], [libvmod-health],
[https://code.uplex.de/uplex-varnish/libvmod-health])
AC_CONFIG_MACRO_DIR([m4])
m4_ifndef([VARNISH_VMOD_INCLUDES], AC_MSG_ERROR([Need varnish.m4]))
AC_CONFIG_SRCDIR(src/vmod_health.vcc)
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_SYSTEM
AC_LANG(C)
AM_INIT_AUTOMAKE([foreign])
AC_PROG_CC
AC_PROG_CC_C99
if test "x$ac_cv_prog_cc_c99" = xno; then
AC_MSG_ERROR([Could not find a C99 compatible compiler])
fi
AC_PROG_CPP
AX_PTHREAD(,[AC_MSG_ERROR([Could not configure pthreads support])])
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"
# Checks for libtool
LT_INIT([disable-static])
AC_PROG_INSTALL
AC_PROG_LIBTOOL
AC_PROG_MAKE_SET
# Checks for header files.
AC_HEADER_STDC
# Checks for C sources
# backwards compat with older pkg-config
# - pull in AC_DEFUN from pkg.m4
m4_ifndef([PKG_CHECK_VAR], [
# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
# -------------------------------------------
# Retrieves the value of the pkg-config variable for the given module.
AC_DEFUN([PKG_CHECK_VAR],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
AS_VAR_COPY([$1], [pkg_cv_][$1])
AS_VAR_IF([$1], [""], [$5], [$4])dnl
])# PKG_CHECK_VAR
])
PKG_CHECK_MODULES([libvarnishapi], [varnishapi])
PKG_CHECK_VAR([LIBVARNISHAPI_PREFIX], [varnishapi], [prefix])
PKG_CHECK_VAR([LIBVARNISHAPI_DATAROOTDIR], [varnishapi], [datarootdir])
PKG_CHECK_VAR([LIBVARNISHAPI_BINDIR], [varnishapi], [bindir])
PKG_CHECK_VAR([LIBVARNISHAPI_SBINDIR], [varnishapi], [sbindir])
AC_SUBST([LIBVARNISHAPI_DATAROOTDIR])
# Varnish include files tree
VARNISH_VMOD_INCLUDES
VARNISH_VMOD_DIR
VARNISH_VMODTOOL
# inherit the prefix from Varnish.
# acessing ac_ variable because AC_PREFIX_DEFAULT acts too early
ac_default_prefix=$LIBVARNISHAPI_PREFIX
AC_PATH_PROG([VARNISHTEST], [varnishtest], [],
[$LIBVARNISHAPI_BINDIR:$LIBVARNISHAPI_SBINDIR:$PATH])
AC_PATH_PROG([VARNISHD], [varnishd], [],
[$LIBVARNISHAPI_SBINDIR:$LIBVARNISHAPI_BINDIR:$PATH])
# Check for rst utilities
AC_CHECK_PROGS(RST2MAN, [rst2man rst2man.py], "no")
if test "x$RST2MAN" = "xno"; then
AC_MSG_WARN([rst2man not found - not building man pages])
fi
AM_CONDITIONAL(HAVE_RST2MAN, [test "x$RST2MAN" != "xno"])
# --enable-stack-protector
AC_ARG_ENABLE(stack-protector,
AS_HELP_STRING([--enable-stack-protector],[enable stack protector (default is YES)]),
[],
[enable_stack_protector=yes])
if test "x$enable_stack_protector" != "xno"; then
AX_CHECK_COMPILE_FLAG([-fstack-protector],
AX_CHECK_LINK_FLAG([-fstack-protector],
[CFLAGS="${CFLAGS} -fstack-protector"], [], []),
[], [])
fi
# --enable-debugging
AC_ARG_ENABLE(debugging,
AS_HELP_STRING([--enable-debugging],[enable debugging (default is NO)]),
[],
[enable_debugging=no])
# AC_PROG_CC sets CFLAGS to '-g -O2' unless it is already set, so
# there's no need to add -g. Disable or change by explicitly setting
# CFLAGS. If this option is enabled, then -Og or -O0 becomes the last
# optimization option, and hence takes precedence.
if test "x$enable_debugging" != "xno"; then
CFLAGS="${CFLAGS} -fno-inline"
AX_CHECK_COMPILE_FLAG([-Og],
[CFLAGS="${CFLAGS} -Og"],
[CFLAGS="${CFLAGS} -O0"],
[])
fi
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT
AM_CPPFLAGS = @VMOD_INCLUDES@ -Wall -Werror -Wextra -std=c99
vmoddir = @VMOD_DIR@
vmod_LTLIBRARIES = libvmod_health.la
libvmod_health_la_LDFLAGS = -module -export-dynamic -avoid-version -shared
libvmod_health_la_SOURCES = \
vmod_health.c
nodist_libvmod_health_la_SOURCES = \
vcc_if.c \
vcc_if.h
vmod_health.lo: vcc_if.c vcc_if.h
vcc_if.c: vcc_if.h
vcc_if.h vmod_health.man.rst: @VMODTOOL@ $(top_srcdir)/src/vmod_health.vcc
@VMODTOOL@ $(top_srcdir)/src/vmod_health.vcc
VMOD_TESTS = $(builddir)/tests/*.vtc
.PHONY: $(VMOD_TESTS)
$(VMOD_TESTS): libvmod_health.la
PATH=@LIBVARNISHAPI_SBINDIR@:$$PATH \
@VARNISHTEST@ -Dvmod_topbuild=$(abs_top_builddir) $@
check: $(VMOD_TESTS)
EXTRA_DIST = vmod_health.vcc
CLEANFILES = \
$(builddir)/vcc_if.c \
$(builddir)/vcc_if.h \
$(builddir)/vmod_health.rst \
$(builddir)/vmod_health.man.rst
# looks like -*- vcl -*-
varnishtest "vcl.use and .discard, and version string"
server s1 {} -start
varnish v1 -vcl+backend {
import health from "${vmod_topbuild}/src/.libs/libvmod_health.so";
} -start
varnish v1 -vcl+backend {}
varnish v1 -cli "vcl.list"
varnish v1 -cli "vcl.use vcl1"
varnish v1 -cli "vcl.use vcl2"
varnish v1 -cli "vcl.use vcl1"
varnish v1 -cli "vcl.show vcl1"
varnish v1 -cli "vcl.use vcl2"
varnish v1 -cli "vcl.discard vcl1"
varnish v1 -cli "vcl.list"
varnish v1 -vcl {
import health from "${vmod_topbuild}/src/.libs/libvmod_health.so";
backend b { .host = "${bad_ip}"; }
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.x-version = health.version();
if (!resp.http.x-version) {
set resp.status = 500;
}
return(deliver);
}
}
client c1 {
txreq -url "/"
rxresp
expect resp.status == 200
expect resp.http.x-version ~ "^.+$"
} -run
# looks like -*- vcl -*-
varnishtest "tests with no probe"
varnish v1 -vcl {
import health from "${vmod_topbuild}/src/.libs/libvmod_health.so";
backend b { .host = "${bad_ip}"; }
sub vcl_recv {
if (req.url == "/probe") {
set req.http.probe-health = health.probe_healthy(b);
}
else {
return(synth(200));
}
}
sub vcl_synth {
set resp.http.probe-healthy = req.http.probe-healthy;
set resp.http.has-probe = health.has_probe(b);
set resp.http.admin-health = health.admin_health(b);
set resp.http.health-changed = health.health_changed(b);
}
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.has-probe == "false"
expect resp.http.admin-health == "probe"
expect resp.http.health-changed ~ ".+"
txreq -url "/probe"
rxresp
expect resp.status == 503
expect resp.http.probe-healthy == ""
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "vmod health error: Backend b has no health probe"
expect * = End
} -run
varnish v1 -cliok "backend.set_health b sick"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.admin-health == "sick"
} -run
varnish v1 -cliok "backend.set_health b healthy"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.admin-health == "healthy"
} -run
varnish v1 -cliok "backend.set_health b probe"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.admin-health == "probe"
} -run
# looks like -*- vcl -*-
varnishtest "tests with probes"
server s1 -repeat 1000 {
rxreq
txresp
} -start
varnish v1 -vcl {
import health from "${vmod_topbuild}/src/.libs/libvmod_health.so";
backend b {
.host = "${s1_addr}";
.port = "${s1_port}";
.probe = {
.timeout = 1s;
.interval = 1s;
}
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.probe-healthy = health.probe_healthy(b);
set resp.http.has-probe = health.has_probe(b);
set resp.http.admin-health = health.admin_health(b);
set resp.http.health-changed = health.health_changed(b);
}
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.probe-healthy == "true"
expect resp.http.has-probe == "true"
expect resp.http.admin-health == "probe"
expect resp.http.health-changed ~ ".+"
} -run
varnish v1 -cliok "backend.set_health b sick"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.probe-healthy == "true"
expect resp.http.has-probe == "true"
expect resp.http.admin-health == "sick"
expect resp.http.health-changed ~ ".+"
} -run
varnish v1 -cliok "backend.set_health b healthy"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.probe-healthy == "true"
expect resp.http.has-probe == "true"
expect resp.http.admin-health == "healthy"
expect resp.http.health-changed ~ ".+"
} -run
varnish v1 -cliok "backend.set_health b probe"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.probe-healthy == "true"
expect resp.http.has-probe == "true"
expect resp.http.admin-health == "probe"
expect resp.http.health-changed ~ ".+"
} -run
server s2 -repeat 1000 {
rxreq
txresp -status 500
} -start
varnish v1 -vcl {
import health from "${vmod_topbuild}/src/.libs/libvmod_health.so";
backend b {
.host = "${s2_addr}";
.port = "${s2_port}";
.probe = {
.timeout = 1s;
.interval = 1s;
}
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.probe-healthy = health.probe_healthy(b);
set resp.http.has-probe = health.has_probe(b);
set resp.http.admin-health = health.admin_health(b);
set resp.http.health-changed = health.health_changed(b);
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.probe-healthy == "false"
expect resp.http.has-probe == "true"
expect resp.http.admin-health == "probe"
expect resp.http.health-changed ~ ".+"
} -run
varnish v1 -cliok "backend.set_health b sick"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.probe-healthy == "false"
expect resp.http.has-probe == "true"
expect resp.http.admin-health == "sick"
expect resp.http.health-changed ~ ".+"
} -run
varnish v1 -cliok "backend.set_health b healthy"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.probe-healthy == "false"
expect resp.http.has-probe == "true"
expect resp.http.admin-health == "healthy"
expect resp.http.health-changed ~ ".+"
} -run
varnish v1 -cliok "backend.set_health b probe"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.probe-healthy == "false"
expect resp.http.has-probe == "true"
expect resp.http.admin-health == "probe"
expect resp.http.health-changed ~ ".+"
} -run
varnish v1 -cliok "backend.list -p"
/*-
* Copyright 2017 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "config.h"
#include "vrt.h"
#include "cache/cache.h"
#include "cache/cache_director.h"
#include "cache/cache_backend.h"
#include "vcc_if.h"
#define FAIL(ctx, fmt, ...) \
VRT_fail((ctx), "vmod health error: " fmt, __VA_ARGS__)
#define NOTABACKEND(ctx, d) \
FAIL((ctx), "%s is a director, not a backend", (d)->vcl_name)
VCL_BOOL
vmod_has_probe(VRT_CTX, VCL_BACKEND d)
{
struct backend *b;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
if (d->priv == NULL) {
NOTABACKEND(ctx, d);
return 0;
}
CAST_OBJ(b, d->priv, BACKEND_MAGIC);
return (b->probe != NULL);
}
VCL_BOOL
vmod_probe_healthy(VRT_CTX, VCL_BACKEND d)
{
struct backend *b;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
if (d->priv == NULL) {
NOTABACKEND(ctx, d);
return 0;
}
CAST_OBJ(b, d->priv, BACKEND_MAGIC);
if (b->probe == NULL) {
FAIL(ctx, "Backend %s has no health probe", d->vcl_name);
return 0;
}
return(b->healthy);
}
VCL_STRING
vmod_admin_health(VRT_CTX, VCL_BACKEND d)
{
struct backend *b;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
if (d->priv == NULL) {
NOTABACKEND(ctx, d);
return NULL;
}
CAST_OBJ(b, d->priv, BACKEND_MAGIC);
AN(b->admin_health);
return(b->admin_health);
}
VCL_TIME
vmod_health_changed(VRT_CTX, VCL_BACKEND d)
{
struct backend *b;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
if (d->priv == NULL) {
NOTABACKEND(ctx, d);
return 0.;
}
CAST_OBJ(b, d->priv, BACKEND_MAGIC);
return(b->health_changed);
}
VCL_STRING
vmod_version(VRT_CTX)
{
(void) ctx;
return VERSION;
}
#-
# This document is licensed under the same conditions
# as the libvmod-health project. See LICENSE for details.
#
# Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
#
$Module health 3 backend health details
::
BOOL health.has_probe(BACKEND)
BOOL health.probe_healthy(BACKEND)
STRING health.admin_health(BACKEND)
TIME health.health_changed(BACKEND)
STRING version()
DESCRIPTION
===========
This Varnish Module (VMOD) makes details about the health of a backend
available in VCL. This is the information that is shown as the reult the
``backend.list`` command in the Varnish CLI, which is commonly invoked
via ``varnishadm``.
If a health probe has been defined for a backend, then its health or
sickness may be determined by the results of the probe. But if the
``backend.set_health`` command has been invoked to set the backend
manually to healthy or sick, then that health state will apply in the
decisions that Varnish makes to choose backends. The ``std.healthy()``
method from the ``std`` VMOD returns the result of that decision, but
does not distinguish whether it resulted from a probe or from the use
of ``backend.set_health``.
The functions in this VMOD make that distinction possible -- it can be
determined, for example, that a backend is healthy according to its
probe, but has been set to sick via the CLI. The states of
administrative health are the same as those seen in the output for the
``backend.list`` command: ``probe``, ``healthy`` or ``sick`` if the
backend health is determined by the probe, manually set to healthy, or
manually set to unhealthy, respectively.
Examples::
sub vcl_recv {
# If backend b has a health probe, log the state of the probe.
if (health.has_probe(b)) {
if (health.probe_healthy(b)) {
std.log("Backend b probe result is good");
}
else {
std.log("Backend b probe result is bad");
}
}
# Log whether the health of backend b was manually set.
if (health.admin_health(b) == "sick") {
std.log("Backend b manually set to unhealthy");
}
elsif (health.admin_health(b) == "healthy") {
std.log("Backend b manually set to healthy");
}
else {
# admin_health() returns "probe"
std.log("Backend b health not manually set");
}
std.log("Backend b health last changed at "
+ health.health_changed(b));
}
$Function BOOL has_probe(BACKEND)
Return true if a probe has been defined for the backend. If no probe has
been defined, then the ``probe_healthy`` function below cannot be used,
so this function makes it possible to check for that.
Example::
backend b { .host = "1.2.3.4";
.probe = { .url = "/"; .interval = 1s; timeout = 1s; }
}
sub vcl_recv {
if (health.has_probe(b)) {
call do_if_a_probe_exists;
}
}
$Function BOOL probe_healthy(BACKEND)
Return whether a backend is healthy according to the results of its probe,
independent of any manual state set by ``backend.set_health``.
This function MUST be invoked only if a probe is assigned to the backend,
which is not necessarily true of every backend. If it is called for a
backend without a probe, then VCL failure is invoked: response status
503 is returned, and an error message with the tag ``VCL_Error`` is
emitted to the log.
To avoid that, you can call the ``has_probe()`` function before calling
this function.
Example::
sub vcl_recv {
if (health.has_probe(b)) {
if (health.probe_healthy(b)) {
return(synth(200, "Health checks for b are good"));
}
else {
return(synth(503, "Health checks for b are failing"));
}
}
}
$Function STRING admin_health(BACKEND)
Return a string indicating the administrative state of the backend's health.
These are the same strings displayed in the output of the ``backend.list``
command:
* ``"healthy"``: backend set to healthy via ``backend.set_health``,
regardless of the results of any probe
* ``"sick"``: backend set to sick regardless of any probe
* ``"probe"``: backend health is not manually set, and hence is either
determined by its probe, or there is no probe
* ``"deleted"``: the backend has been deleted and will be removed
The ``"deleted"`` state is rarely seen in the output of
``backend.list``, but it is possible, for example, if the backend has
been marked for deletion by the action of a VMOD but has not yet been
fully de-allocated.
If no probe has been defined for a backend, and its health has not
been manually set, then the result is ``"probe"``. This may seem
contradictory, but it simply means that neither the ``"healthy"`` nor
the ``"sick"`` states apply, which would indicate that health was
manually set. The backend is always regarded as healthy in this
situation -- that is, requests are always sent to it, since no health
probe and no manual health state indicate that they should not be.
Example::
sub vcl_recv {
if (health.admin_health(b) == "sick") {
return(synth(503, "Backend b manually set to sick"));
}
}
$Function TIME health_changed(BACKEND)
Return the time at which the health state of the backend last changed.
Example::
sub vcl_recv {
if (health.has_probe(b)) {
if (health.probe_healthy(b)) {
std.log("Backend b healthy since: "
+ health.health_changed(b));
}
}
}
$Function STRING version()
Return the version string for this VMOD.
Example::
std.log("Using VMOD health version: " + health.version());
REQUIREMENTS
============
This VMOD has been tested with Varnish version 5.1.2.
INSTALLATION
============
See `INSTALL.rst <INSTALL.rst>`_ in the source repository.
SEE ALSO
========
* varnishd(1)
* vcl(7)
* varnish-cli(7)
* varnishadm(1)
* vmod_std(3)
* VMOD source repository: https://code.uplex.de/uplex-varnish/libvmod-health
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment