[master] 2b874fe Test dynamic creation of backends listening at Unix domain sockets.

Geoff Simmons geoff at uplex.de
Mon Mar 12 09:45:10 UTC 2018


commit 2b874fe7e19fcddc9f69a29187191a4b1cb6d557
Author: Geoff Simmons <geoff at uplex.de>
Date:   Mon Feb 19 03:13:35 2018 +0100

    Test dynamic creation of backends listening at Unix domain sockets.

diff --git a/bin/varnishtest/tests/d00032.vtc b/bin/varnishtest/tests/d00032.vtc
new file mode 100644
index 0000000..5e30767
--- /dev/null
+++ b/bin/varnishtest/tests/d00032.vtc
@@ -0,0 +1,70 @@
+varnishtest "Test dynamic backends listening at Unix domain sockets"
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("${s1_sock}");
+	}
+
+	sub vcl_recv {
+		set req.backend_hint = s1.backend();
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 200
+} -run
+
+varnish v1 -errvcl {path must be an absolute path} {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("");
+	}
+}
+
+varnish v1 -errvcl {path must be an absolute path} {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("s1.sock");
+	}
+}
+
+shell { rm -f ${tmpdir}/foo }
+
+varnish v1 -errvcl {Cannot stat path} {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("${tmpdir}/foo");
+	}
+}
+
+varnish v1 -errvcl {is not a socket} {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("${tmpdir}");
+	}
+}
diff --git a/bin/varnishtest/tests/d00033.vtc b/bin/varnishtest/tests/d00033.vtc
new file mode 100644
index 0000000..1d837ab
--- /dev/null
+++ b/bin/varnishtest/tests/d00033.vtc
@@ -0,0 +1,47 @@
+varnishtest "Test dynamic UDS backends hot swap"
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	expect req.url == "/foo"
+	txresp
+} -start
+
+server s2 -listen "${tmpdir}/s2.sock" {
+	rxreq
+	expect req.url == "/bar"
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("${s1_sock}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.refresh(req.http.X-Path);
+			return (synth(200));
+		}
+		set req.backend_hint = s1.backend();
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.status == 200
+
+	txreq -req "SWAP" -hdr "X-Path: ${s2_sock}"
+	rxresp
+	expect resp.status == 200
+
+	txreq -url "/bar"
+	rxresp
+	expect resp.status == 200
+} -run
diff --git a/bin/varnishtest/tests/d00034.vtc b/bin/varnishtest/tests/d00034.vtc
new file mode 100644
index 0000000..314673f
--- /dev/null
+++ b/bin/varnishtest/tests/d00034.vtc
@@ -0,0 +1,58 @@
+varnishtest "Test dynamic UDS backends hot swap while being used"
+
+barrier b1 cond 2
+barrier b2 cond 2
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	expect req.url == "/foo"
+	barrier b1 sync
+	barrier b2 sync
+	txresp
+} -start
+
+server s2 -listen "${tmpdir}/s2.sock" {
+	rxreq
+	expect req.url == "/bar"
+	barrier b2 sync
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("${s1_sock}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.refresh(req.http.X-Path);
+			return (synth(200));
+		}
+		set req.backend_hint = s1.backend();
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.status == 200
+} -start
+
+client c2 {
+	barrier b1 sync
+	txreq -req "SWAP" -hdr "X-Path: ${s2_sock}"
+	rxresp
+	expect resp.status == 200
+
+	txreq -url "/bar"
+	rxresp
+	expect resp.status == 200
+} -run
+
+client c1 -wait
diff --git a/bin/varnishtest/tests/d00035.vtc b/bin/varnishtest/tests/d00035.vtc
new file mode 100644
index 0000000..e47c670
--- /dev/null
+++ b/bin/varnishtest/tests/d00035.vtc
@@ -0,0 +1,59 @@
+varnishtest "Test dynamic UDS backends hot swap during a pipe"
+
+barrier b1 cond 2
+barrier b2 cond 2
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	expect req.url == "/foo"
+	barrier b1 sync
+	barrier b2 sync
+	txresp
+} -start
+
+server s2 -listen "${tmpdir}/s2.sock" {
+	rxreq
+	expect req.url == "/bar"
+	barrier b2 sync
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("${s1_sock}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.refresh(req.http.X-Path);
+			return (synth(200));
+		}
+		set req.backend_hint = s1.backend();
+		return (pipe);
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.status == 200
+} -start
+
+client c2 {
+	barrier b1 sync
+	txreq -req "SWAP" -hdr "X-Path: ${s2_sock}"
+	rxresp
+	expect resp.status == 200
+
+	txreq -url "/bar"
+	rxresp
+	expect resp.status == 200
+} -run
+
+client c1 -wait
diff --git a/bin/varnishtest/tests/d00036.vtc b/bin/varnishtest/tests/d00036.vtc
new file mode 100644
index 0000000..3b739c7
--- /dev/null
+++ b/bin/varnishtest/tests/d00036.vtc
@@ -0,0 +1,62 @@
+varnishtest "Test dynamic UDS backend hot swap after it was picked by a bereq"
+
+barrier b1 cond 2
+
+server s1 -listen "${tmpdir}/s1.sock" {
+} -start
+
+server s2 -listen "${tmpdir}/s2.sock" {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import std;
+	import debug;
+	import vtc;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("${s1_sock}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.refresh(req.http.X-Path);
+			return (synth(200));
+		}
+	}
+
+	sub vcl_backend_fetch {
+		set bereq.backend = s1.backend();
+		# hot swap should happen while we sleep
+		vtc.sleep(2s);
+		if (std.healthy(bereq.backend)) {
+			return(abandon);
+		} else {
+			set bereq.backend = s1.backend();
+		}
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq
+	barrier b1 sync
+	rxresp
+	expect resp.status == 200
+}
+
+client c2 {
+	barrier b1 sync
+	delay 0.1
+	txreq -req "SWAP" -hdr "X-Path: ${s2_sock}"
+	rxresp
+	expect resp.status == 200
+}
+
+client c1 -start
+client c2 -run
+client c1 -wait
diff --git a/bin/varnishtest/tests/d00037.vtc b/bin/varnishtest/tests/d00037.vtc
new file mode 100644
index 0000000..51c6e8f
--- /dev/null
+++ b/bin/varnishtest/tests/d00037.vtc
@@ -0,0 +1,76 @@
+varnishtest "Test a dynamic UDS backend discard during a request"
+
+barrier b1 cond 2
+barrier b2 cond 2
+
+server s1 -listen "${tmpdir}/s1.sock" {
+	rxreq
+	expect req.url == "/foo"
+	barrier b1 sync
+	barrier b2 sync
+	txresp
+} -start
+
+varnish v1 -arg "-p thread_pools=1" -vcl {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("${s1_sock}");
+	}
+
+	sub vcl_recv {
+		set req.backend_hint = s1.backend();
+	}
+} -start
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.status == 200
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+server s2 -listen "${tmpdir}/s2.sock" {
+	rxreq
+	expect req.url == "/bar"
+	txresp
+} -start
+
+barrier b1 sync
+
+varnish v1 -vcl {
+	import debug;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s2 = debug.dyn_uds("${s2_sock}");
+	}
+
+	sub vcl_recv {
+		set req.backend_hint = s2.backend();
+	}
+}
+
+varnish v1 -cli "vcl.discard vcl1"
+barrier b2 sync
+
+client c1 -wait
+delay 2
+
+varnish v1 -expect MAIN.n_backend == 4
+
+varnish v1 -expect n_vcl_avail == 1
+varnish v1 -expect n_vcl_discard == 1
+
+client c1 {
+	txreq -url "/bar"
+	rxresp
+	expect resp.status == 200
+} -run
+
+varnish v1 -cli "vcl.list"
+varnish v1 -expect n_vcl_avail == 1
diff --git a/bin/varnishtest/tests/d00038.vtc b/bin/varnishtest/tests/d00038.vtc
new file mode 100644
index 0000000..1b8b704
--- /dev/null
+++ b/bin/varnishtest/tests/d00038.vtc
@@ -0,0 +1,61 @@
+varnishtest "Test a dynamic UDS backend hot swap after it was hinted to a req"
+
+barrier b1 cond 2
+
+server s1 -listen "${tmpdir}/s1.sock" {
+} -start
+
+server s2 -listen "${tmpdir}/s2.sock" {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl {
+	import std;
+	import debug;
+	import vtc;
+
+	backend dummy { .host = "${bad_ip}"; }
+
+	sub vcl_init {
+		new s1 = debug.dyn_uds("${s1_sock}");
+	}
+
+	sub vcl_recv {
+		if (req.method == "SWAP") {
+			s1.refresh(req.http.X-Path);
+			return (synth(200));
+		}
+		set req.backend_hint = s1.backend();
+		# hot swap should happen while we sleep
+		vtc.sleep(2s);
+		if (std.healthy(req.backend_hint)) {
+			return(synth(800));
+		} else {
+			set req.backend_hint = s1.backend();
+		}
+	}
+} -start
+
+varnish v1 -expect MAIN.n_backend == 2
+
+client c1 {
+	txreq
+	barrier b1 sync
+	rxresp
+	expect resp.status == 200
+}
+
+client c2 {
+	barrier b1 sync
+	delay 0.1
+	txreq -req "SWAP" -hdr "X-Path: ${s2_sock}"
+	rxresp
+	expect resp.status == 200
+}
+
+client c1 -start
+client c2 -run
+client c1 -wait
+
+varnish v1 -cli backend.list
diff --git a/lib/libvmod_debug/vmod.vcc b/lib/libvmod_debug/vmod.vcc
index 5e1ac0c..20f75ec 100644
--- a/lib/libvmod_debug/vmod.vcc
+++ b/lib/libvmod_debug/vmod.vcc
@@ -141,6 +141,19 @@ $Method VOID .refresh(STRING addr, STRING port)
 
 Dynamically refresh & (always!) replace the backend by a new one.
 
+$Object dyn_uds(STRING path)
+
+Dynamically create a single-backend director listening at a Unix
+domain socket, path must not be empty.
+
+$Method BACKEND .backend()
+
+Return the dynamic UDS backend.
+
+$Method VOID .refresh(STRING path)
+
+Dynamically refresh & (always!) replace the backend by a new UDS backend.
+
 $Function VOID vcl_release_delay(DURATION)
 
 Hold a reference to the VCL when it goes cold for the given delay.
diff --git a/lib/libvmod_debug/vmod_debug_dyn.c b/lib/libvmod_debug/vmod_debug_dyn.c
index ebd7330..17b0da2 100644
--- a/lib/libvmod_debug/vmod_debug_dyn.c
+++ b/lib/libvmod_debug/vmod_debug_dyn.c
@@ -32,6 +32,10 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
 
 #include "cache/cache.h"
 #include "cache/cache_director.h"
@@ -47,6 +51,14 @@ struct xyzzy_debug_dyn {
 	struct director		*dir;
 };
 
+struct xyzzy_debug_dyn_uds {
+	unsigned		magic;
+#define VMOD_DEBUG_UDS_MAGIC	0x6c7370e6
+	pthread_mutex_t		mtx;
+	char			*vcl_name;
+	struct director		*dir;
+};
+
 static void
 dyn_dir_init(VRT_CTX, struct xyzzy_debug_dyn *dyn,
     VCL_STRING addr, VCL_STRING port)
@@ -167,3 +179,105 @@ xyzzy_dyn_refresh(VRT_CTX, struct xyzzy_debug_dyn *dyn,
 	CHECK_OBJ_NOTNULL(dyn, VMOD_DEBUG_DYN_MAGIC);
 	dyn_dir_init(ctx, dyn, addr, port);
 }
+
+static int
+dyn_uds_init(VRT_CTX, struct xyzzy_debug_dyn_uds *uds, VCL_STRING path)
+{
+	struct director *dir, *dir2;
+	struct vrt_backend vrt;
+	struct stat st;
+
+	if (path == NULL) {
+		VRT_fail(ctx, "path is NULL");
+		return (-1);
+	}
+	if (*path != '/') {
+		VRT_fail(ctx, "path must be an absolute path: %s", path);
+		return (-1);
+	}
+	errno = 0;
+	if (stat(path, &st) != 0) {
+		VRT_fail(ctx, "Cannot stat path %s: %s", path, strerror(errno));
+		return (-1);
+	}
+	if (! S_ISSOCK(st.st_mode)) {
+		VRT_fail(ctx, "%s is not a socket", path);
+		return (-1);
+	}
+
+	INIT_OBJ(&vrt, VRT_BACKEND_MAGIC);
+	vrt.path = path;
+	vrt.vcl_name = uds->vcl_name;
+	vrt.hosthdr = "localhost";
+	vrt.ipv4_suckaddr = NULL;
+	vrt.ipv6_suckaddr = NULL;
+
+	if ((dir = VRT_new_backend(ctx, &vrt)) == NULL)
+		return (-1);
+
+	AZ(pthread_mutex_lock(&uds->mtx));
+	dir2 = uds->dir;
+	uds->dir = dir;
+	AZ(pthread_mutex_unlock(&uds->mtx));
+
+	if (dir2 != NULL)
+		VRT_delete_backend(ctx, &dir2);
+	return (0);
+}
+
+VCL_VOID v_matchproto_(td_debug_dyn_uds__init)
+xyzzy_dyn_uds__init(VRT_CTX, struct xyzzy_debug_dyn_uds **udsp,
+		    const char *vcl_name, VCL_STRING path)
+{
+	struct xyzzy_debug_dyn_uds *uds;
+
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	AN(udsp);
+	AZ(*udsp);
+	AN(vcl_name);
+
+	ALLOC_OBJ(uds, VMOD_DEBUG_UDS_MAGIC);
+	AN(uds);
+	REPLACE(uds->vcl_name, vcl_name);
+	AZ(pthread_mutex_init(&uds->mtx, NULL));
+
+	if (dyn_uds_init(ctx, uds, path) != 0) {
+		free(uds->vcl_name);
+		AZ(pthread_mutex_destroy(&uds->mtx));
+		FREE_OBJ(uds);
+		return;
+	}
+	*udsp = uds;
+}
+
+VCL_VOID v_matchproto_(td_debug_dyn_uds__fini)
+xyzzy_dyn_uds__fini(struct xyzzy_debug_dyn_uds **udsp)
+{
+	struct xyzzy_debug_dyn_uds *uds;
+
+	if (udsp == NULL || *udsp == NULL)
+		return;
+	CHECK_OBJ(*udsp, VMOD_DEBUG_UDS_MAGIC);
+	uds = *udsp;
+	free(uds->vcl_name);
+	AZ(pthread_mutex_destroy(&uds->mtx));
+	FREE_OBJ(uds);
+	udsp = NULL;
+}
+
+VCL_BACKEND v_matchproto_(td_debug_dyn_uds_backend)
+xyzzy_dyn_uds_backend(VRT_CTX, struct xyzzy_debug_dyn_uds *uds)
+{
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(uds, VMOD_DEBUG_UDS_MAGIC);
+	AN(uds->dir);
+	return (uds->dir);
+}
+
+VCL_VOID v_matchproto_(td_debug_dyn_uds_refresh)
+xyzzy_dyn_uds_refresh(VRT_CTX, struct xyzzy_debug_dyn_uds *uds, VCL_STRING path)
+{
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(uds, VMOD_DEBUG_UDS_MAGIC);
+	(void) dyn_uds_init(ctx, uds, path);
+}


More information about the varnish-commit mailing list