Commit 20054d30 authored by Geoff Simmons's avatar Geoff Simmons

Add admin.VCLList() and .ActiveVCL().

parent be0a7893
...@@ -30,6 +30,8 @@ package admin ...@@ -30,6 +30,8 @@ package admin
import ( import (
"errors" "errors"
"fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
...@@ -279,3 +281,332 @@ func (adm *Admin) VCLDiscard(config string) error { ...@@ -279,3 +281,332 @@ func (adm *Admin) VCLDiscard(config string) error {
} }
return nil return nil
} }
// VCLStatus encodes whether a VCL configuration is the one in active
// use by a Varnish instance, having been specified by VCLUse() or the
// vcl.use command. It corresponds to the first column of vcl.list
// output.
type VCLStatus uint8
const (
// Active indicates that a VCL has been specified by vcl.use.
Active VCLStatus = iota
// Available indicates that a VCL has been loaded, but is not
// active.
Available
// Discarded indicates that a VCL has been marked for removal,
// having been specified by VCLDiscard() or the vcl.discard
// command.
Discarded
)
// String returns the string for a VCLStatus that appears in the first
// column of vcl.list output.
func (status VCLStatus) String() string {
switch status {
case Active:
return "active"
case Available:
return "available"
case Discarded:
return "discarded"
default:
panic("Invalid status")
}
}
// VCLState encodes the state of a VCL configuration or label. It
// corresponds the string before the slash in the second column of
// vcl.list output.
//
// If a VCL's state is Label, then it is a label that has been applied
// to a configuration with VCLLabel() or the vcl.label
// command. Otherwise it is a configuration that has been loaded with
// VCLLoad() or VCLInline(), or one of the vcl.load or vcl.inline
// commands.
type VCLState uint8
const (
// Auto indicates a VCL configuration that undergoes
// temperature transitions automatically -- it remains warm
// when not in use for the duration of the cooldown period
// (set by the varnishd parameter vcl_cooldown).
Auto VCLState = iota
// ColdState indicates a VCL configuration in the cold state
// -- it has released resources, including removal of backend
// definitions and running VMOD finalizers.
ColdState
// WarmState indicates a VCL configuration in the warm state
// -- even while inactive, resources are not yet released (so
// so that it can be quickly made active again).
WarmState
// Label indicates a VCL label, applied to a configuration
// with VCLLabel() or the vcl.label command.
Label
)
// String returns the string for a VCLState that appears in the second
// column of vcl.list output.
func (state VCLState) String() string {
switch state {
case Auto:
return "auto"
case ColdState:
return "cold"
case WarmState:
return "warm"
case Label:
return "label"
default:
panic("Invalid state")
}
}
// VCLTemperature encodes information about the initialized or
// released state of a VCL configuration. It corresponds the string
// after the slash in the second column of vcl.list output.
type VCLTemperature uint8
const (
// Init indicates that a VCL instance is initializing (for
// example, it may be running VMOD object constructors).
Init VCLTemperature = iota
// ColdTemp indicates that the VCL instance is in the cold
// state -- it has released resources, including removal of
// backend definitions and running VMOD finalizers.
ColdTemp
// WarmTemp indicates a VCL configuration in the warm state --
// even while inactive, resources are not yet released (so so
// that it can be quickly made active again).
WarmTemp
// Busy indicates that the VCL instance is transitioning to
// the cold state, but there are still active threads using
// the configuration.
Busy
// Cooling indicates a VCL instance in the transition to the
// cold state that is not in use by any thread, but has not
// yet completed releasing resources.
Cooling
)
// String returns the string for a VCLTemperature that appears in the
// second column of vcl.list output.
func (temp VCLTemperature) String() string {
switch temp {
case Init:
return "init"
case ColdTemp:
return "cold"
case WarmTemp:
return "warm"
case Busy:
return "busy"
case Cooling:
return "cooling"
default:
panic("Invalid temperature")
}
}
// VCLData represents information about loaded VCL configurations and
// labels that is returned by the vcl.list command.
//
// If the value of the State field is Label, then this object
// represents a VCL label, applied to a configuration with VCLLabel()
// or the vcl.label command. In that case, the Labels field does not
// apply, but the LabelVCL and LabelReturns fields are valid.
//
// With any other value for the State field, the object represents a
// VCL configuration, loaded with VCLLoad() or VCLInline(), or one of
// the vcl.load or vcl.inline commands. In that case the Labels field
// is valid, but the LabelVCL and LabelReturns fields are not.
type VCLData struct {
// Name is the name of the VCL configuration or label, given
// when the object was created. It corresponds to the fourth
// column of vcl.list output.
Name string
// Status indicates whether a VCL configuration is in active
// use, having been specified by VCLUse() or vcl.use. It
// corresponds to the first column of vcl.list output.
Status VCLStatus
// State indicates the state of the configuration or label.
// It is a label if and only if State==Label. The field
// corresponds to the string before the slash in the second
// column of vcl.list output.
State VCLState
// Temperature indicates the initialized or released state of
// a VCL configuration. It corresponds to the string after the
// slash in the second column of vcl.list output.
Temperature VCLTemperature
// Busy indicates the number of resources using the VCL
// configuration, such as client sessions or backend
// fetches. A VCL instance cannot transition to the cold state
// or be discarded until the busy count reaches 0. The field
// corresponds to the number in the third column of vcl.list
// output
Busy uint
// LabelVCL is only valid for a label (State==Label). It is
// the name of the VCL configuration to which the label is
// applied.
LabelVCL string
// LabelReturns is only valid for a label (State==Label),
// indicating the number of return(vcl) statements in VCL
// configurations that branch to the label.
LabelReturns uint
// Labels is only valid for a VCL configuration
// (State!=Label), indicating the number of labels that have
// been applied to it.
Labels uint
}
var (
str2status = map[string]VCLStatus{
"active": Active,
"available": Available,
"discarded": Discarded,
}
str2state = map[string]VCLState{
"auto": Auto,
"cold": ColdState,
"warm": WarmState,
"label": Label,
}
str2temp = map[string]VCLTemperature{
"init": Init,
"cold": ColdTemp,
"warm": WarmTemp,
"busy": Busy,
"cooling": Cooling,
}
vclMain = regexp.MustCompile(`^(\w+)\s+(\w+)/(\w+)\s+(\d+)\s+(\S+)`)
vclLbls = regexp.MustCompile(`->\s+(\S+)\s+\((\d+)`)
vclLblRefs = regexp.MustCompile(`(\d+)\s+labels?\)\s*$`)
)
// VCLList encapsulates the "vcl.list" command. error is non-nil when
// the response was not OK. In that case, the error is an
// UnexpectedResponse, which wraps the response from Varnish.
//
// When error is nil, VCLList() returns a slice of VCLData objects --
// structured data encoding the information in the output of vcl.list.
func (adm *Admin) VCLList() ([]VCLData, error) {
data := []VCLData{}
resp, err := adm.Command("vcl.list")
if err != nil {
return data, err
}
if resp.Code != OK {
badResp := UnexpectedResponse{Response: resp}
return data, badResp
}
vcls := strings.Split(resp.Msg, "\n")
for _, vcl := range vcls {
datum := VCLData{}
if main := vclMain.FindStringSubmatch(vcl); main != nil {
if status, ok := str2status[main[1]]; ok {
datum.Status = status
} else {
return data,
fmt.Errorf("Unknown status %s: %s",
main[1], vcl)
}
if state, ok := str2state[main[2]]; ok {
datum.State = state
} else {
return data,
fmt.Errorf("Unknown state %s: %s",
main[2], vcl)
}
if temp, ok := str2temp[main[3]]; ok {
datum.Temperature = temp
} else {
return data,
fmt.Errorf("Unknown temperature %s: %s",
main[3], vcl)
}
if b, e := strconv.ParseUint(main[4], 0, 0); e != nil {
datum.Busy = uint(b)
} else {
return data,
fmt.Errorf("Cannot parse busy field %s"+
": %s", main[4], vcl)
}
datum.Name = main[5]
} else {
return data,
fmt.Errorf("Cannot parse vcl.list output: %s",
vcl)
}
if datum.State != Label {
if r := vclLblRefs.FindStringSubmatch(vcl); r != nil {
if n, err := strconv.ParseUint(r[1], 0, 0); err != nil {
datum.Labels = uint(n)
} else {
return data,
fmt.Errorf("Cannot parse "+
"labels field %s: %s",
r[1], vcl)
}
}
} else {
if l := vclLbls.FindStringSubmatch(vcl); l != nil {
datum.LabelVCL = l[1]
if n, err := strconv.ParseUint(l[2], 0, 0); err != nil {
datum.LabelReturns = uint(n)
} else {
return data,
fmt.Errorf("Cannot parse "+
"returns field %s: %s",
l[2], vcl)
}
} else {
return data,
fmt.Errorf("Cannot parse vcl.list "+
"output: %s", vcl)
}
}
data = append(data, datum)
}
return data, nil
}
// ActiveVCL returns data about the active VCL configuration, as
// indicated by the output of the vcl.list command. error is non-nil
// if the response to vcl.list was not OK. In that case, the error is
// an UnexpectedResponse, which wraps the response from Varnish.
//
// When error is nil, ActiveVCL() returns a VCLData object --
// structured data encoding the information in the output of vcl.list.
// If no configuration is currently active, all fields of the VCLData
// object are set to their zero values; for example, the Name field is
// empty (which is otherwise never the case).
func (adm *Admin) ActiveVCL() (VCLData, error) {
vcl := VCLData{}
data, err := adm.VCLList()
if err != nil {
return vcl, err
}
for _, datum := range data {
if datum.Status == Active {
return datum, nil
}
}
return vcl, nil
}
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