Commit a01b4f26 authored by Nils Goroll's avatar Nils Goroll

initial version of the all_healthy director

parent ca6076a3
#include "config.h" #include "config.h"
#include <string.h> // memset for INIT_OBJ
#include <stdlib.h> // free/calloc
#include <cache/cache.h> #include <cache/cache.h>
#include <vcl.h>
#include "vcc_all_healthy_if.h" #include "vcc_all_healthy_if.h"
VCL_STRING struct vmod_all_healthy_director {
vmod_hello(VRT_CTX) unsigned magic;
#define VMOD_ALL_HEALTHY_DIRECTOR_MAGIC 0x0d8790a3
VCL_BACKEND dir;
VCL_BACKEND backend;
int spcconsider;
int nconsider;
VCL_BACKEND *consider;
};
static VCL_BACKEND vmod_director_resolve(VRT_CTX, VCL_BACKEND);
static VCL_BOOL vmod_director_healthy(VRT_CTX, VCL_BACKEND, VCL_TIME *);
static const struct vdi_methods vmod_director_methods[1] = {
{
.magic = VDI_METHODS_MAGIC,
.type = "all_healthy",
.resolve = vmod_director_resolve,
.healthy = vmod_director_healthy,
}
};
VCL_VOID
vmod_director__init(VRT_CTX,
struct vmod_all_healthy_director **dp, const char *vcl_name)
{ {
struct vmod_all_healthy_director *d;
const int spc = 4;
AN(dp);
AZ(*dp);
ALLOC_OBJ(d, VMOD_ALL_HEALTHY_DIRECTOR_MAGIC);
if (d == NULL) {
VRT_fail(ctx, "obj alloc failed");
return;
}
d->consider = calloc(spc, sizeof(VCL_BACKEND));
if (d->consider == NULL) {
VRT_fail(ctx, "consider list alloc failed");
goto fail_consider;
}
d->spcconsider = spc;
d->dir = VRT_AddDirector(ctx, vmod_director_methods, d, "%s", vcl_name);
if (d->dir == NULL) {
VRT_fail(ctx, "AddDirector failed");
goto fail_dir;
}
*dp = d;
return;
fail_dir:
free(TRUST_ME(d->consider));
fail_consider:
FREE_OBJ(d);
return;
}
VCL_VOID
vmod_director__fini(struct vmod_all_healthy_director **dp) {
struct vmod_all_healthy_director *d = *dp;
*dp = NULL;
if (d == NULL)
return;
CHECK_OBJ(d, VMOD_ALL_HEALTHY_DIRECTOR_MAGIC);
VRT_DelDirector(&d->dir);
free(TRUST_ME(d->consider));
FREE_OBJ(d);
}
#define check_init(ctx, name) do { \
if (((ctx)->method & VCL_MET_INIT) == 0) { \
VRT_fail(ctx, "." #name \
" can only be called from vcl_init {}"); \
return; \
} \
} while(0)
VCL_VOID
vmod_director_consider(VRT_CTX,
struct vmod_all_healthy_director *d, VCL_BACKEND b)
{
int i;
void *n;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
return ("vmod-all_healthy"); CHECK_OBJ(d, VMOD_ALL_HEALTHY_DIRECTOR_MAGIC);
check_init(ctx, consider);
if (b == NULL) {
VRT_fail(ctx, "cannot consider a NULL backend");
return;
}
for (i = 0; i < d->nconsider; i++) {
if (d->consider[i] == b)
return;
}
if (d->spcconsider == d->nconsider) {
i = d->spcconsider << 1;
n = realloc(d->consider, i * sizeof(VCL_BACKEND));
if (n == NULL) {
VRT_fail(ctx, "growing consider list failed");
return;
}
d->spcconsider = i;
d->consider = n;
}
d->consider[d->nconsider++] = b;
if (d->backend == NULL)
d->backend = b;
}
VCL_VOID
vmod_director_set_backend(VRT_CTX,
struct vmod_all_healthy_director *d, VCL_BACKEND b)
{
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ(d, VMOD_ALL_HEALTHY_DIRECTOR_MAGIC);
check_init(ctx, consider);
if (b == NULL) {
VRT_fail(ctx, "cannot set a NULL backend");
return;
}
d->backend = b;
}
VCL_BACKEND
vmod_director_backend(VRT_CTX, struct vmod_all_healthy_director *d)
{
CHECK_OBJ(d, VMOD_ALL_HEALTHY_DIRECTOR_MAGIC);
(void) ctx;
return (d->dir);
}
static VCL_BACKEND vmod_director_resolve(VRT_CTX, VCL_BACKEND b)
{
struct vmod_all_healthy_director *d;
CAST_OBJ_NOTNULL(d, b->priv, VMOD_ALL_HEALTHY_DIRECTOR_MAGIC);
return (d->backend);
}
static VCL_BOOL
vmod_director_healthy(VRT_CTX, VCL_BACKEND b, VCL_TIME *t)
{
struct vmod_all_healthy_director *d;
int i;
VCL_BOOL r = 1;
VCL_TIME bt;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CAST_OBJ_NOTNULL(d, b->priv, VMOD_ALL_HEALTHY_DIRECTOR_MAGIC);
for (i = 0; i < d->nconsider; i++) {
CHECK_OBJ_NOTNULL(d->consider[i], DIRECTOR_MAGIC);
r &= VRT_Healthy(ctx, d->consider[i], &bt);
if (t && bt > *t)
*t = bt;
}
return (r);
} }
...@@ -3,26 +3,94 @@ $Module all_healthy 3 Varnish all_healthy Module ...@@ -3,26 +3,94 @@ $Module all_healthy 3 Varnish all_healthy Module
DESCRIPTION DESCRIPTION
=========== ===========
This VCC file was generated by VCDK, it is used to for both the VMOD The all_healthy vmod provides a varnish director tieing the health
interface and its manual using reStructuredText. state of several other backends / health states: An all_healthy
director is only healthy if all backends to consider are healthy. It
always resolves to exactly one backend, which may or may not be part
of the set of backends considered for health state.
XXX: document vmod-all_healthy An all_healthy director will commonly be layered below other directors
for actual load balancing.
Example Examples and use cases:
::
import all_healthy; * Using more than one health checks for a backend::
sub vcl_deliver { probe probe_a { ... }
set resp.http.Hello = all_healthy.hello(); probe probe_b { ... }
}
XXX: define vmod-all_healthy interface backend be_a {
.host = "1.2.3.4";
.probe = probe_a;
}
$Function STRING hello() # will never actually be used, only provides the second probe
backend be_b {
.host = "1.2.3.4";
.probe = probe_b;
}
Description sub vcl_init {
Hello world for vmod-all_healthy new be = all_healthy.director();
# implies be.set_backend(be_a);
be.consider(be_a);
be.consider(be_b);
some_director.add_backend(be);
}
sub vcl_backend_fetch {
set bereq.backend = be.backend();
}
* Checking health on a different port::
probe oob_probe { ... }
backend be_traffic {
.host = "1.2.3.4";
# no .probe !
}
backend be_oob_probe {
.host = "1.2.3.4";
.port = "4242";
.probe = oob_probe;
}
sub vcl_init {
new be = all_healthy.director();
be.consider(be_oob_probe);
be.set_backend(be_traffic);
some_director.add_backend(be);
}
sub vcl_backend_fetch {
set bereq.backend = be.backend();
}
$Object director()
Instantiate an all_healthy director.
$Method VOID .consider(BACKEND)
Add a backend to consider for determining the health state of the
director.
This method may only be called from vcl_init {}
$Method VOID .set_backend(BACKEND)
Set the backend the director resolves to.
This method may only be called from vcl_init {}
$Method BACKEND .backend()
Return the all_healthy director instance, which will resolve to the
backend set using the .set_backend() method.
SEE ALSO SEE ALSO
========vcl\(7),varnishd\(1) ========vcl\(7),varnishd\(1)
varnishtest "test vmod-all_healthy" varnishtest "test vmod-all_healthy"
server s1 { varnish v1 -vcl {
rxreq import std;
txresp import vtc;
} -start import all_healthy;
backend be_a { .host = "${bad_backend}"; }
backend be_b { .host = "${bad_backend}"; }
varnish v1 -vcl+backend { sub vcl_init {
import all_healthy; new be = all_healthy.director();
be.consider(be_a);
be.consider(be_b);
}
sub vcl_recv {
return (synth(200));
}
sub vcl_deliver { sub vcl_synth {
set resp.http.Hello = all_healthy.hello(); set resp.http.a = std.healthy(be_a);
} set resp.http.b = std.healthy(be_b);
set resp.http.be = std.healthy(be.backend());
set resp.http.name = be.backend();
}
} -start } -start
varnish v1 -cliok "backend.list -j"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.a == true
expect resp.http.b == true
expect resp.http.be == true
expect resp.http.name == be
} -run
varnish v1 -cliok "backend.set_health be_a sick"
varnish v1 -cliok "backend.list -j"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.a == false
expect resp.http.b == true
expect resp.http.be == false
expect resp.http.name == be
} -run
varnish v1 -cliok "backend.set_health be_a healthy"
varnish v1 -cliok "backend.set_health be_b sick"
varnish v1 -cliok "backend.list -j"
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.a == true
expect resp.http.b == false
expect resp.http.be == false
expect resp.http.name == be
} -run
varnish v1 -cliok "backend.set_health be_b healthy"
varnish v1 -cliok "backend.list -j"
client c1 { client c1 {
txreq txreq
rxresp rxresp
expect resp.status == 200 expect resp.status == 200
expect resp.http.Hello == "vmod-all_healthy" expect resp.http.a == true
expect resp.http.b == true
expect resp.http.be == true
expect resp.http.name == be
} -run } -run
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