[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