Merge pull request #3313 from skeeey/master

Support cookie expires
This commit is contained in:
k8s-ci-robot 2018-11-02 06:53:46 -07:00 committed by GitHub
commit b5619cc978
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 91 additions and 7 deletions

View file

@ -39,10 +39,19 @@ const (
// one isn't supplied and affinity is set to "cookie". // one isn't supplied and affinity is set to "cookie".
annotationAffinityCookieHash = "session-cookie-hash" annotationAffinityCookieHash = "session-cookie-hash"
defaultAffinityCookieHash = "md5" defaultAffinityCookieHash = "md5"
// This is used to control the cookie expires, its value is a number of seconds until the
// cookie expires
annotationAffinityCookieExpires = "session-cookie-expires"
// This is used to control the cookie expires, its value is a number of seconds until the
// cookie expires
annotationAffinityCookieMaxAge = "session-cookie-max-age"
) )
var ( var (
affinityCookieHashRegex = regexp.MustCompile(`^(index|md5|sha1)$`) affinityCookieHashRegex = regexp.MustCompile(`^(index|md5|sha1)$`)
affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`)
) )
// Config describes the per ingress session affinity config // Config describes the per ingress session affinity config
@ -58,17 +67,23 @@ type Cookie struct {
Name string `json:"name"` Name string `json:"name"`
// The hash that will be used to encode the cookie in case of cookie affinity type // The hash that will be used to encode the cookie in case of cookie affinity type
Hash string `json:"hash"` Hash string `json:"hash"`
// The time duration to control cookie expires
Expires string `json:"expires"`
// The number of seconds until the cookie expires
MaxAge string `json:"maxage"`
} }
// cookieAffinityParse gets the annotation values related to Cookie Affinity // cookieAffinityParse gets the annotation values related to Cookie Affinity
// It also sets default values when no value or incorrect value is found // It also sets default values when no value or incorrect value is found
func (a affinity) cookieAffinityParse(ing *extensions.Ingress) *Cookie { func (a affinity) cookieAffinityParse(ing *extensions.Ingress) *Cookie {
cookie := &Cookie{}
sn, err := parser.GetStringAnnotation(annotationAffinityCookieName, ing) sn, err := parser.GetStringAnnotation(annotationAffinityCookieName, ing)
if err != nil || sn == "" { if err != nil || sn == "" {
glog.V(3).Infof("Ingress %v: No value found in annotation %v. Using the default %v", ing.Name, annotationAffinityCookieName, defaultAffinityCookieName) glog.V(3).Infof("Ingress %v: No value found in annotation %v. Using the default %v", ing.Name, annotationAffinityCookieName, defaultAffinityCookieName)
sn = defaultAffinityCookieName sn = defaultAffinityCookieName
} }
cookie.Name = sn
sh, err := parser.GetStringAnnotation(annotationAffinityCookieHash, ing) sh, err := parser.GetStringAnnotation(annotationAffinityCookieHash, ing)
@ -76,11 +91,22 @@ func (a affinity) cookieAffinityParse(ing *extensions.Ingress) *Cookie {
glog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Setting it to default %v", ing.Name, annotationAffinityCookieHash, defaultAffinityCookieHash) glog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Setting it to default %v", ing.Name, annotationAffinityCookieHash, defaultAffinityCookieHash)
sh = defaultAffinityCookieHash sh = defaultAffinityCookieHash
} }
cookie.Hash = sh
return &Cookie{ se, err := parser.GetStringAnnotation(annotationAffinityCookieExpires, ing)
Name: sn, if err != nil || !affinityCookieExpiresRegex.MatchString(se) {
Hash: sh, glog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieExpires)
se = ""
} }
cookie.Expires = se
sm, err := parser.GetStringAnnotation(annotationAffinityCookieMaxAge, ing)
if err != nil || !affinityCookieExpiresRegex.MatchString(sm) {
glog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieMaxAge)
sm = ""
}
cookie.MaxAge = sm
return cookie
} }
// NewParser creates a new Affinity annotation parser // NewParser creates a new Affinity annotation parser

View file

@ -69,6 +69,8 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(annotationAffinityType)] = "cookie" data[parser.GetAnnotationWithPrefix(annotationAffinityType)] = "cookie"
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieHash)] = "sha123" data[parser.GetAnnotationWithPrefix(annotationAffinityCookieHash)] = "sha123"
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieName)] = "INGRESSCOOKIE" data[parser.GetAnnotationWithPrefix(annotationAffinityCookieName)] = "INGRESSCOOKIE"
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieExpires)] = "4500"
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieMaxAge)] = "3000"
ing.SetAnnotations(data) ing.SetAnnotations(data)
affin, _ := NewParser(&resolver.Mock{}).Parse(ing) affin, _ := NewParser(&resolver.Mock{}).Parse(ing)
@ -88,4 +90,12 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
if nginxAffinity.Cookie.Name != "INGRESSCOOKIE" { if nginxAffinity.Cookie.Name != "INGRESSCOOKIE" {
t.Errorf("expected route as sticky-name but returned %v", nginxAffinity.Cookie.Name) t.Errorf("expected route as sticky-name but returned %v", nginxAffinity.Cookie.Name)
} }
if nginxAffinity.Cookie.Expires != "4500" {
t.Errorf("expected 1h as sticky-expires but returned %v", nginxAffinity.Cookie.Expires)
}
if nginxAffinity.Cookie.MaxAge != "3000" {
t.Errorf("expected 3000 as sticky-max-age but returned %v", nginxAffinity.Cookie.MaxAge)
}
} }

View file

@ -406,6 +406,8 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
if anns.SessionAffinity.Type == "cookie" { if anns.SessionAffinity.Type == "cookie" {
ups.SessionAffinity.CookieSessionAffinity.Name = anns.SessionAffinity.Cookie.Name ups.SessionAffinity.CookieSessionAffinity.Name = anns.SessionAffinity.Cookie.Name
ups.SessionAffinity.CookieSessionAffinity.Hash = anns.SessionAffinity.Cookie.Hash ups.SessionAffinity.CookieSessionAffinity.Hash = anns.SessionAffinity.Cookie.Hash
ups.SessionAffinity.CookieSessionAffinity.Expires = anns.SessionAffinity.Cookie.Expires
ups.SessionAffinity.CookieSessionAffinity.MaxAge = anns.SessionAffinity.Cookie.MaxAge
locs := ups.SessionAffinity.CookieSessionAffinity.Locations locs := ups.SessionAffinity.CookieSessionAffinity.Locations
if _, ok := locs[host]; !ok { if _, ok := locs[host]; !ok {

View file

@ -109,6 +109,8 @@ type SessionAffinityConfig struct {
type CookieSessionAffinity struct { type CookieSessionAffinity struct {
Name string `json:"name"` Name string `json:"name"`
Hash string `json:"hash"` Hash string `json:"hash"`
Expires string `json:"expires,omitempty"`
MaxAge string `json:"maxage,omitempty"`
Locations map[string][]string `json:"locations,omitempty"` Locations map[string][]string `json:"locations,omitempty"`
} }

View file

@ -15,6 +15,8 @@ function _M.new(self, backend)
local o = { local o = {
instance = self.factory:new(nodes), instance = self.factory:new(nodes),
cookie_name = backend["sessionAffinityConfig"]["cookieSessionAffinity"]["name"] or "route", cookie_name = backend["sessionAffinityConfig"]["cookieSessionAffinity"]["name"] or "route",
cookie_expires = backend["sessionAffinityConfig"]["cookieSessionAffinity"]["expires"],
cookie_max_age = backend["sessionAffinityConfig"]["cookieSessionAffinity"]["maxage"],
digest_func = digest_func, digest_func = digest_func,
} }
setmetatable(o, self) setmetatable(o, self)
@ -37,14 +39,24 @@ local function set_cookie(self, value)
ngx.log(ngx.ERR, err) ngx.log(ngx.ERR, err)
end end
local ok local cookie_data = {
ok, err = cookie:set({
key = self.cookie_name, key = self.cookie_name,
value = value, value = value,
path = ngx.var.location_path, path = ngx.var.location_path,
domain = ngx.var.host, domain = ngx.var.host,
httponly = true, httponly = true,
}) }
if self.cookie_expires and self.cookie_expires ~= "" then
cookie_data.expires = ngx.cookie_time(tonumber(self.cookie_expires))
end
if self.cookie_max_age and self.cookie_max_age ~= "" then
cookie_data.max_age = tonumber(self.cookie_max_age)
end
local ok
ok, err = cookie:set(cookie_data)
if not ok then if not ok then
ngx.log(ngx.ERR, err) ngx.log(ngx.ERR, err)
end end

View file

@ -21,6 +21,7 @@ import (
"net/http" "net/http"
"regexp" "regexp"
"strings" "strings"
"time"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -191,4 +192,35 @@ var _ = framework.IngressNginxDescribe("Annotations - Affinity/Sticky Sessions",
Expect(resp.StatusCode).Should(Equal(http.StatusOK)) Expect(resp.StatusCode).Should(Equal(http.StatusOK))
Expect(resp.Header.Get("Set-Cookie")).Should(ContainSubstring("Path=/somewhereelese;")) Expect(resp.Header.Get("Set-Cookie")).Should(ContainSubstring("Path=/somewhereelese;"))
}) })
It("should set cookie with expires", func() {
host := "cookie.foo.com"
annotations := map[string]string{
"nginx.ingress.kubernetes.io/affinity": "cookie",
"nginx.ingress.kubernetes.io/session-cookie-name": "ExpiresCookie",
"nginx.ingress.kubernetes.io/session-cookie-expires": "172800",
"nginx.ingress.kubernetes.io/session-cookie-max-age": "259200",
}
ing := framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, &annotations)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
resp, _, errs := gorequest.New().
Get(f.IngressController.HTTPURL).
Set("Host", host).
End()
Expect(len(errs)).Should(BeNumerically("==", 0))
Expect(resp.StatusCode).Should(Equal(http.StatusOK))
local, _ := time.LoadLocation("GMT")
duration, _ := time.ParseDuration("48h")
expected := time.Date(1970, time.January, 1, 0, 0, 0, 0, local).Add(duration).Format("Mon, 02-Jan-06 15:04:05 MST")
Expect(resp.Header.Get("Set-Cookie")).Should(ContainSubstring(fmt.Sprintf("Expires=%s", expected)))
Expect(resp.Header.Get("Set-Cookie")).Should(ContainSubstring("Max-Age=259200"))
})
}) })