Refactor annotations
This commit is contained in:
parent
f215828b1b
commit
fb33c58d18
33 changed files with 370 additions and 401 deletions
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/alias"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/auth"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/authreq"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/authtls"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/clientbodybuffersize"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/cors"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/defaultbackend"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/healthcheck"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/ipwhitelist"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/portinredirect"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/proxy"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/ratelimit"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/redirect"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/rewrite"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/secureupstream"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/serversnippet"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/serviceupstream"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/sessionaffinity"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/snippet"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/sslpassthrough"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/upstreamhashby"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/upstreamvhost"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/vtsfilterkey"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/errors"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
||||
)
|
||||
|
||||
type extractorConfig interface {
|
||||
resolver.AuthCertificate
|
||||
resolver.DefaultBackend
|
||||
resolver.Secret
|
||||
resolver.Service
|
||||
}
|
||||
|
||||
type annotationExtractor struct {
|
||||
secretResolver resolver.Secret
|
||||
annotations map[string]parser.IngressAnnotation
|
||||
}
|
||||
|
||||
func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
|
||||
return annotationExtractor{
|
||||
cfg,
|
||||
map[string]parser.IngressAnnotation{
|
||||
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
|
||||
"ExternalAuth": authreq.NewParser(),
|
||||
"CertificateAuth": authtls.NewParser(cfg),
|
||||
"CorsConfig": cors.NewParser(),
|
||||
"HealthCheck": healthcheck.NewParser(cfg),
|
||||
"Whitelist": ipwhitelist.NewParser(cfg),
|
||||
"UsePortInRedirects": portinredirect.NewParser(cfg),
|
||||
"Proxy": proxy.NewParser(cfg),
|
||||
"RateLimit": ratelimit.NewParser(cfg),
|
||||
"Redirect": redirect.NewParser(),
|
||||
"Rewrite": rewrite.NewParser(cfg),
|
||||
"SecureUpstream": secureupstream.NewParser(cfg),
|
||||
"ServiceUpstream": serviceupstream.NewParser(),
|
||||
"SessionAffinity": sessionaffinity.NewParser(),
|
||||
"SSLPassthrough": sslpassthrough.NewParser(),
|
||||
"ConfigurationSnippet": snippet.NewParser(),
|
||||
"Alias": alias.NewParser(),
|
||||
"ClientBodyBufferSize": clientbodybuffersize.NewParser(),
|
||||
"DefaultBackend": defaultbackend.NewParser(cfg),
|
||||
"UpstreamHashBy": upstreamhashby.NewParser(),
|
||||
"UpstreamVhost": upstreamvhost.NewParser(),
|
||||
"VtsFilterKey": vtsfilterkey.NewParser(),
|
||||
"ServerSnippet": serversnippet.NewParser(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) Extract(ing *extensions.Ingress) map[string]interface{} {
|
||||
anns := make(map[string]interface{})
|
||||
for name, annotationParser := range e.annotations {
|
||||
val, err := annotationParser.Parse(ing)
|
||||
glog.V(5).Infof("annotation %v in Ingress %v/%v: %v", name, ing.GetNamespace(), ing.GetName(), val)
|
||||
if err != nil {
|
||||
if errors.IsMissingAnnotations(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !errors.IsLocationDenied(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
_, alreadyDenied := anns[DeniedKeyName]
|
||||
if !alreadyDenied {
|
||||
anns[DeniedKeyName] = err
|
||||
glog.Errorf("error reading %v annotation in Ingress %v/%v: %v", name, ing.GetNamespace(), ing.GetName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
glog.V(5).Infof("error reading %v annotation in Ingress %v/%v: %v", name, ing.GetNamespace(), ing.GetName(), err)
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
anns[name] = val
|
||||
}
|
||||
}
|
||||
|
||||
return anns
|
||||
}
|
||||
|
||||
const (
|
||||
secureUpstream = "SecureUpstream"
|
||||
healthCheck = "HealthCheck"
|
||||
sslPassthrough = "SSLPassthrough"
|
||||
sessionAffinity = "SessionAffinity"
|
||||
serviceUpstream = "ServiceUpstream"
|
||||
serverAlias = "Alias"
|
||||
corsConfig = "CorsConfig"
|
||||
clientBodyBufferSize = "ClientBodyBufferSize"
|
||||
certificateAuth = "CertificateAuth"
|
||||
serverSnippet = "ServerSnippet"
|
||||
upstreamHashBy = "UpstreamHashBy"
|
||||
)
|
||||
|
||||
func (e *annotationExtractor) ServiceUpstream(ing *extensions.Ingress) bool {
|
||||
val, _ := e.annotations[serviceUpstream].Parse(ing)
|
||||
return val.(bool)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) SecureUpstream(ing *extensions.Ingress) *secureupstream.Secure {
|
||||
val, err := e.annotations[secureUpstream].Parse(ing)
|
||||
if err != nil {
|
||||
glog.Errorf("error parsing secure upstream: %v", err)
|
||||
}
|
||||
secure := val.(*secureupstream.Secure)
|
||||
return secure
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) HealthCheck(ing *extensions.Ingress) *healthcheck.Upstream {
|
||||
val, _ := e.annotations[healthCheck].Parse(ing)
|
||||
return val.(*healthcheck.Upstream)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) SSLPassthrough(ing *extensions.Ingress) bool {
|
||||
val, _ := e.annotations[sslPassthrough].Parse(ing)
|
||||
return val.(bool)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) Alias(ing *extensions.Ingress) string {
|
||||
val, _ := e.annotations[serverAlias].Parse(ing)
|
||||
return val.(string)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) ClientBodyBufferSize(ing *extensions.Ingress) string {
|
||||
val, _ := e.annotations[clientBodyBufferSize].Parse(ing)
|
||||
return val.(string)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) SessionAffinity(ing *extensions.Ingress) *sessionaffinity.AffinityConfig {
|
||||
val, _ := e.annotations[sessionAffinity].Parse(ing)
|
||||
return val.(*sessionaffinity.AffinityConfig)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) Cors(ing *extensions.Ingress) *cors.CorsConfig {
|
||||
val, _ := e.annotations[corsConfig].Parse(ing)
|
||||
return val.(*cors.CorsConfig)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) CertificateAuth(ing *extensions.Ingress) *authtls.AuthSSLConfig {
|
||||
val, err := e.annotations[certificateAuth].Parse(ing)
|
||||
if errors.IsMissingAnnotations(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("error parsing certificate auth: %v", err)
|
||||
}
|
||||
secure := val.(*authtls.AuthSSLConfig)
|
||||
return secure
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) ServerSnippet(ing *extensions.Ingress) string {
|
||||
val, _ := e.annotations[serverSnippet].Parse(ing)
|
||||
return val.(string)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) UpstreamHashBy(ing *extensions.Ingress) string {
|
||||
val, _ := e.annotations[upstreamHashBy].Parse(ing)
|
||||
return val.(string)
|
||||
}
|
||||
|
|
@ -1,353 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
"k8s.io/ingress-nginx/pkg/ingress/defaults"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
||||
)
|
||||
|
||||
const (
|
||||
annotationSecureUpstream = "ingress.kubernetes.io/secure-backends"
|
||||
annotationSecureVerifyCACert = "ingress.kubernetes.io/secure-verify-ca-secret"
|
||||
annotationUpsMaxFails = "ingress.kubernetes.io/upstream-max-fails"
|
||||
annotationUpsFailTimeout = "ingress.kubernetes.io/upstream-fail-timeout"
|
||||
annotationPassthrough = "ingress.kubernetes.io/ssl-passthrough"
|
||||
annotationAffinityType = "ingress.kubernetes.io/affinity"
|
||||
annotationCorsEnabled = "ingress.kubernetes.io/enable-cors"
|
||||
annotationCorsAllowOrigin = "ingress.kubernetes.io/cors-allow-origin"
|
||||
annotationCorsAllowMethods = "ingress.kubernetes.io/cors-allow-methods"
|
||||
annotationCorsAllowHeaders = "ingress.kubernetes.io/cors-allow-headers"
|
||||
annotationCorsAllowCredentials = "ingress.kubernetes.io/cors-allow-credentials"
|
||||
defaultCorsMethods = "GET, PUT, POST, DELETE, PATCH, OPTIONS"
|
||||
defaultCorsHeaders = "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"
|
||||
annotationAffinityCookieName = "ingress.kubernetes.io/session-cookie-name"
|
||||
annotationAffinityCookieHash = "ingress.kubernetes.io/session-cookie-hash"
|
||||
annotationUpstreamHashBy = "ingress.kubernetes.io/upstream-hash-by"
|
||||
)
|
||||
|
||||
type mockCfg struct {
|
||||
MockSecrets map[string]*apiv1.Secret
|
||||
MockServices map[string]*apiv1.Service
|
||||
}
|
||||
|
||||
func (m mockCfg) GetDefaultBackend() defaults.Backend {
|
||||
return defaults.Backend{}
|
||||
}
|
||||
|
||||
func (m mockCfg) GetSecret(name string) (*apiv1.Secret, error) {
|
||||
return m.MockSecrets[name], nil
|
||||
}
|
||||
|
||||
func (m mockCfg) GetService(name string) (*apiv1.Service, error) {
|
||||
return m.MockServices[name], nil
|
||||
}
|
||||
|
||||
func (m mockCfg) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) {
|
||||
if secret, _ := m.GetSecret(name); secret != nil {
|
||||
return &resolver.AuthSSLCert{
|
||||
Secret: name,
|
||||
CAFileName: "/opt/ca.pem",
|
||||
PemSHA: "123",
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestAnnotationExtractor(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
m := ec.Extract(ing)
|
||||
// the map at least should contains HealthCheck and Proxy information (defaults)
|
||||
if _, ok := m["HealthCheck"]; !ok {
|
||||
t.Error("expected HealthCheck annotation")
|
||||
}
|
||||
if _, ok := m["Proxy"]; !ok {
|
||||
t.Error("expected Proxy annotation")
|
||||
}
|
||||
}
|
||||
|
||||
func buildIngress() *extensions.Ingress {
|
||||
defaultBackend := extensions.IngressBackend{
|
||||
ServiceName: "default-backend",
|
||||
ServicePort: intstr.FromInt(80),
|
||||
}
|
||||
|
||||
return &extensions.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: apiv1.NamespaceDefault,
|
||||
},
|
||||
Spec: extensions.IngressSpec{
|
||||
Backend: &extensions.IngressBackend{
|
||||
ServiceName: "default-backend",
|
||||
ServicePort: intstr.FromInt(80),
|
||||
},
|
||||
Rules: []extensions.IngressRule{
|
||||
{
|
||||
Host: "foo.bar.com",
|
||||
IngressRuleValue: extensions.IngressRuleValue{
|
||||
HTTP: &extensions.HTTPIngressRuleValue{
|
||||
Paths: []extensions.HTTPIngressPath{
|
||||
{
|
||||
Path: "/foo",
|
||||
Backend: defaultBackend,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureUpstream(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
fooAnns := []struct {
|
||||
annotations map[string]string
|
||||
er bool
|
||||
}{
|
||||
{map[string]string{annotationSecureUpstream: "true"}, true},
|
||||
{map[string]string{annotationSecureUpstream: "false"}, false},
|
||||
{map[string]string{annotationSecureUpstream + "_no": "true"}, false},
|
||||
{map[string]string{}, false},
|
||||
{nil, false},
|
||||
}
|
||||
|
||||
for _, foo := range fooAnns {
|
||||
ing.SetAnnotations(foo.annotations)
|
||||
r := ec.SecureUpstream(ing)
|
||||
if r.Secure != foo.er {
|
||||
t.Errorf("Returned %v but expected %v", r, foo.er)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureVerifyCACert(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{
|
||||
MockSecrets: map[string]*apiv1.Secret{
|
||||
"default/secure-verify-ca": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "secure-verify-ca",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
anns := []struct {
|
||||
it int
|
||||
annotations map[string]string
|
||||
exists bool
|
||||
}{
|
||||
{1, map[string]string{annotationSecureUpstream: "true", annotationSecureVerifyCACert: "not"}, false},
|
||||
{2, map[string]string{annotationSecureUpstream: "false", annotationSecureVerifyCACert: "secure-verify-ca"}, false},
|
||||
{3, map[string]string{annotationSecureUpstream: "true", annotationSecureVerifyCACert: "secure-verify-ca"}, true},
|
||||
{4, map[string]string{annotationSecureUpstream: "true", annotationSecureVerifyCACert + "_not": "secure-verify-ca"}, false},
|
||||
{5, map[string]string{annotationSecureUpstream: "true"}, false},
|
||||
{6, map[string]string{}, false},
|
||||
{7, nil, false},
|
||||
}
|
||||
|
||||
for _, ann := range anns {
|
||||
ing := buildIngress()
|
||||
ing.SetAnnotations(ann.annotations)
|
||||
res := ec.SecureUpstream(ing)
|
||||
if (res.CACert.CAFileName != "") != ann.exists {
|
||||
t.Errorf("Expected exists was %v on iteration %v", ann.exists, ann.it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthCheck(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
fooAnns := []struct {
|
||||
annotations map[string]string
|
||||
eumf int
|
||||
euft int
|
||||
}{
|
||||
{map[string]string{annotationUpsMaxFails: "3", annotationUpsFailTimeout: "10"}, 3, 10},
|
||||
{map[string]string{annotationUpsMaxFails: "3"}, 3, 0},
|
||||
{map[string]string{annotationUpsFailTimeout: "10"}, 0, 10},
|
||||
{map[string]string{}, 0, 0},
|
||||
{nil, 0, 0},
|
||||
}
|
||||
|
||||
for _, foo := range fooAnns {
|
||||
ing.SetAnnotations(foo.annotations)
|
||||
r := ec.HealthCheck(ing)
|
||||
if r == nil {
|
||||
t.Errorf("Returned nil but expected a healthcheck.Upstream")
|
||||
continue
|
||||
}
|
||||
|
||||
if r.FailTimeout != foo.euft {
|
||||
t.Errorf("Returned %d but expected %d for FailTimeout", r.FailTimeout, foo.euft)
|
||||
}
|
||||
|
||||
if r.MaxFails != foo.eumf {
|
||||
t.Errorf("Returned %d but expected %d for MaxFails", r.MaxFails, foo.eumf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSLPassthrough(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
fooAnns := []struct {
|
||||
annotations map[string]string
|
||||
er bool
|
||||
}{
|
||||
{map[string]string{annotationPassthrough: "true"}, true},
|
||||
{map[string]string{annotationPassthrough: "false"}, false},
|
||||
{map[string]string{annotationPassthrough + "_no": "true"}, false},
|
||||
{map[string]string{}, false},
|
||||
{nil, false},
|
||||
}
|
||||
|
||||
for _, foo := range fooAnns {
|
||||
ing.SetAnnotations(foo.annotations)
|
||||
r := ec.SSLPassthrough(ing)
|
||||
if r != foo.er {
|
||||
t.Errorf("Returned %v but expected %v", r, foo.er)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpstreamHashBy(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
fooAnns := []struct {
|
||||
annotations map[string]string
|
||||
er string
|
||||
}{
|
||||
{map[string]string{annotationUpstreamHashBy: "$request_uri"}, "$request_uri"},
|
||||
{map[string]string{annotationUpstreamHashBy: "false"}, "false"},
|
||||
{map[string]string{annotationUpstreamHashBy + "_no": "true"}, ""},
|
||||
{map[string]string{}, ""},
|
||||
{nil, ""},
|
||||
}
|
||||
|
||||
for _, foo := range fooAnns {
|
||||
ing.SetAnnotations(foo.annotations)
|
||||
r := ec.UpstreamHashBy(ing)
|
||||
if r != foo.er {
|
||||
t.Errorf("Returned %v but expected %v", r, foo.er)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAffinitySession(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
fooAnns := []struct {
|
||||
annotations map[string]string
|
||||
affinitytype string
|
||||
hash string
|
||||
name string
|
||||
}{
|
||||
{map[string]string{annotationAffinityType: "cookie", annotationAffinityCookieHash: "md5", annotationAffinityCookieName: "route"}, "cookie", "md5", "route"},
|
||||
{map[string]string{annotationAffinityType: "cookie", annotationAffinityCookieHash: "xpto", annotationAffinityCookieName: "route1"}, "cookie", "md5", "route1"},
|
||||
{map[string]string{annotationAffinityType: "cookie", annotationAffinityCookieHash: "", annotationAffinityCookieName: ""}, "cookie", "md5", "INGRESSCOOKIE"},
|
||||
{map[string]string{}, "", "", ""},
|
||||
{nil, "", "", ""},
|
||||
}
|
||||
|
||||
for _, foo := range fooAnns {
|
||||
ing.SetAnnotations(foo.annotations)
|
||||
r := ec.SessionAffinity(ing)
|
||||
t.Logf("Testing pass %v %v %v", foo.affinitytype, foo.hash, foo.name)
|
||||
if r == nil {
|
||||
t.Errorf("Returned nil but expected a SessionAffinity.AffinityConfig")
|
||||
continue
|
||||
}
|
||||
|
||||
if r.CookieConfig.Hash != foo.hash {
|
||||
t.Errorf("Returned %v but expected %v for Hash", r.CookieConfig.Hash, foo.hash)
|
||||
}
|
||||
|
||||
if r.CookieConfig.Name != foo.name {
|
||||
t.Errorf("Returned %v but expected %v for Name", r.CookieConfig.Name, foo.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCors(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
fooAnns := []struct {
|
||||
annotations map[string]string
|
||||
corsenabled bool
|
||||
methods string
|
||||
headers string
|
||||
origin string
|
||||
credentials bool
|
||||
}{
|
||||
{map[string]string{annotationCorsEnabled: "true"}, true, defaultCorsMethods, defaultCorsHeaders, "*", true},
|
||||
{map[string]string{annotationCorsEnabled: "true", annotationCorsAllowMethods: "POST, GET, OPTIONS", annotationCorsAllowHeaders: "$nginx_version", annotationCorsAllowCredentials: "false"}, true, "POST, GET, OPTIONS", defaultCorsHeaders, "*", false},
|
||||
{map[string]string{annotationCorsEnabled: "true", annotationCorsAllowCredentials: "false"}, true, defaultCorsMethods, defaultCorsHeaders, "*", false},
|
||||
{map[string]string{}, false, defaultCorsMethods, defaultCorsHeaders, "*", true},
|
||||
{nil, false, defaultCorsMethods, defaultCorsHeaders, "*", true},
|
||||
}
|
||||
|
||||
for _, foo := range fooAnns {
|
||||
ing.SetAnnotations(foo.annotations)
|
||||
r := ec.Cors(ing)
|
||||
t.Logf("Testing pass %v %v %v %v %v", foo.corsenabled, foo.methods, foo.headers, foo.origin, foo.credentials)
|
||||
if r == nil {
|
||||
t.Errorf("Returned nil but expected a Cors.CorsConfig")
|
||||
continue
|
||||
}
|
||||
|
||||
if r.CorsEnabled != foo.corsenabled {
|
||||
t.Errorf("Returned %v but expected %v for Cors Enabled", r.CorsEnabled, foo.corsenabled)
|
||||
}
|
||||
|
||||
if r.CorsAllowHeaders != foo.headers {
|
||||
t.Errorf("Returned %v but expected %v for Cors Headers", r.CorsAllowHeaders, foo.headers)
|
||||
}
|
||||
|
||||
if r.CorsAllowMethods != foo.methods {
|
||||
t.Errorf("Returned %v but expected %v for Cors Methods", r.CorsAllowMethods, foo.methods)
|
||||
}
|
||||
|
||||
if r.CorsAllowOrigin != foo.origin {
|
||||
t.Errorf("Returned %v but expected %v for Cors Methods", r.CorsAllowOrigin, foo.origin)
|
||||
}
|
||||
|
||||
if r.CorsAllowCredentials != foo.credentials {
|
||||
t.Errorf("Returned %v but expected %v for Cors Methods", r.CorsAllowCredentials, foo.credentials)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,6 @@ import (
|
|||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"k8s.io/ingress-nginx/pkg/ingress"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
||||
|
|
@ -156,18 +155,3 @@ func (ic *NGINXController) checkMissingSecrets() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sslCertTracker holds a store of referenced Secrets in Ingress rules
|
||||
type sslCertTracker struct {
|
||||
cache.ThreadSafeStore
|
||||
}
|
||||
|
||||
func newSSLCertTracker() *sslCertTracker {
|
||||
return &sslCertTracker{
|
||||
cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sslCertTracker) DeleteAll(key string) {
|
||||
s.Delete(key)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import (
|
|||
clientset "k8s.io/client-go/kubernetes"
|
||||
|
||||
"k8s.io/ingress-nginx/pkg/ingress"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/healthcheck"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
||||
|
|
@ -316,7 +317,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
|
|||
for _, sp := range svc.Spec.Ports {
|
||||
if sp.Name == svcPort {
|
||||
if sp.Protocol == proto {
|
||||
endps = n.getEndpoints(svc, &sp, proto, &healthcheck.Upstream{})
|
||||
endps = n.getEndpoints(svc, &sp, proto, &healthcheck.Config{})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -327,7 +328,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
|
|||
for _, sp := range svc.Spec.Ports {
|
||||
if sp.Port == int32(targetPort) {
|
||||
if sp.Protocol == proto {
|
||||
endps = n.getEndpoints(svc, &sp, proto, &healthcheck.Upstream{})
|
||||
endps = n.getEndpoints(svc, &sp, proto, &healthcheck.Config{})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -379,7 +380,7 @@ func (n *NGINXController) getDefaultUpstream() *ingress.Backend {
|
|||
}
|
||||
|
||||
svc := svcObj.(*apiv1.Service)
|
||||
endps := n.getEndpoints(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, &healthcheck.Upstream{})
|
||||
endps := n.getEndpoints(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, &healthcheck.Config{})
|
||||
if len(endps) == 0 {
|
||||
glog.Warningf("service %v does not have any active endpoints", svcKey)
|
||||
endps = []ingress.Endpoint{n.DefaultEndpoint()}
|
||||
|
|
@ -398,8 +399,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
|||
servers := n.createServers(ingresses, upstreams, du)
|
||||
|
||||
for _, ing := range ingresses {
|
||||
affinity := n.annotations.SessionAffinity(ing)
|
||||
anns := n.annotations.Extract(ing)
|
||||
anns := n.getIngressAnnotations(ing)
|
||||
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
host := rule.Host
|
||||
|
|
@ -418,13 +418,11 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
|||
}
|
||||
|
||||
if server.CertificateAuth.CAFileName == "" {
|
||||
ca := n.annotations.CertificateAuth(ing)
|
||||
if ca != nil {
|
||||
server.CertificateAuth = *ca
|
||||
// It is possible that no CAFileName is found in the secret
|
||||
if server.CertificateAuth.CAFileName == "" {
|
||||
glog.V(3).Infof("secret %v does not contain 'ca.crt', mutual authentication not enabled - ingress rule %v/%v.", server.CertificateAuth.Secret, ing.Namespace, ing.Name)
|
||||
}
|
||||
server.CertificateAuth = anns.CertificateAuth
|
||||
// It is possible that no CAFileName is found in the secret
|
||||
if server.CertificateAuth.CAFileName == "" {
|
||||
glog.V(3).Infof("secret %v does not contain 'ca.crt', mutual authentication not enabled - ingress rule %v/%v.", server.CertificateAuth.Secret, ing.Namespace, ing.Name)
|
||||
|
||||
}
|
||||
} else {
|
||||
glog.V(3).Infof("server %v already contains a mutual authentication configuration - ingress rule %v/%v", server.Hostname, ing.Namespace, ing.Name)
|
||||
|
|
@ -461,7 +459,19 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
|||
loc.Port = ups.Port
|
||||
loc.Service = ups.Service
|
||||
loc.Ingress = ing
|
||||
mergeLocationAnnotations(loc, anns)
|
||||
loc.BasicDigestAuth = anns.BasicDigestAuth
|
||||
loc.ClientBodyBufferSize = anns.ClientBodyBufferSize
|
||||
loc.ConfigurationSnippet = anns.ConfigurationSnippet
|
||||
loc.CorsConfig = anns.CorsConfig
|
||||
loc.ExternalAuth = anns.ExternalAuth
|
||||
loc.Proxy = anns.Proxy
|
||||
loc.RateLimit = anns.RateLimit
|
||||
loc.Redirect = anns.Redirect
|
||||
loc.Rewrite = anns.Rewrite
|
||||
loc.UpstreamVhost = anns.UpstreamVhost
|
||||
loc.VtsFilterKey = anns.VtsFilterKey
|
||||
loc.Whitelist = anns.Whitelist
|
||||
|
||||
if loc.Redirect.FromToWWW {
|
||||
server.RedirectFromToWWW = true
|
||||
}
|
||||
|
|
@ -472,14 +482,26 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
|||
if addLoc {
|
||||
glog.V(3).Infof("adding location %v in ingress rule %v/%v upstream %v", nginxPath, ing.Namespace, ing.Name, ups.Name)
|
||||
loc := &ingress.Location{
|
||||
Path: nginxPath,
|
||||
Backend: ups.Name,
|
||||
IsDefBackend: false,
|
||||
Service: ups.Service,
|
||||
Port: ups.Port,
|
||||
Ingress: ing,
|
||||
Path: nginxPath,
|
||||
Backend: ups.Name,
|
||||
IsDefBackend: false,
|
||||
Service: ups.Service,
|
||||
Port: ups.Port,
|
||||
Ingress: ing,
|
||||
BasicDigestAuth: anns.BasicDigestAuth,
|
||||
ClientBodyBufferSize: anns.ClientBodyBufferSize,
|
||||
ConfigurationSnippet: anns.ConfigurationSnippet,
|
||||
CorsConfig: anns.CorsConfig,
|
||||
ExternalAuth: anns.ExternalAuth,
|
||||
Proxy: anns.Proxy,
|
||||
RateLimit: anns.RateLimit,
|
||||
Redirect: anns.Redirect,
|
||||
Rewrite: anns.Rewrite,
|
||||
UpstreamVhost: anns.UpstreamVhost,
|
||||
VtsFilterKey: anns.VtsFilterKey,
|
||||
Whitelist: anns.Whitelist,
|
||||
}
|
||||
mergeLocationAnnotations(loc, anns)
|
||||
|
||||
if loc.Redirect.FromToWWW {
|
||||
server.RedirectFromToWWW = true
|
||||
}
|
||||
|
|
@ -487,12 +509,12 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
|||
}
|
||||
|
||||
if ups.SessionAffinity.AffinityType == "" {
|
||||
ups.SessionAffinity.AffinityType = affinity.AffinityType
|
||||
ups.SessionAffinity.AffinityType = anns.SessionAffinity.Type
|
||||
}
|
||||
|
||||
if affinity.AffinityType == "cookie" {
|
||||
ups.SessionAffinity.CookieSessionAffinity.Name = affinity.CookieConfig.Name
|
||||
ups.SessionAffinity.CookieSessionAffinity.Hash = affinity.CookieConfig.Hash
|
||||
if anns.SessionAffinity.Type == "cookie" {
|
||||
ups.SessionAffinity.CookieSessionAffinity.Name = anns.SessionAffinity.Cookie.Name
|
||||
ups.SessionAffinity.CookieSessionAffinity.Hash = anns.SessionAffinity.Cookie.Hash
|
||||
|
||||
locs := ups.SessionAffinity.CookieSessionAffinity.Locations
|
||||
if _, ok := locs[host]; !ok {
|
||||
|
|
@ -519,7 +541,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
|||
// check if the location contains endpoints and a custom default backend
|
||||
if location.DefaultBackend != nil {
|
||||
sp := location.DefaultBackend.Spec.Ports[0]
|
||||
endps := n.getEndpoints(location.DefaultBackend, &sp, apiv1.ProtocolTCP, &healthcheck.Upstream{})
|
||||
endps := n.getEndpoints(location.DefaultBackend, &sp, apiv1.ProtocolTCP, &healthcheck.Config{})
|
||||
if len(endps) > 0 {
|
||||
glog.V(3).Infof("using custom default backend in server %v location %v (service %v/%v)",
|
||||
server.Hostname, location.Path, location.DefaultBackend.Namespace, location.DefaultBackend.Name)
|
||||
|
|
@ -617,10 +639,7 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
|||
upstreams[defUpstreamName] = du
|
||||
|
||||
for _, ing := range data {
|
||||
secUpstream := n.annotations.SecureUpstream(ing)
|
||||
hz := n.annotations.HealthCheck(ing)
|
||||
serviceUpstream := n.annotations.ServiceUpstream(ing)
|
||||
upstreamHashBy := n.annotations.UpstreamHashBy(ing)
|
||||
anns := n.getIngressAnnotations(ing)
|
||||
|
||||
var defBackend string
|
||||
if ing.Spec.Backend != nil {
|
||||
|
|
@ -635,7 +654,7 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
|||
|
||||
// Add the service cluster endpoint as the upstream instead of individual endpoints
|
||||
// if the serviceUpstream annotation is enabled
|
||||
if serviceUpstream {
|
||||
if anns.ServiceUpstream {
|
||||
endpoint, err := n.getServiceClusterEndpoint(svcKey, ing.Spec.Backend)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to get service cluster endpoint for service %s: %v", svcKey, err)
|
||||
|
|
@ -645,7 +664,7 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
|||
}
|
||||
|
||||
if len(upstreams[defBackend].Endpoints) == 0 {
|
||||
endps, err := n.serviceEndpoints(svcKey, ing.Spec.Backend.ServicePort.String(), hz)
|
||||
endps, err := n.serviceEndpoints(svcKey, ing.Spec.Backend.ServicePort.String(), &anns.HealthCheck)
|
||||
upstreams[defBackend].Endpoints = append(upstreams[defBackend].Endpoints, endps...)
|
||||
if err != nil {
|
||||
glog.Warningf("error creating upstream %v: %v", defBackend, err)
|
||||
|
|
@ -674,22 +693,22 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
|||
upstreams[name].Port = path.Backend.ServicePort
|
||||
|
||||
if !upstreams[name].Secure {
|
||||
upstreams[name].Secure = secUpstream.Secure
|
||||
upstreams[name].Secure = anns.SecureUpstream.Secure
|
||||
}
|
||||
|
||||
if upstreams[name].SecureCACert.Secret == "" {
|
||||
upstreams[name].SecureCACert = secUpstream.CACert
|
||||
upstreams[name].SecureCACert = anns.SecureUpstream.CACert
|
||||
}
|
||||
|
||||
if upstreams[name].UpstreamHashBy == "" {
|
||||
upstreams[name].UpstreamHashBy = upstreamHashBy
|
||||
upstreams[name].UpstreamHashBy = anns.UpstreamHashBy
|
||||
}
|
||||
|
||||
svcKey := fmt.Sprintf("%v/%v", ing.GetNamespace(), path.Backend.ServiceName)
|
||||
|
||||
// Add the service cluster endpoint as the upstream instead of individual endpoints
|
||||
// if the serviceUpstream annotation is enabled
|
||||
if serviceUpstream {
|
||||
if anns.ServiceUpstream {
|
||||
endpoint, err := n.getServiceClusterEndpoint(svcKey, &path.Backend)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to get service cluster endpoint for service %s: %v", svcKey, err)
|
||||
|
|
@ -699,7 +718,7 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
|||
}
|
||||
|
||||
if len(upstreams[name].Endpoints) == 0 {
|
||||
endp, err := n.serviceEndpoints(svcKey, path.Backend.ServicePort.String(), hz)
|
||||
endp, err := n.serviceEndpoints(svcKey, path.Backend.ServicePort.String(), &anns.HealthCheck)
|
||||
if err != nil {
|
||||
glog.Warningf("error obtaining service endpoints: %v", err)
|
||||
continue
|
||||
|
|
@ -759,7 +778,7 @@ func (n *NGINXController) getServiceClusterEndpoint(svcKey string, backend *exte
|
|||
// serviceEndpoints returns the upstream servers (endpoints) associated
|
||||
// to a service.
|
||||
func (n *NGINXController) serviceEndpoints(svcKey, backendPort string,
|
||||
hz *healthcheck.Upstream) ([]ingress.Endpoint, error) {
|
||||
hz *healthcheck.Config) ([]ingress.Endpoint, error) {
|
||||
svc, err := n.listers.Service.GetByName(svcKey)
|
||||
|
||||
var upstreams []ingress.Endpoint
|
||||
|
|
@ -843,7 +862,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
|||
aliases := make(map[string]string, len(data))
|
||||
|
||||
bdef := n.GetDefaultBackend()
|
||||
ngxProxy := proxy.Configuration{
|
||||
ngxProxy := proxy.Config{
|
||||
BodySize: bdef.ProxyBodySize,
|
||||
ConnectTimeout: bdef.ProxyConnectTimeout,
|
||||
SendTimeout: bdef.ProxySendTimeout,
|
||||
|
|
@ -884,9 +903,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
|||
|
||||
// initialize all the servers
|
||||
for _, ing := range data {
|
||||
|
||||
// check if ssl passthrough is configured
|
||||
sslpt := n.annotations.SSLPassthrough(ing)
|
||||
anns := n.getIngressAnnotations(ing)
|
||||
|
||||
// default upstream server
|
||||
un := du.Name
|
||||
|
|
@ -930,16 +947,14 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
|||
Service: &apiv1.Service{},
|
||||
},
|
||||
},
|
||||
SSLPassthrough: sslpt,
|
||||
SSLPassthrough: anns.SSLPassthrough,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// configure default location, alias, and SSL
|
||||
for _, ing := range data {
|
||||
// setup server-alias based on annotations
|
||||
aliasAnnotation := n.annotations.Alias(ing)
|
||||
srvsnippet := n.annotations.ServerSnippet(ing)
|
||||
anns := n.getIngressAnnotations(ing)
|
||||
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
host := rule.Host
|
||||
|
|
@ -948,11 +963,11 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
|||
}
|
||||
|
||||
// setup server aliases
|
||||
if aliasAnnotation != "" {
|
||||
if anns.Alias != "" {
|
||||
if servers[host].Alias == "" {
|
||||
servers[host].Alias = aliasAnnotation
|
||||
if _, ok := aliases[aliasAnnotation]; !ok {
|
||||
aliases[aliasAnnotation] = host
|
||||
servers[host].Alias = anns.Alias
|
||||
if _, ok := aliases["Alias"]; !ok {
|
||||
aliases["Alias"] = host
|
||||
}
|
||||
} else {
|
||||
glog.Warningf("ingress %v/%v for host %v contains an Alias but one has already been configured.",
|
||||
|
|
@ -961,14 +976,14 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
|||
}
|
||||
|
||||
//notifying the user that it has already been configured.
|
||||
if servers[host].ServerSnippet != "" && srvsnippet != "" {
|
||||
if servers[host].ServerSnippet != "" && anns.ServerSnippet != "" {
|
||||
glog.Warningf("ingress %v/%v for host %v contains a Server Snippet section that it has already been configured.",
|
||||
ing.Namespace, ing.Name, host)
|
||||
}
|
||||
|
||||
// only add a server snippet if the server does not have one previously configured
|
||||
if servers[host].ServerSnippet == "" && srvsnippet != "" {
|
||||
servers[host].ServerSnippet = srvsnippet
|
||||
if servers[host].ServerSnippet == "" && anns.ServerSnippet != "" {
|
||||
servers[host].ServerSnippet = anns.ServerSnippet
|
||||
}
|
||||
|
||||
// only add a certificate if the server does not have one previously configured
|
||||
|
|
@ -1044,7 +1059,7 @@ func (n *NGINXController) getEndpoints(
|
|||
s *apiv1.Service,
|
||||
servicePort *apiv1.ServicePort,
|
||||
proto apiv1.Protocol,
|
||||
hz *healthcheck.Upstream) []ingress.Endpoint {
|
||||
hz *healthcheck.Config) []ingress.Endpoint {
|
||||
|
||||
upsServers := []ingress.Endpoint{}
|
||||
|
||||
|
|
@ -1152,6 +1167,7 @@ func (n *NGINXController) isForceReload() bool {
|
|||
return atomic.LoadInt32(&n.forceReload) != 0
|
||||
}
|
||||
|
||||
// SetForceReload sets if the ingress controller should be reloaded or not
|
||||
func (n *NGINXController) SetForceReload(shouldReload bool) {
|
||||
if shouldReload {
|
||||
atomic.StoreInt32(&n.forceReload, 1)
|
||||
|
|
@ -1160,3 +1176,24 @@ func (n *NGINXController) SetForceReload(shouldReload bool) {
|
|||
atomic.StoreInt32(&n.forceReload, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NGINXController) extractAnnotations(ing *extensions.Ingress) {
|
||||
anns := n.annotations.Extract(ing)
|
||||
glog.V(3).Infof("updating annotations information for ingres %v/%v", anns.Namespace, anns.Name)
|
||||
n.listers.IngressAnnotation.Update(anns)
|
||||
}
|
||||
|
||||
// getByIngress returns the parsed annotations from an Ingress
|
||||
func (n *NGINXController) getIngressAnnotations(ing *extensions.Ingress) *annotations.Ingress {
|
||||
key := fmt.Sprintf("%v/%v", ing.Namespace, ing.Name)
|
||||
item, exists, err := n.listers.IngressAnnotation.GetByKey(key)
|
||||
if err != nil {
|
||||
glog.Errorf("unexpected error getting ingress annotation %v: %v", key, err)
|
||||
return &annotations.Ingress{}
|
||||
}
|
||||
if !exists {
|
||||
glog.Errorf("ingress annotation %v was not found", key)
|
||||
return &annotations.Ingress{}
|
||||
}
|
||||
return item.(*annotations.Ingress)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
cache_client "k8s.io/client-go/tools/cache"
|
||||
|
||||
"k8s.io/ingress-nginx/pkg/ingress"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
||||
|
|
@ -60,7 +61,7 @@ func (c *cacheController) Run(stopCh chan struct{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreLister {
|
||||
func (n *NGINXController) createListers(stopCh chan struct{}) (*ingress.StoreLister, *cacheController) {
|
||||
// from here to the end of the method all the code is just boilerplate
|
||||
// required to watch Ingress, Secrets, ConfigMaps and Endoints.
|
||||
// This is used to detect new content, updates or removals and act accordingly
|
||||
|
|
@ -73,6 +74,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
|||
return
|
||||
}
|
||||
|
||||
n.extractAnnotations(addIng)
|
||||
n.recorder.Eventf(addIng, apiv1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", addIng.Namespace, addIng.Name))
|
||||
n.syncQueue.Enqueue(obj)
|
||||
},
|
||||
|
|
@ -113,6 +115,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
|||
n.recorder.Eventf(curIng, apiv1.EventTypeNormal, "UPDATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
|
||||
}
|
||||
|
||||
n.extractAnnotations(curIng)
|
||||
n.syncQueue.Enqueue(cur)
|
||||
},
|
||||
}
|
||||
|
|
@ -141,7 +144,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
|||
}
|
||||
}
|
||||
key := fmt.Sprintf("%v/%v", sec.Namespace, sec.Name)
|
||||
n.sslCertTracker.DeleteAll(key)
|
||||
n.sslCertTracker.Delete(key)
|
||||
n.syncQueue.Enqueue(key)
|
||||
},
|
||||
}
|
||||
|
|
@ -196,6 +199,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
|||
}
|
||||
|
||||
lister := &ingress.StoreLister{}
|
||||
lister.IngressAnnotation.Store = cache_client.NewStore(cache_client.DeletionHandlingMetaNamespaceKeyFunc)
|
||||
|
||||
controller := &cacheController{}
|
||||
|
||||
|
|
@ -219,7 +223,5 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
|||
cache.NewListWatchFromClient(n.cfg.Client.CoreV1().RESTClient(), "services", n.cfg.Namespace, fields.Everything()),
|
||||
&apiv1.Service{}, n.cfg.ResyncPeriod, cache.ResourceEventHandlerFuncs{})
|
||||
|
||||
controller.Run(n.stopCh)
|
||||
|
||||
return lister
|
||||
return lister, controller
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/util/filesystem"
|
||||
|
||||
"k8s.io/ingress-nginx/pkg/ingress"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
||||
ngx_config "k8s.io/ingress-nginx/pkg/ingress/controller/config"
|
||||
|
|
@ -50,6 +51,7 @@ import (
|
|||
ngx_template "k8s.io/ingress-nginx/pkg/ingress/controller/template"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/defaults"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/status"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/store"
|
||||
ing_net "k8s.io/ingress-nginx/pkg/net"
|
||||
"k8s.io/ingress-nginx/pkg/net/dns"
|
||||
"k8s.io/ingress-nginx/pkg/net/ssl"
|
||||
|
|
@ -102,7 +104,7 @@ func NewNGINXController(config *Configuration) *NGINXController {
|
|||
|
||||
resolver: h,
|
||||
cfg: config,
|
||||
sslCertTracker: newSSLCertTracker(),
|
||||
sslCertTracker: store.NewSSLCertTracker(),
|
||||
syncRateLimiter: flowcontrol.NewTokenBucketRateLimiter(0.3, 1),
|
||||
|
||||
recorder: eventBroadcaster.NewRecorder(scheme.Scheme, apiv1.EventSource{
|
||||
|
|
@ -115,11 +117,13 @@ func NewNGINXController(config *Configuration) *NGINXController {
|
|||
fileSystem: filesystem.DefaultFs{},
|
||||
}
|
||||
|
||||
n.listers, n.controllers = n.createListers(n.stopCh)
|
||||
|
||||
n.stats = newStatsCollector(config.Namespace, config.IngressClass, n.binary, n.cfg.ListenPorts.Status)
|
||||
|
||||
n.syncQueue = task.NewTaskQueue(n.syncIngress)
|
||||
|
||||
n.listers = n.createListers(n.stopCh)
|
||||
n.annotations = annotations.NewAnnotationExtractor(n)
|
||||
|
||||
if config.UpdateStatus {
|
||||
n.syncStatus = status.NewStatusSyncer(status.Config{
|
||||
|
|
@ -135,7 +139,6 @@ func NewNGINXController(config *Configuration) *NGINXController {
|
|||
} else {
|
||||
glog.Warning("Update of ingress status is disabled (flag --update-status=false was specified)")
|
||||
}
|
||||
n.annotations = newAnnotationExtractor(n)
|
||||
|
||||
var onChange func()
|
||||
onChange = func() {
|
||||
|
|
@ -170,9 +173,10 @@ Error loading new template : %v
|
|||
type NGINXController struct {
|
||||
cfg *Configuration
|
||||
|
||||
listers *ingress.StoreLister
|
||||
listers *ingress.StoreLister
|
||||
controllers *cacheController
|
||||
|
||||
annotations annotationExtractor
|
||||
annotations annotations.Extractor
|
||||
|
||||
recorder record.EventRecorder
|
||||
|
||||
|
|
@ -182,7 +186,7 @@ type NGINXController struct {
|
|||
|
||||
// local store of SSL certificates
|
||||
// (only certificates used in ingress)
|
||||
sslCertTracker *sslCertTracker
|
||||
sslCertTracker *store.SSLCertTracker
|
||||
|
||||
syncRateLimiter flowcontrol.RateLimiter
|
||||
|
||||
|
|
@ -234,6 +238,8 @@ type NGINXController struct {
|
|||
func (n *NGINXController) Start() {
|
||||
glog.Infof("starting Ingress controller")
|
||||
|
||||
n.controllers.Run(n.stopCh)
|
||||
|
||||
// initial sync of secrets to avoid unnecessary reloads
|
||||
glog.Info("running initial sync of secrets")
|
||||
for _, obj := range n.listers.Ingress.List() {
|
||||
|
|
@ -425,12 +431,12 @@ func (n *NGINXController) SetConfig(cmap *apiv1.ConfigMap) {
|
|||
n.backendDefaults = c.Backend
|
||||
}
|
||||
|
||||
// OnUpdate is called by syncQueue in https://github.com/kubernetes/ingress-nginx/blob/master/pkg/ingress/controller/controller.go#L426
|
||||
// periodically to keep the configuration in sync.
|
||||
// OnUpdate is called periodically by syncQueue to keep the configuration in sync.
|
||||
//
|
||||
// 1. converts configmap configuration to custom configuration object
|
||||
// 2. write the custom template (the complexity depends on the implementation)
|
||||
// 3. write the configuration file
|
||||
//
|
||||
// convert configmap to custom configuration object (different in each implementation)
|
||||
// write the custom template (the complexity depends on the implementation)
|
||||
// write the configuration file
|
||||
// returning nill implies the backend will be reloaded.
|
||||
// if an error is returned means requeue the update
|
||||
func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
|
||||
|
|
|
|||
|
|
@ -354,8 +354,8 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string {
|
|||
}
|
||||
|
||||
// TODO: Needs Unit Tests
|
||||
func filterRateLimits(input interface{}) []ratelimit.RateLimit {
|
||||
ratelimits := []ratelimit.RateLimit{}
|
||||
func filterRateLimits(input interface{}) []ratelimit.Config {
|
||||
ratelimits := []ratelimit.Config{}
|
||||
found := sets.String{}
|
||||
|
||||
servers, ok := input.([]*ingress.Server)
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ func TestBuildLocation(t *testing.T) {
|
|||
for k, tc := range tmplFuncTestcases {
|
||||
loc := &ingress.Location{
|
||||
Path: tc.Path,
|
||||
Rewrite: rewrite.Redirect{Target: tc.Target, AddBaseURL: tc.AddBaseURL},
|
||||
Rewrite: rewrite.Config{Target: tc.Target, AddBaseURL: tc.AddBaseURL},
|
||||
}
|
||||
|
||||
newLoc := buildLocation(loc)
|
||||
|
|
@ -128,7 +128,7 @@ func TestBuildProxyPass(t *testing.T) {
|
|||
for k, tc := range tmplFuncTestcases {
|
||||
loc := &ingress.Location{
|
||||
Path: tc.Path,
|
||||
Rewrite: rewrite.Redirect{Target: tc.Target, AddBaseURL: tc.AddBaseURL, BaseURLScheme: tc.BaseURLScheme},
|
||||
Rewrite: rewrite.Config{Target: tc.Target, AddBaseURL: tc.AddBaseURL, BaseURLScheme: tc.BaseURLScheme},
|
||||
Backend: "upstream-name",
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ func TestBuildProxyPass(t *testing.T) {
|
|||
|
||||
func TestBuildAuthResponseHeaders(t *testing.T) {
|
||||
loc := &ingress.Location{
|
||||
ExternalAuth: authreq.External{ResponseHeaders: []string{"h1", "H-With-Caps-And-Dashes"}},
|
||||
ExternalAuth: authreq.Config{ResponseHeaders: []string{"h1", "H-With-Caps-And-Dashes"}},
|
||||
}
|
||||
headers := buildAuthResponseHeaders(loc)
|
||||
expected := []string{
|
||||
|
|
|
|||
|
|
@ -21,17 +21,12 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
|
||||
api "k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/util/sysctl"
|
||||
|
||||
"k8s.io/ingress-nginx/pkg/ingress"
|
||||
)
|
||||
|
||||
// DeniedKeyName name of the key that contains the reason to deny a location
|
||||
const DeniedKeyName = "Denied"
|
||||
|
||||
// newUpstream creates an upstream without servers.
|
||||
func newUpstream(name string) *ingress.Backend {
|
||||
return &ingress.Backend{
|
||||
|
|
@ -46,17 +41,6 @@ func newUpstream(name string) *ingress.Backend {
|
|||
}
|
||||
}
|
||||
|
||||
func mergeLocationAnnotations(loc *ingress.Location, anns map[string]interface{}) {
|
||||
if _, ok := anns[DeniedKeyName]; ok {
|
||||
loc.Denied = anns[DeniedKeyName].(error)
|
||||
}
|
||||
delete(anns, DeniedKeyName)
|
||||
err := mergo.Map(loc, anns)
|
||||
if err != nil {
|
||||
glog.Errorf("unexpected error merging extracted annotations in location type: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// sysctlSomaxconn returns the value of net.core.somaxconn, i.e.
|
||||
// maximum number of connections that can be queued for acceptance
|
||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
|
||||
|
|
|
|||
|
|
@ -17,18 +17,7 @@ limitations under the License.
|
|||
package controller
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/ingress-nginx/pkg/ingress"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/auth"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/authreq"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/cors"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/ipwhitelist"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/proxy"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/ratelimit"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/redirect"
|
||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/rewrite"
|
||||
)
|
||||
|
||||
type fakeError struct{}
|
||||
|
|
@ -37,51 +26,6 @@ func (fe *fakeError) Error() string {
|
|||
return "fakeError"
|
||||
}
|
||||
|
||||
func TestMergeLocationAnnotations(t *testing.T) {
|
||||
// initial parameters
|
||||
loc := ingress.Location{}
|
||||
annotations := map[string]interface{}{
|
||||
"Path": "/checkpath",
|
||||
"IsDefBackend": true,
|
||||
"Backend": "foo_backend",
|
||||
"BasicDigestAuth": auth.BasicDigest{},
|
||||
DeniedKeyName: &fakeError{},
|
||||
"CorsConfig": cors.CorsConfig{},
|
||||
"ExternalAuth": authreq.External{},
|
||||
"RateLimit": ratelimit.RateLimit{},
|
||||
"Redirect": redirect.Redirect{},
|
||||
"Rewrite": rewrite.Redirect{},
|
||||
"Whitelist": ipwhitelist.SourceRange{},
|
||||
"Proxy": proxy.Configuration{},
|
||||
"UsePortInRedirects": true,
|
||||
}
|
||||
|
||||
// create test table
|
||||
type fooMergeLocationAnnotationsStruct struct {
|
||||
fName string
|
||||
er interface{}
|
||||
}
|
||||
fooTests := []fooMergeLocationAnnotationsStruct{}
|
||||
for name, value := range annotations {
|
||||
fva := fooMergeLocationAnnotationsStruct{name, value}
|
||||
fooTests = append(fooTests, fva)
|
||||
}
|
||||
|
||||
// execute test
|
||||
mergeLocationAnnotations(&loc, annotations)
|
||||
|
||||
// check result
|
||||
for _, foo := range fooTests {
|
||||
fv := reflect.ValueOf(loc).FieldByName(foo.fName).Interface()
|
||||
if !reflect.DeepEqual(fv, foo.er) {
|
||||
t.Errorf("Returned %v but expected %v for the field %s", fv, foo.er, foo.fName)
|
||||
}
|
||||
}
|
||||
if _, ok := annotations[DeniedKeyName]; ok {
|
||||
t.Errorf("%s should be removed after mergeLocationAnnotations", DeniedKeyName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntInSlice(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
i int
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue