Commit 34350d5e authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

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...
parent 13b4890b
......@@ -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,12 +804,48 @@ 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)
{
......@@ -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 }
};
......
......@@ -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 }
};
......
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"
......@@ -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>]",
......
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