[master] 34350d5 Introduce "VCL labels".
Poul-Henning Kamp
phk at FreeBSD.org
Sun May 22 12:07:04 CEST 2016
commit 34350d5e183ef4e04285729d1f63b784d1bc6454
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date: Sun May 22 09:37:22 2016 +0000
Introduce "VCL labels".
A VCL label is a symbolic name pointing to a "real" VCL program.
VCL labels can be the active VCL and can be repointed to a different
VCL at any time.
Labels are always warm, and can be discarded with vcl.discard if not
in use.
Labeled VCLs are _also_ always warm.
One possible use could be for site develpment to label a "production",
and a "emergency" VCL so operations personel only have to know
these two labels, not worrying about versioning of the VCLs.
Of course I have other interesting evil plans for this too...
diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c
index f36f2b4..dfc7235 100644
--- a/bin/varnishd/cache/cache_vcl.c
+++ b/bin/varnishd/cache/cache_vcl.c
@@ -51,6 +51,7 @@ static const char * const VCL_TEMP_COLD = "cold";
static const char * const VCL_TEMP_WARM = "warm";
static const char * const VCL_TEMP_BUSY = "busy";
static const char * const VCL_TEMP_COOLING = "cooling";
+static const char * const VCL_TEMP_LABEL = "label";
struct vcl {
unsigned magic;
@@ -65,6 +66,7 @@ struct vcl {
const char *temp;
VTAILQ_HEAD(,backend) backend_list;
VTAILQ_HEAD(,vclref) ref_list;
+ struct vcl *label;
};
struct vclref {
@@ -161,7 +163,10 @@ VCL_Get(struct vcl **vcc)
assert(vcl_active->temp == VCL_TEMP_WARM);
Lck_Lock(&vcl_mtx);
AN(vcl_active);
- *vcc = vcl_active;
+ if (vcl_active->label == NULL)
+ *vcc = vcl_active;
+ else
+ *vcc = vcl_active->label;
AN(*vcc);
AZ((*vcc)->discard);
(*vcc)->busy++;
@@ -609,11 +614,7 @@ VCL_Load(struct cli *cli, const char *name, const char *fn, const char *state)
ASSERT_CLI();
vcl = vcl_find(name);
- if (vcl != NULL) {
- VCLI_SetResult(cli, CLIS_PARAM);
- VCLI_Out(cli, "Config '%s' already loaded", name);
- return;
- }
+ AZ(vcl);
vsb = VSB_new_auto();
AN(vsb);
@@ -736,8 +737,14 @@ ccf_config_list(struct cli *cli, const char * const *av, void *priv)
flg = "discarded";
} else
flg = "available";
- VCLI_Out(cli, "%-10s %4s/%-8s %6u %s\n",
+ VCLI_Out(cli, "%-10s %5s/%-8s %6u %s",
flg, vcl->state, vcl->temp, vcl->busy, vcl->loaded_name);
+ if (vcl->label != NULL) {
+ VCLI_Out(cli, " %s %s",
+ strcmp(vcl->state, VCL_TEMP_LABEL) ?
+ "<-" : "->", vcl->label->loaded_name);
+ }
+ VCLI_Out(cli, "\n");
}
}
@@ -797,13 +804,49 @@ ccf_config_discard(struct cli *cli, const char * const *av, void *priv)
VSC_C_main->n_vcl_discard++;
VSC_C_main->n_vcl_avail--;
vcl->discard = 1;
+ if (vcl->label != NULL) {
+ AZ(strcmp(vcl->state, VCL_TEMP_LABEL));
+ vcl->label->label = NULL;
+ vcl->label= NULL;
+ }
Lck_Unlock(&vcl_mtx);
- if (vcl->temp == VCL_TEMP_COLD)
+ if (!strcmp(vcl->state, VCL_TEMP_LABEL)) {
+ VTAILQ_REMOVE(&vcl_head, vcl, list);
+ free(vcl->loaded_name);
+ } else if (vcl->temp == VCL_TEMP_COLD)
VCL_Nuke(vcl);
}
static void __match_proto__(cli_func_t)
+ccf_config_label(struct cli *cli, const char * const *av, void *priv)
+{
+ struct vcl *lbl;
+ struct vcl *vcl;
+
+ ASSERT_CLI();
+ (void)cli;
+ (void)priv;
+ vcl = vcl_find(av[3]);
+ AN(vcl);
+ lbl = vcl_find(av[2]);
+ if (lbl == NULL) {
+ ALLOC_OBJ(lbl, VCL_MAGIC);
+ AN(lbl);
+ strcpy(lbl->state, VCL_TEMP_LABEL);
+ lbl->temp = VCL_TEMP_WARM;
+ lbl->loaded_name = strdup(av[2]);
+ AN(lbl->loaded_name);
+ VTAILQ_INSERT_TAIL(&vcl_head, lbl, list);
+ }
+ if (lbl->label != NULL)
+ lbl->label->label = NULL;
+ lbl->label = vcl;
+ vcl->label = lbl;
+ return;
+}
+
+static void __match_proto__(cli_func_t)
ccf_config_use(struct cli *cli, const char * const *av, void *priv)
{
struct vcl *vcl;
@@ -952,6 +995,7 @@ static struct cli_proto vcl_cmds[] = {
{ CLICMD_VCL_DISCARD, "", ccf_config_discard },
{ CLICMD_VCL_USE, "", ccf_config_use },
{ CLICMD_VCL_SHOW, "", ccf_config_show },
+ { CLICMD_VCL_LABEL, "", ccf_config_label },
{ NULL }
};
diff --git a/bin/varnishd/mgt/mgt_vcl.c b/bin/varnishd/mgt/mgt_vcl.c
index 8817440..1b3950c 100644
--- a/bin/varnishd/mgt/mgt_vcl.c
+++ b/bin/varnishd/mgt/mgt_vcl.c
@@ -47,6 +47,7 @@
static const char * const VCL_STATE_COLD = "cold";
static const char * const VCL_STATE_WARM = "warm";
static const char * const VCL_STATE_AUTO = "auto";
+static const char * const VCL_STATE_LABEL = "label";
struct vclprog {
VTAILQ_ENTRY(vclprog) list;
@@ -55,6 +56,7 @@ struct vclprog {
unsigned warm;
char state[8];
double go_cold;
+ struct vclprog *label;
};
static VTAILQ_HEAD(, vclprog) vclhead = VTAILQ_HEAD_INITIALIZER(vclhead);
@@ -91,7 +93,8 @@ mgt_vcl_del(struct vclprog *vp)
char dn[256];
VTAILQ_REMOVE(&vclhead, vp, list);
- XXXAZ(unlink(vp->fname));
+ if (strcmp(vp->state, VCL_STATE_LABEL))
+ XXXAZ(unlink(vp->fname));
bprintf(dn, "vcl_%s", vp->name);
VJ_master(JAIL_MASTER_FILE);
(void)rmdir(dn); // compiler droppings, eg gcov
@@ -127,6 +130,10 @@ mgt_vcl_setstate(struct cli *cli, struct vclprog *vp, const char *vs)
char *p;
int i;
+ if (!strcmp(vp->state, VCL_STATE_LABEL)) {
+ AN(vp->warm);
+ return (0);
+ }
if (vs == VCL_STATE_AUTO) {
assert(vp != active_vcl);
now = VTIM_mono();
@@ -202,16 +209,13 @@ mgt_new_vcl(struct cli *cli, const char *vclname, const char *vclsrc,
if (child_pid < 0)
return;
- if (!mgt_cli_askchild(&status, &p, "vcl.load %s %s %d%s\n",
+ if (mgt_cli_askchild(&status, &p, "vcl.load %s %s %d%s\n",
vp->name, vp->fname, vp->warm, vp->state)) {
- free(p);
- return;
+ mgt_vcl_del(vp);
+ VCLI_Out(cli, "%s", p);
+ VCLI_SetResult(cli, CLIS_PARAM);
}
-
- mgt_vcl_del(vp);
- VCLI_Out(cli, "%s", p);
free(p);
- VCLI_SetResult(cli, CLIS_PARAM);
}
/*--------------------------------------------------------------------*/
@@ -251,14 +255,27 @@ mgt_push_vcls_and_start(struct cli *cli, unsigned *status, char **p)
AZ(mgt_vcl_setstate(cli, active_vcl, VCL_STATE_WARM));
VTAILQ_FOREACH(vp, &vclhead, list) {
+ if (!strcmp(vp->state, VCL_STATE_LABEL))
+ continue;
if (mgt_cli_askchild(status, p, "vcl.load \"%s\" %s %d%s\n",
vp->name, vp->fname, vp->warm, vp->state))
return (1);
free(*p);
+ *p = NULL;
+ }
+ VTAILQ_FOREACH(vp, &vclhead, list) {
+ if (strcmp(vp->state, VCL_STATE_LABEL))
+ continue;
+ if (mgt_cli_askchild(status, p, "vcl.label %s %s\n",
+ vp->name, vp->label->name))
+ return (1);
+ free(*p);
+ *p = NULL;
}
if (mgt_cli_askchild(status, p, "vcl.use \"%s\"\n", active_vcl->name))
return (1);
free(*p);
+ *p = NULL;
if (mgt_cli_askchild(status, p, "start\n"))
return (1);
free(*p);
@@ -307,11 +324,11 @@ mcf_find_vcl(struct cli *cli, const char *name)
struct vclprog *vp;
vp = mgt_vcl_byname(name);
- if (vp != NULL)
- return (vp);
- VCLI_SetResult(cli, CLIS_PARAM);
- VCLI_Out(cli, "No configuration named %s known.", name);
- return (NULL);
+ if (vp == NULL) {
+ VCLI_SetResult(cli, CLIS_PARAM);
+ VCLI_Out(cli, "No configuration named %s known.", name);
+ }
+ return (vp);
}
static void __match_proto__(cli_func_t)
@@ -323,6 +340,11 @@ mcf_vcl_state(struct cli *cli, const char * const *av, void *priv)
vp = mcf_find_vcl(cli, av[2]);
if (vp == NULL)
return;
+ if (!strcmp(vp->state, VCL_STATE_LABEL)) {
+ VCLI_Out(cli, "Labels are always warm");
+ VCLI_SetResult(cli, CLIS_PARAM);
+ return;
+ }
if (!strcmp(vp->state, av[3]))
return;
@@ -399,6 +421,16 @@ mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv)
VCLI_Out(cli, "Cannot discard active VCL program\n");
return;
}
+ if (!strcmp(vp->state, VCL_STATE_LABEL)) {
+ vp->label->label = NULL;
+ vp->label = NULL;
+ }
+
+ if (vp->label != NULL) {
+ VCLI_SetResult(cli, CLIS_PARAM);
+ VCLI_Out(cli, "Must remove label to discard VCL\n");
+ return;
+ }
(void)mgt_vcl_setstate(cli, vp, VCL_STATE_COLD);
if (child_pid >= 0) {
/* XXX If this fails the child is crashing, figure that later */
@@ -425,14 +457,64 @@ mcf_vcl_list(struct cli *cli, const char * const *av, void *priv)
free(p);
} else {
VTAILQ_FOREACH(vp, &vclhead, list) {
- VCLI_Out(cli, "%-10s %4s/%-8s %6s %s\n",
+ VCLI_Out(cli, "%-10s %5s",
vp == active_vcl ? "active" : "available",
- vp->state, vp->warm ? "warm" : "cold", "",
- vp->name);
+ vp->state);
+ VCLI_Out(cli, "/%-8s", vp->warm ? "warm" : "cold");
+ VCLI_Out(cli, " %6s %s", "", vp->name);
+ if (vp->label != NULL)
+ VCLI_Out(cli, " %s %s",
+ strcmp(vp->state, VCL_STATE_LABEL) ?
+ "<-" : "->", vp->label->name);
+ VCLI_Out(cli, "\n");
}
}
}
+static void __match_proto__(cli_func_t)
+mcf_vcl_label(struct cli *cli, const char * const *av, void *priv)
+{
+ struct vclprog *vpl;
+ struct vclprog *vpt;
+ unsigned status;
+ char *p;
+ int i;
+
+ (void)av;
+ (void)priv;
+ vpt = mcf_find_vcl(cli, av[3]);
+ if (vpt == NULL)
+ return;
+ if (!strcmp(vpt->state, VCL_STATE_LABEL)) {
+ VCLI_SetResult(cli, CLIS_PARAM);
+ VCLI_Out(cli, "VCL labels cannot point to labels");
+ return;
+ }
+ vpl = mgt_vcl_byname(av[2]);
+ if (vpl == NULL)
+ vpl = mgt_vcl_add(av[2], NULL, VCL_STATE_LABEL);
+ AN(vpl);
+ if (strcmp(vpl->state, VCL_STATE_LABEL)) {
+ VCLI_SetResult(cli, CLIS_PARAM);
+ VCLI_Out(cli, "%s is not a label", vpl->name);
+ return;
+ }
+ vpl->warm = 1;
+ if (vpl->label != NULL)
+ vpl->label->label = NULL;
+ vpl->label = vpt;
+ vpt->label = vpl;
+ if (child_pid < 0)
+ return;
+
+ i = mgt_cli_askchild(&status, &p, "vcl.label %s %s\n", av[2], av[3]);
+ if (i) {
+ VCLI_SetResult(cli, status);
+ VCLI_Out(cli, "%s", p);
+ }
+ free(p);
+}
+
/*--------------------------------------------------------------------*/
static int __match_proto__(vev_cb_f)
@@ -459,6 +541,7 @@ static struct cli_proto cli_vcl[] = {
{ CLICMD_VCL_STATE, "", mcf_vcl_state },
{ CLICMD_VCL_DISCARD, "", mcf_vcl_discard },
{ CLICMD_VCL_LIST, "", mcf_vcl_list },
+ { CLICMD_VCL_LABEL, "", mcf_vcl_label },
{ NULL }
};
diff --git a/bin/varnishtest/tests/v00048.vtc b/bin/varnishtest/tests/v00048.vtc
new file mode 100644
index 0000000..6accba4
--- /dev/null
+++ b/bin/varnishtest/tests/v00048.vtc
@@ -0,0 +1,84 @@
+varnishtest "Test VCL labels"
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+varnish v1 -vcl+backend {}
+
+varnish v1 -vcl+backend {
+ sub vcl_recv {
+ return (synth(400));
+ }
+}
+
+varnish v1 -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 400
+} -run
+
+varnish v1 -cliok "vcl.use vcl1"
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+varnish v1 -cliok "vcl.list"
+varnish v1 -clierr 106 "vcl.label foo vcl0"
+varnish v1 -cliok "vcl.label foo vcl1"
+varnish v1 -clierr 106 "vcl.state foo cold"
+varnish v1 -clierr 106 "vcl.label bar foo"
+varnish v1 -clierr 106 "vcl.discard vcl1"
+varnish v1 -cliok "vcl.list"
+varnish v1 -cliok "vcl.use foo"
+varnish v1 -clierr 106 "vcl.discard foo"
+varnish v1 -cliok "vcl.list"
+
+client c1 -run
+
+varnish v1 -cliok "vcl.label foo vcl2"
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 400
+} -run
+
+varnish v1 -cliok "vcl.use vcl1"
+varnish v1 -cliok "vcl.list"
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
+
+varnish v1 -cliok "vcl.discard foo"
+varnish v1 -clierr 106 "vcl.discard foo"
+
+varnish v1 -stop
+varnish v1 -cliok "vcl.list"
+varnish v1 -clierr 106 "vcl.label foo vcl0"
+varnish v1 -cliok "vcl.label foo vcl1"
+varnish v1 -clierr 106 "vcl.label bar foo"
+varnish v1 -clierr 106 "vcl.discard vcl1"
+varnish v1 -cliok "vcl.list"
+varnish v1 -cliok "vcl.use foo"
+varnish v1 -clierr 106 "vcl.discard foo"
+varnish v1 -cliok "vcl.list"
+
+server s1 -start
+
+varnish v1 -start
+client c1 -run
+varnish v1 -stop
+varnish v1 -cliok "vcl.use vcl1"
+varnish v1 -cliok "vcl.discard foo"
+varnish v1 -clierr 106 "vcl.discard foo"
+
diff --git a/include/tbl/cli_cmds.h b/include/tbl/cli_cmds.h
index 7869313..a0ccb4b 100644
--- a/include/tbl/cli_cmds.h
+++ b/include/tbl/cli_cmds.h
@@ -95,7 +95,7 @@ CLI_CMD(VCL_STATE,
CLI_CMD(VCL_DISCARD,
"vcl.discard",
- "vcl.discard <configname>",
+ "vcl.discard <configname|label>",
"Unload the named configuration (when possible).",
"",
1, 1
@@ -119,12 +119,20 @@ CLI_CMD(VCL_SHOW,
CLI_CMD(VCL_USE,
"vcl.use",
- "vcl.use <configname>",
+ "vcl.use <configname|label>",
"Switch to the named configuration immediately.",
"",
1, 1
)
+CLI_CMD(VCL_LABEL,
+ "vcl.label",
+ "vcl.label <label> <configname>",
+ "Apply label to configuration.",
+ "",
+ 2, 2
+)
+
CLI_CMD(PARAM_SHOW,
"param.show",
"param.show [-l] [<param>]",
More information about the varnish-commit
mailing list