Commit b113c219 authored by Geoff Simmons's avatar Geoff Simmons

Add the req-disposition field to VarnishConfig.

Ref #24
parent ddcfac81
......@@ -278,6 +278,94 @@ spec:
type: boolean
word-boundary:
type: boolean
req-disposition:
type: array
minItems: 1
items:
type: object
required:
- conditions
- disposition
properties:
conditions:
type: array
minItems: 1
items:
type: object
required:
- comparand
properties:
comparand:
type: string
pattern: "^req\\.(url|method|proto|esi_level|restarts|http\\.[a-zA-Z0-9!#$%&'*+.^_`|~-]+)$"
compare:
enum:
- equal
- not-equal
- match
- not-match
- prefix
- not-prefix
- exists
- not-exists
- greater
- greater-equal
- less
- less-equal
type: string
values:
type: array
minItems: 1
items:
type: string
count:
type: integer
minimum: 0
match-flags:
type: object
properties:
max-mem:
type: integer
min: 0
anchor:
type: string
enum:
- none
- start
- both
utf8:
type: boolean
posix-syntax:
type: boolean
longest-match:
type: boolean
literal:
type: boolean
never-capture:
type: boolean
case-sensitive:
type: boolean
perl-classes:
type: boolean
word-boundary:
type: boolean
disposition:
type: object
required:
- action
properties:
action:
enum:
- hash
- pass
- pipe
- purge
- synth
type: string
status:
type: integer
minimum: 200
maximum: 599
status:
acceptedNames:
kind: VarnishConfig
......
# Configuration for disposition of client requests that permits cache
# lookups for requests with Cookie or Authorization headers, and
# handles some requests differently from vcl_recv in builtin.vcl.
apiVersion: "ingress.varnish-cache.org/v1alpha1"
kind: VarnishConfig
metadata:
name: alt-recv-cfg
spec:
# The services array is required and must have at least one element.
# Lists the Service names of Varnish services in the same namespace
# to which this config is to be applied.
services:
- varnish-ingress
req-disposition:
- conditions:
- comparand: req.http.Host
compare: not-exists
- comparand: req.esi_level
count: 0
- comparand: req.proto
compare: prefix
values:
- HTTP/1.1
match-flags:
case-insensitive: true
disposition:
action: synth
status: 400
- conditions:
- comparand: req.method
compare: equal
values:
- CONNECT
disposition:
action: pipe
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
- PUT
- POST
- TRACE
- OPTIONS
- DELETE
- PATCH
disposition:
action: synth
status: 405
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
disposition:
action: pass
# Configuration for disposition of client requests that re-implements
# vcl_recv in builtin.vcl.
apiVersion: "ingress.varnish-cache.org/v1alpha1"
kind: VarnishConfig
metadata:
name: builtin-recv-cfg
spec:
# The services array is required and must have at least one element.
# Lists the Service names of Varnish services in the same namespace
# to which this config is to be applied.
services:
- varnish-ingress
req-disposition:
- conditions:
- comparand: req.method
compare: equal
values:
- PRI
disposition:
action: synth
status: 405
- conditions:
- comparand: req.http.Host
compare: not-exists
- comparand: req.esi_level
count: 0
- comparand: req.proto
compare: prefix
values:
- HTTP/1.1
match-flags:
case-sensitive: false
disposition:
action: synth
status: 400
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
- PUT
- POST
- TRACE
- OPTIONS
- DELETE
- PATCH
disposition:
action: pipe
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
disposition:
action: pass
- conditions:
- comparand: req.http.Cookie
compare: exists
disposition:
action: pass
- conditions:
- comparand: req.http.Authorization
compare: exists
disposition:
action: pass
# Configuration for disposition of client requests that permits cache
# lookups for requests with Cookie or Authorization headers, and
# defines URL path patterns for which cache lookups are invoked or
# bypassed.
apiVersion: "ingress.varnish-cache.org/v1alpha1"
kind: VarnishConfig
metadata:
name: cacheability-cfg
spec:
# The services array is required and must have at least one element.
# Lists the Service names of Varnish services in the same namespace
# to which this config is to be applied.
services:
- varnish-ingress
req-disposition:
- conditions:
- comparand: req.http.Host
compare: not-exists
- comparand: req.esi_level
count: 0
- comparand: req.proto
compare: prefix
values:
- HTTP/1.1
match-flags:
case-insensitive: true
disposition:
action: synth
status: 400
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
- PUT
- POST
- TRACE
- OPTIONS
- DELETE
- PATCH
disposition:
action: synth
status: 405
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
disposition:
action: pass
- conditions:
- comparand: req.url
compare: match
values:
- \.png$
- \.jpe?g$
- \.css$
- \.js$
disposition:
action: hash
- conditions:
- comparand: req.url
compare: prefix
values:
- /interactive/
- /basket/
- /personal/
- /dynamic/
disposition:
action: pass
# Configuration for disposition of client requests that permits cache
# lookups for requests with Cookie or Authorization headers, and
# allows use of the PURGE request method to purge cache objects.
apiVersion: "ingress.varnish-cache.org/v1alpha1"
kind: VarnishConfig
metadata:
name: purge-cfg
spec:
# The services array is required and must have at least one element.
# Lists the Service names of Varnish services in the same namespace
# to which this config is to be applied.
services:
- varnish-ingress
req-disposition:
- conditions:
- comparand: req.http.Host
compare: not-exists
- comparand: req.esi_level
count: 0
- comparand: req.proto
compare: prefix
values:
- HTTP/1.1
match-flags:
case-insensitive: true
disposition:
action: synth
status: 400
- conditions:
- comparand: req.method
compare: equal
values:
- PURGE
disposition:
action: purge
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
- PUT
- POST
- TRACE
- OPTIONS
- DELETE
- PATCH
disposition:
action: synth
status: 405
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
disposition:
action: pass
# Configuration for disposition of client requests that permits cache
# lookups for requests with Cookie or Authorization headers, and
# defines a whitelist for requests based on URL path prefixes.
apiVersion: "ingress.varnish-cache.org/v1alpha1"
kind: VarnishConfig
metadata:
name: url-whitelist-cfg
spec:
# The services array is required and must have at least one element.
# Lists the Service names of Varnish services in the same namespace
# to which this config is to be applied.
services:
- varnish-ingress
req-disposition:
- conditions:
- comparand: req.http.Host
compare: not-exists
- comparand: req.esi_level
count: 0
- comparand: req.proto
compare: prefix
values:
- HTTP/1.1
match-flags:
case-insensitive: true
disposition:
action: synth
status: 400
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
- PUT
- POST
- TRACE
- OPTIONS
- DELETE
- PATCH
disposition:
action: synth
status: 405
- conditions:
- comparand: req.method
compare: not-equal
values:
- GET
- HEAD
disposition:
action: pass
- conditions:
- comparand: req.url
compare: not-prefix
values:
- /tea/
- /coffee/
disposition:
action: synth
status: 403
......@@ -9,6 +9,7 @@ require (
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff // indirect
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect
github.com/google/go-cmp v0.2.0
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
github.com/googleapis/gnostic v0.2.0 // indirect
github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f // indirect
......
......@@ -32,6 +32,8 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
......
......@@ -49,12 +49,13 @@ type VarnishConfig struct {
// VarnishConfigSpec corresponds to the spec section of a
// VarnishConfig Custom Resource.
type VarnishConfigSpec struct {
Services []string `json:"services,omitempty"`
SelfSharding *SelfShardSpec `json:"self-sharding,omitempty"`
VCL string `json:"vcl,omitempty"`
Auth []AuthSpec `json:"auth,omitempty"`
ACLs []ACLSpec `json:"acl,omitempty"`
Rewrites []RewriteSpec `json:"rewrites,omitempty"`
Services []string `json:"services,omitempty"`
SelfSharding *SelfShardSpec `json:"self-sharding,omitempty"`
VCL string `json:"vcl,omitempty"`
Auth []AuthSpec `json:"auth,omitempty"`
ACLs []ACLSpec `json:"acl,omitempty"`
Rewrites []RewriteSpec `json:"rewrites,omitempty"`
ReqDispositions []RequestDispSpec `json:"req-disposition,omitempty"`
}
// SelfShardSpec specifies self-sharding in a Varnish cluster.
......@@ -333,6 +334,91 @@ type RewriteSpec struct {
Select SelectType `json:"select,omitempty"`
}
// ReqCompareType classifies comparison operations performed for the
// Conditions in a request disposition specification.
type ReqCompareType string
const (
// ReqEqual specifies equality -- string equality, numeric
// equality, or membership in a set of fixed strings.
ReqEqual ReqCompareType = "equal"
// ReqNotEqual specifies non-equality.
ReqNotEqual = "not-equal"
// ReqMatch specifies a regular expression match.
ReqMatch = "match"
// ReqNotMatch specifies a regular expression non-match.
ReqNotMatch = "not-match"
// ReqPrefix specifies that a string has a prefix in a set of
// fixed strings.
ReqPrefix = "prefix"
// ReqNotPrefix specifies that a string does not have a prefix
// in a set of fixed strings.
ReqNotPrefix = "not-prefix"
// Exists specifies that a request header exists.
Exists = "exists"
// NotExists specifies that a request header does not exist.
NotExists = "not-exists"
// Greater specifies the > relation between a VCL variable
// with a numeric value and a constant.
Greater = "greater"
// GreaterEqual specifies the >= relation for a numeric VCL
// variable and a constant.
GreaterEqual = "greater-equal"
// Less specifies the < relation for a numeric VCL variable
// and a constant.
Less = "less"
// LessEqual specifies the <= relation for a numeric VCL
// variable and a constant.
LessEqual = "less-equal"
)
// ReqCondition specifies (one of) the conditions that must be true if
// a client request disposition is to be executed.
type ReqCondition struct {
Values []string `json:"values,omitempty"`
MatchFlags *MatchFlagsType `json:"match-flags,omitempty"`
Count *int64 `json:"count,omitempty"`
Comparand string `json:"comparand"`
Compare ReqCompareType `json:"compare,omitempty"`
}
// RecvReturn is a name for the disposition of a client request.
// See: https://varnish-cache.org/docs/6.1/reference/states.html
type RecvReturn string
const (
// RecvHash to invoke cache lookup.
RecvHash RecvReturn = "hash"
// RecvPass to bypass cache lookup.
RecvPass = "pass"
// RecvPipe for pipe mode -- Varnish passes data between
// client and backend with no further intervention.
RecvPipe = "pipe"
// RecvPurge to purge a cache object.
// See: https://varnish-cache.org/docs/6.1/users-guide/purging.html?highlight=purge#http-purging
RecvPurge = "purge"
// RecvSynth to generate a synthetic response with a given
// HTTP response status.
RecvSynth = "synth"
)
// DispositionSpec specifies the disposition of a client request when
// associated Conditions are met.
//
// Status is the HTTP response status to set when Action is RecvSynth;
// ignored for other values of Action.
type DispositionSpec struct {
Action RecvReturn `json:"action"`
Status *int64 `json:"status,omitempty"`
}
// RequestDispSpec specifies the disposition of a client request when
// all of the Conditions are met.
type RequestDispSpec struct {
Conditions []ReqCondition `json:"conditions"`
Disposition DispositionSpec `json:"disposition"`
}
// VarnishConfigStatus is the status for a VarnishConfig resource
// type VarnishConfigStatus struct {
// AvailableReplicas int32 `json:"availableReplicas"`
......
......@@ -594,6 +594,37 @@ func (worker *NamespaceWorker) configACL(spec *vcl.Spec,
return nil
}
func configMatchFlags(flags vcr_v1alpha1.MatchFlagsType) vcl.MatchFlagsType {
vclFlags := vcl.MatchFlagsType{
UTF8: flags.UTF8,
PosixSyntax: flags.PosixSyntax,
LongestMatch: flags.LongestMatch,
Literal: flags.Literal,
NeverCapture: flags.NeverCapture,
PerlClasses: flags.PerlClasses,
WordBoundary: flags.WordBoundary,
}
if flags.MaxMem != nil && *flags.MaxMem != 0 {
vclFlags.MaxMem = *flags.MaxMem
}
if flags.CaseSensitive == nil {
vclFlags.CaseSensitive = true
} else {
vclFlags.CaseSensitive = *flags.CaseSensitive
}
switch flags.Anchor {
case vcr_v1alpha1.None:
vclFlags.Anchor = vcl.None
case vcr_v1alpha1.Start:
vclFlags.Anchor = vcl.Start
case vcr_v1alpha1.Both:
vclFlags.Anchor = vcl.Both
default:
vclFlags.Anchor = vcl.None
}
return vclFlags
}
func (worker *NamespaceWorker) configRewrites(spec *vcl.Spec,
vcfg *vcr_v1alpha1.VarnishConfig) error {
......@@ -715,40 +746,95 @@ func (worker *NamespaceWorker) configRewrites(spec *vcl.Spec,
}
if rw.MatchFlags != nil {
vclRw.MatchFlags = vcl.MatchFlagsType{
UTF8: rw.MatchFlags.UTF8,
PosixSyntax: rw.MatchFlags.PosixSyntax,
LongestMatch: rw.MatchFlags.LongestMatch,
Literal: rw.MatchFlags.Literal,
NeverCapture: rw.MatchFlags.NeverCapture,
PerlClasses: rw.MatchFlags.PerlClasses,
WordBoundary: rw.MatchFlags.WordBoundary,
}
if rw.MatchFlags.MaxMem != nil &&
*rw.MatchFlags.MaxMem != 0 {
vclRw.MatchFlags = configMatchFlags(*rw.MatchFlags)
} else {
vclRw.MatchFlags.CaseSensitive = true
}
spec.Rewrites[i] = vclRw
}
return nil
}
vclRw.MatchFlags.MaxMem = *rw.MatchFlags.MaxMem
func (worker *NamespaceWorker) configReqDisps(spec *vcl.Spec,
reqDisps []vcr_v1alpha1.RequestDispSpec, kind, namespace, name string) {
if len(reqDisps) == 0 {
worker.log.Infof("No request disposition specs found for %s "+
"%s/%s", kind, namespace, name)
return
}
worker.log.Infof("Configuring request dispositions for %s %s/%s",
kind, namespace, name)
spec.Dispositions = make([]vcl.DispositionSpec, len(reqDisps))
for i, disp := range reqDisps {
worker.log.Tracef("ReqDisposition: %+v", disp)
vclDisp := vcl.DispositionSpec{
Conditions: make([]vcl.Condition, len(disp.Conditions)),
}
for j, cond := range disp.Conditions {
vclCond := vcl.Condition{
Comparand: cond.Comparand,
}
if rw.MatchFlags.CaseSensitive == nil {
vclRw.MatchFlags.CaseSensitive = true
} else {
vclRw.MatchFlags.CaseSensitive =
*rw.MatchFlags.CaseSensitive
if len(cond.Values) > 0 {
vclCond.Values = make([]string, len(cond.Values))
copy(vclCond.Values, cond.Values)
}
if cond.Count != nil {
count := uint(*cond.Count)
vclCond.Count = &count
}
switch rw.MatchFlags.Anchor {
case vcr_v1alpha1.None:
vclRw.MatchFlags.Anchor = vcl.None
case vcr_v1alpha1.Start:
vclRw.MatchFlags.Anchor = vcl.Start
case vcr_v1alpha1.Both:
vclRw.MatchFlags.Anchor = vcl.Both
switch cond.Compare {
case vcr_v1alpha1.ReqEqual:
vclCond.Compare = vcl.ReqEqual
vclCond.Negate = false
case vcr_v1alpha1.ReqNotEqual:
vclCond.Compare = vcl.ReqEqual
vclCond.Negate = true
case vcr_v1alpha1.ReqMatch:
vclCond.Compare = vcl.ReqMatch
vclCond.Negate = false
case vcr_v1alpha1.ReqNotMatch:
vclCond.Compare = vcl.ReqMatch
vclCond.Negate = true
case vcr_v1alpha1.ReqPrefix:
vclCond.Compare = vcl.ReqPrefix
vclCond.Negate = false
case vcr_v1alpha1.ReqNotPrefix:
vclCond.Compare = vcl.ReqPrefix
vclCond.Negate = true
case vcr_v1alpha1.Exists:
vclCond.Compare = vcl.Exists
vclCond.Negate = false
case vcr_v1alpha1.NotExists:
vclCond.Compare = vcl.Exists
vclCond.Negate = true
case vcr_v1alpha1.Greater:
vclCond.Compare = vcl.Greater
case vcr_v1alpha1.GreaterEqual:
vclCond.Compare = vcl.GreaterEqual
case vcr_v1alpha1.Less:
vclCond.Compare = vcl.Less
case vcr_v1alpha1.LessEqual: