diff --git a/pkg/ingress/controller/annotations.go b/pkg/ingress/annotations/annotations.go similarity index 59% rename from pkg/ingress/controller/annotations.go rename to pkg/ingress/annotations/annotations.go index d251db4eb..d4aca392a 100644 --- a/pkg/ingress/controller/annotations.go +++ b/pkg/ingress/annotations/annotations.go @@ -14,12 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controller +package annotations import ( "github.com/golang/glog" + "github.com/imdario/mergo" extensions "k8s.io/api/extensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/ingress-nginx/pkg/ingress/annotations/alias" "k8s.io/ingress-nginx/pkg/ingress/annotations/auth" "k8s.io/ingress-nginx/pkg/ingress/annotations/authreq" @@ -48,51 +51,89 @@ import ( "k8s.io/ingress-nginx/pkg/ingress/resolver" ) -type extractorConfig interface { +// DeniedKeyName name of the key that contains the reason to deny a location +const DeniedKeyName = "Denied" + +type config interface { resolver.AuthCertificate resolver.DefaultBackend resolver.Secret resolver.Service } -type annotationExtractor struct { +// Ingress defines the valid annotations present in one NGINX Ingress rule +type Ingress struct { + metav1.ObjectMeta + Alias string + BasicDigestAuth auth.Config + CertificateAuth authtls.Config + ClientBodyBufferSize string + ConfigurationSnippet string + CorsConfig cors.Config + DefaultBackend string + ExternalAuth authreq.Config + HealthCheck healthcheck.Config + Proxy proxy.Config + RateLimit ratelimit.Config + Redirect redirect.Config + Rewrite rewrite.Config + SecureUpstream secureupstream.Config + ServerSnippet string + ServiceUpstream bool + SessionAffinity sessionaffinity.Config + SSLPassthrough bool + UsePortInRedirects bool + UpstreamHashBy string + UpstreamVhost string + VtsFilterKey string + Whitelist ipwhitelist.SourceRange +} + +// Extractor defines the annotation parsers to be used in the extraction of annotations +type Extractor struct { secretResolver resolver.Secret annotations map[string]parser.IngressAnnotation } -func newAnnotationExtractor(cfg extractorConfig) annotationExtractor { - return annotationExtractor{ +// NewAnnotationExtractor creates a new annotations extractor +func NewAnnotationExtractor(cfg config) Extractor { + return Extractor{ cfg, map[string]parser.IngressAnnotation{ + "Alias": alias.NewParser(), "BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg), - "ExternalAuth": authreq.NewParser(), "CertificateAuth": authtls.NewParser(cfg), + "ClientBodyBufferSize": clientbodybuffersize.NewParser(), + "ConfigurationSnippet": snippet.NewParser(), "CorsConfig": cors.NewParser(), + "DefaultBackend": defaultbackend.NewParser(cfg), + "ExternalAuth": authreq.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), + "ServerSnippet": serversnippet.NewParser(), "ServiceUpstream": serviceupstream.NewParser(), "SessionAffinity": sessionaffinity.NewParser(), "SSLPassthrough": sslpassthrough.NewParser(), - "ConfigurationSnippet": snippet.NewParser(), - "Alias": alias.NewParser(), - "ClientBodyBufferSize": clientbodybuffersize.NewParser(), - "DefaultBackend": defaultbackend.NewParser(cfg), + "UsePortInRedirects": portinredirect.NewParser(cfg), "UpstreamHashBy": upstreamhashby.NewParser(), "UpstreamVhost": upstreamvhost.NewParser(), "VtsFilterKey": vtsfilterkey.NewParser(), - "ServerSnippet": serversnippet.NewParser(), + "Whitelist": ipwhitelist.NewParser(cfg), }, } } -func (e *annotationExtractor) Extract(ing *extensions.Ingress) map[string]interface{} { - anns := make(map[string]interface{}) +// Extract extracts the annotations from an Ingress +func (e Extractor) Extract(ing *extensions.Ingress) *Ingress { + pia := &Ingress{ + ObjectMeta: ing.ObjectMeta, + } + + data := 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) @@ -105,9 +146,9 @@ func (e *annotationExtractor) Extract(ing *extensions.Ingress) map[string]interf continue } - _, alreadyDenied := anns[DeniedKeyName] + _, alreadyDenied := data[DeniedKeyName] if !alreadyDenied { - anns[DeniedKeyName] = err + data[DeniedKeyName] = err glog.Errorf("error reading %v annotation in Ingress %v/%v: %v", name, ing.GetNamespace(), ing.GetName(), err) continue } @@ -116,90 +157,14 @@ func (e *annotationExtractor) Extract(ing *extensions.Ingress) map[string]interf } if val != nil { - anns[name] = val + data[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) + err := mergo.Map(pia, data) 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 + glog.Errorf("unexpected error merging extracted annotations: %v", err) } - 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) + return pia } diff --git a/pkg/ingress/controller/annotations_test.go b/pkg/ingress/annotations/annotations_test.go similarity index 81% rename from pkg/ingress/controller/annotations_test.go rename to pkg/ingress/annotations/annotations_test.go index 025a526d5..55fde07ae 100644 --- a/pkg/ingress/controller/annotations_test.go +++ b/pkg/ingress/annotations/annotations_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controller +package annotations import ( "testing" @@ -75,20 +75,6 @@ func (m mockCfg) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) 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", @@ -125,7 +111,7 @@ func buildIngress() *extensions.Ingress { } func TestSecureUpstream(t *testing.T) { - ec := newAnnotationExtractor(mockCfg{}) + ec := NewAnnotationExtractor(mockCfg{}) ing := buildIngress() fooAnns := []struct { @@ -141,7 +127,7 @@ func TestSecureUpstream(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.SecureUpstream(ing) + r := ec.Extract(ing).SecureUpstream if r.Secure != foo.er { t.Errorf("Returned %v but expected %v", r, foo.er) } @@ -149,7 +135,7 @@ func TestSecureUpstream(t *testing.T) { } func TestSecureVerifyCACert(t *testing.T) { - ec := newAnnotationExtractor(mockCfg{ + ec := NewAnnotationExtractor(mockCfg{ MockSecrets: map[string]*apiv1.Secret{ "default/secure-verify-ca": { ObjectMeta: metav1.ObjectMeta{ @@ -176,15 +162,16 @@ func TestSecureVerifyCACert(t *testing.T) { for _, ann := range anns { ing := buildIngress() ing.SetAnnotations(ann.annotations) - res := ec.SecureUpstream(ing) - if (res.CACert.CAFileName != "") != ann.exists { + res := ec.Extract(ing).SecureUpstream + + if (res != nil && 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{}) + ec := NewAnnotationExtractor(mockCfg{}) ing := buildIngress() fooAnns := []struct { @@ -201,7 +188,7 @@ func TestHealthCheck(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.HealthCheck(ing) + r := ec.Extract(ing).HealthCheck if r == nil { t.Errorf("Returned nil but expected a healthcheck.Upstream") continue @@ -218,7 +205,7 @@ func TestHealthCheck(t *testing.T) { } func TestSSLPassthrough(t *testing.T) { - ec := newAnnotationExtractor(mockCfg{}) + ec := NewAnnotationExtractor(mockCfg{}) ing := buildIngress() fooAnns := []struct { @@ -234,7 +221,7 @@ func TestSSLPassthrough(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.SSLPassthrough(ing) + r := ec.Extract(ing).SSLPassthrough if r != foo.er { t.Errorf("Returned %v but expected %v", r, foo.er) } @@ -242,7 +229,7 @@ func TestSSLPassthrough(t *testing.T) { } func TestUpstreamHashBy(t *testing.T) { - ec := newAnnotationExtractor(mockCfg{}) + ec := NewAnnotationExtractor(mockCfg{}) ing := buildIngress() fooAnns := []struct { @@ -258,7 +245,7 @@ func TestUpstreamHashBy(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.UpstreamHashBy(ing) + r := ec.Extract(ing).UpstreamHashBy if r != foo.er { t.Errorf("Returned %v but expected %v", r, foo.er) } @@ -266,7 +253,7 @@ func TestUpstreamHashBy(t *testing.T) { } func TestAffinitySession(t *testing.T) { - ec := newAnnotationExtractor(mockCfg{}) + ec := NewAnnotationExtractor(mockCfg{}) ing := buildIngress() fooAnns := []struct { @@ -284,25 +271,25 @@ func TestAffinitySession(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.SessionAffinity(ing) + r := ec.Extract(ing).SessionAffinity 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.Cookie.Hash != foo.hash { + t.Errorf("Returned %v but expected %v for Hash", r.Cookie.Hash, foo.hash) } - if r.CookieConfig.Name != foo.name { - t.Errorf("Returned %v but expected %v for Name", r.CookieConfig.Name, foo.name) + if r.Cookie.Name != foo.name { + t.Errorf("Returned %v but expected %v for Name", r.Cookie.Name, foo.name) } } } func TestCors(t *testing.T) { - ec := newAnnotationExtractor(mockCfg{}) + ec := NewAnnotationExtractor(mockCfg{}) ing := buildIngress() fooAnns := []struct { @@ -322,7 +309,7 @@ func TestCors(t *testing.T) { for _, foo := range fooAnns { ing.SetAnnotations(foo.annotations) - r := ec.Cors(ing) + r := ec.Extract(ing).CorsConfig 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") @@ -351,3 +338,48 @@ func TestCors(t *testing.T) { } } + +/* +func TestMergeLocationAnnotations(t *testing.T) { + // initial parameters + keys := []string{"BasicDigestAuth", "CorsConfig", "ExternalAuth", "RateLimit", "Redirect", "Rewrite", "Whitelist", "Proxy", "UsePortInRedirects"} + + loc := ingress.Location{} + annotations := &Ingress{ + BasicDigestAuth: &auth.Config{}, + CorsConfig: &cors.Config{}, + ExternalAuth: &authreq.Config{}, + RateLimit: &ratelimit.Config{}, + Redirect: &redirect.Config{}, + Rewrite: &rewrite.Config{}, + Whitelist: &ipwhitelist.SourceRange{}, + Proxy: &proxy.Config{}, + UsePortInRedirects: true, + } + + // create test table + type fooMergeLocationAnnotationsStruct struct { + fName string + er interface{} + } + fooTests := []fooMergeLocationAnnotationsStruct{} + for name, value := range keys { + fva := fooMergeLocationAnnotationsStruct{name, value} + fooTests = append(fooTests, fva) + } + + // execute test + MergeWithLocation(&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) + } +} +*/ diff --git a/pkg/ingress/annotations/auth/main.go b/pkg/ingress/annotations/auth/main.go index 5830f0f71..1b28d81d4 100644 --- a/pkg/ingress/annotations/auth/main.go +++ b/pkg/ingress/annotations/auth/main.go @@ -46,8 +46,8 @@ var ( AuthDirectory = "/etc/ingress-controller/auth" ) -// BasicDigest returns authentication configuration for an Ingress rule -type BasicDigest struct { +// Config returns authentication configuration for an Ingress rule +type Config struct { Type string `json:"type"` Realm string `json:"realm"` File string `json:"file"` @@ -55,8 +55,8 @@ type BasicDigest struct { FileSHA string `json:"fileSha"` } -// Equal tests for equality between two BasicDigest types -func (bd1 *BasicDigest) Equal(bd2 *BasicDigest) bool { +// Equal tests for equality between two Config types +func (bd1 *Config) Equal(bd2 *Config) bool { if bd1 == bd2 { return true } @@ -140,7 +140,7 @@ func (a auth) Parse(ing *extensions.Ingress) (interface{}, error) { return nil, err } - return &BasicDigest{ + return &Config{ Type: at, Realm: realm, File: passFile, diff --git a/pkg/ingress/annotations/auth/main_test.go b/pkg/ingress/annotations/auth/main_test.go index df042ad4f..ffb421719 100644 --- a/pkg/ingress/annotations/auth/main_test.go +++ b/pkg/ingress/annotations/auth/main_test.go @@ -109,7 +109,7 @@ func TestIngressAuth(t *testing.T) { if err != nil { t.Errorf("Uxpected error with ingress: %v", err) } - auth, ok := i.(*BasicDigest) + auth, ok := i.(*Config) if !ok { t.Errorf("expected a BasicDigest type") } diff --git a/pkg/ingress/annotations/authreq/main.go b/pkg/ingress/annotations/authreq/main.go index 89ec8d8c1..28e884892 100644 --- a/pkg/ingress/annotations/authreq/main.go +++ b/pkg/ingress/annotations/authreq/main.go @@ -36,7 +36,7 @@ const ( ) // External returns external authentication configuration for an Ingress rule -type External struct { +type Config struct { URL string `json:"url"` // Host contains the hostname defined in the URL Host string `json:"host"` @@ -45,8 +45,8 @@ type External struct { ResponseHeaders []string `json:"responseHeaders,omitEmpty"` } -// Equal tests for equality between two External types -func (e1 *External) Equal(e2 *External) bool { +// Equal tests for equality between two Config types +func (e1 *Config) Equal(e2 *Config) bool { if e1 == e2 { return true } @@ -116,7 +116,7 @@ func NewParser() parser.IngressAnnotation { } // ParseAnnotations parses the annotations contained in the ingress -// rule used to use an external URL as source for authentication +// rule used to use an Config URL as source for authentication func (a authReq) Parse(ing *extensions.Ingress) (interface{}, error) { str, err := parser.GetStringAnnotation(authURL, ing) if err != nil { @@ -165,7 +165,7 @@ func (a authReq) Parse(ing *extensions.Ingress) (interface{}, error) { } } - return &External{ + return &Config{ URL: str, Host: ur.Hostname(), SigninURL: signin, diff --git a/pkg/ingress/annotations/authreq/main_test.go b/pkg/ingress/annotations/authreq/main_test.go index 1fc75fcbc..8256302a7 100644 --- a/pkg/ingress/annotations/authreq/main_test.go +++ b/pkg/ingress/annotations/authreq/main_test.go @@ -97,7 +97,7 @@ func TestAnnotations(t *testing.T) { } continue } - u, ok := i.(*External) + u, ok := i.(*Config) if !ok { t.Errorf("%v: expected an External type", test.title) } @@ -149,7 +149,7 @@ func TestHeaderAnnotations(t *testing.T) { } t.Log(i) - u, ok := i.(*External) + u, ok := i.(*Config) if !ok { t.Errorf("%v: expected an External type", test.title) continue diff --git a/pkg/ingress/annotations/authtls/main.go b/pkg/ingress/annotations/authtls/main.go index 7b2746435..73556bee2 100644 --- a/pkg/ingress/annotations/authtls/main.go +++ b/pkg/ingress/annotations/authtls/main.go @@ -20,11 +20,12 @@ import ( "github.com/pkg/errors" extensions "k8s.io/api/extensions/v1beta1" + "regexp" + "k8s.io/ingress-nginx/pkg/ingress/annotations/parser" ing_errors "k8s.io/ingress-nginx/pkg/ingress/errors" "k8s.io/ingress-nginx/pkg/ingress/resolver" "k8s.io/ingress-nginx/pkg/k8s" - "regexp" ) const ( @@ -41,17 +42,17 @@ var ( authVerifyClientRegex = regexp.MustCompile(`on|off|optional|optional_no_ca`) ) -// AuthSSLConfig contains the AuthSSLCert used for muthual autentication +// Config contains the AuthSSLCert used for muthual autentication // and the configured ValidationDepth -type AuthSSLConfig struct { +type Config struct { resolver.AuthSSLCert VerifyClient string `json:"verify_client"` ValidationDepth int `json:"validationDepth"` ErrorPage string `json:"errorPage"` } -// Equal tests for equality between two AuthSSLConfig types -func (assl1 *AuthSSLConfig) Equal(assl2 *AuthSSLConfig) bool { +// Equal tests for equality between two Config types +func (assl1 *Config) Equal(assl2 *Config) bool { if assl1 == assl2 { return true } @@ -88,16 +89,16 @@ func (a authTLS) Parse(ing *extensions.Ingress) (interface{}, error) { tlsauthsecret, err := parser.GetStringAnnotation(annotationAuthTLSSecret, ing) if err != nil { - return &AuthSSLConfig{}, err + return &Config{}, err } if tlsauthsecret == "" { - return &AuthSSLConfig{}, ing_errors.NewLocationDenied("an empty string is not a valid secret name") + return &Config{}, ing_errors.NewLocationDenied("an empty string is not a valid secret name") } _, _, err = k8s.ParseNameNS(tlsauthsecret) if err != nil { - return &AuthSSLConfig{}, ing_errors.NewLocationDenied(err.Error()) + return &Config{}, ing_errors.NewLocationDenied(err.Error()) } tlsVerifyClient, err := parser.GetStringAnnotation(annotationAuthVerifyClient, ing) @@ -112,7 +113,7 @@ func (a authTLS) Parse(ing *extensions.Ingress) (interface{}, error) { authCert, err := a.certResolver.GetAuthCertificate(tlsauthsecret) if err != nil { - return &AuthSSLConfig{}, ing_errors.LocationDenied{ + return &Config{}, ing_errors.LocationDenied{ Reason: errors.Wrap(err, "error obtaining certificate"), } } @@ -122,7 +123,7 @@ func (a authTLS) Parse(ing *extensions.Ingress) (interface{}, error) { errorpage = "" } - return &AuthSSLConfig{ + return &Config{ AuthSSLCert: *authCert, VerifyClient: tlsVerifyClient, ValidationDepth: tlsdepth, diff --git a/pkg/ingress/annotations/cors/main.go b/pkg/ingress/annotations/cors/main.go index 152174cb6..f382606fa 100644 --- a/pkg/ingress/annotations/cors/main.go +++ b/pkg/ingress/annotations/cors/main.go @@ -51,8 +51,8 @@ var ( type cors struct { } -// CorsConfig contains the Cors configuration to be used in the Ingress -type CorsConfig struct { +// Config contains the Cors configuration to be used in the Ingress +type Config struct { CorsEnabled bool `json:"corsEnabled"` CorsAllowOrigin string `json:"corsAllowOrigin"` CorsAllowMethods string `json:"corsAllowMethods"` @@ -66,7 +66,7 @@ func NewParser() parser.IngressAnnotation { } // Equal tests for equality between two External types -func (c1 *CorsConfig) Equal(c2 *CorsConfig) bool { +func (c1 *Config) Equal(c2 *Config) bool { if c1 == c2 { return true } @@ -120,7 +120,7 @@ func (a cors) Parse(ing *extensions.Ingress) (interface{}, error) { corsallowcredentials = true } - return &CorsConfig{ + return &Config{ CorsEnabled: corsenabled, CorsAllowOrigin: corsalloworigin, CorsAllowHeaders: corsallowheaders, diff --git a/pkg/ingress/annotations/cors/main_test.go b/pkg/ingress/annotations/cors/main_test.go index 11a022731..101e26cbc 100644 --- a/pkg/ingress/annotations/cors/main_test.go +++ b/pkg/ingress/annotations/cors/main_test.go @@ -72,7 +72,7 @@ func TestIngressCorsConfig(t *testing.T) { ing.SetAnnotations(data) corst, _ := NewParser().Parse(ing) - nginxCors, ok := corst.(*CorsConfig) + nginxCors, ok := corst.(*Config) if !ok { t.Errorf("expected a Config type") } diff --git a/pkg/ingress/annotations/healthcheck/main.go b/pkg/ingress/annotations/healthcheck/main.go index 5d3850d90..8b29838be 100644 --- a/pkg/ingress/annotations/healthcheck/main.go +++ b/pkg/ingress/annotations/healthcheck/main.go @@ -28,9 +28,9 @@ const ( upsFailTimeout = "ingress.kubernetes.io/upstream-fail-timeout" ) -// Upstream returns the URL and method to use check the status of +// Config returns the URL and method to use check the status of // the upstream server/s -type Upstream struct { +type Config struct { MaxFails int `json:"maxFails"` FailTimeout int `json:"failTimeout"` } @@ -49,7 +49,7 @@ func NewParser(br resolver.DefaultBackend) parser.IngressAnnotation { func (a healthCheck) Parse(ing *extensions.Ingress) (interface{}, error) { defBackend := a.backendResolver.GetDefaultBackend() if ing.GetAnnotations() == nil { - return &Upstream{defBackend.UpstreamMaxFails, defBackend.UpstreamFailTimeout}, nil + return &Config{defBackend.UpstreamMaxFails, defBackend.UpstreamFailTimeout}, nil } mf, err := parser.GetIntAnnotation(upsMaxFails, ing) @@ -62,5 +62,5 @@ func (a healthCheck) Parse(ing *extensions.Ingress) (interface{}, error) { ft = defBackend.UpstreamFailTimeout } - return &Upstream{mf, ft}, nil + return &Config{mf, ft}, nil } diff --git a/pkg/ingress/annotations/healthcheck/main_test.go b/pkg/ingress/annotations/healthcheck/main_test.go index afcc9540d..d32dc8de2 100644 --- a/pkg/ingress/annotations/healthcheck/main_test.go +++ b/pkg/ingress/annotations/healthcheck/main_test.go @@ -77,7 +77,7 @@ func TestIngressHealthCheck(t *testing.T) { ing.SetAnnotations(data) hzi, _ := NewParser(mockBackend{}).Parse(ing) - nginxHz, ok := hzi.(*Upstream) + nginxHz, ok := hzi.(*Config) if !ok { t.Errorf("expected a Upstream type") } diff --git a/pkg/ingress/annotations/portinredirect/main_test.go b/pkg/ingress/annotations/portinredirect/main_test.go index 44b0444e9..802e8b50a 100644 --- a/pkg/ingress/annotations/portinredirect/main_test.go +++ b/pkg/ingress/annotations/portinredirect/main_test.go @@ -17,6 +17,7 @@ limitations under the License. package portinredirect import ( + "fmt" "testing" api "k8s.io/api/core/v1" @@ -24,8 +25,6 @@ import ( meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" - "fmt" - "k8s.io/ingress-nginx/pkg/ingress/defaults" ) diff --git a/pkg/ingress/annotations/proxy/main.go b/pkg/ingress/annotations/proxy/main.go index b35696b97..ee8360d97 100644 --- a/pkg/ingress/annotations/proxy/main.go +++ b/pkg/ingress/annotations/proxy/main.go @@ -36,8 +36,8 @@ const ( requestBuffering = "ingress.kubernetes.io/proxy-request-buffering" ) -// Configuration returns the proxy timeout to use in the upstream server/s -type Configuration struct { +// Config returns the proxy timeout to use in the upstream server/s +type Config struct { BodySize string `json:"bodySize"` ConnectTimeout int `json:"connectTimeout"` SendTimeout int `json:"sendTimeout"` @@ -51,7 +51,7 @@ type Configuration struct { } // Equal tests for equality between two Configuration types -func (l1 *Configuration) Equal(l2 *Configuration) bool { +func (l1 *Config) Equal(l2 *Config) bool { if l1 == l2 { return true } @@ -156,5 +156,5 @@ func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) { rb = defBackend.ProxyRequestBuffering } - return &Configuration{bs, ct, st, rt, bufs, cd, cp, nu, pp, rb}, nil + return &Config{bs, ct, st, rt, bufs, cd, cp, nu, pp, rb}, nil } diff --git a/pkg/ingress/annotations/proxy/main_test.go b/pkg/ingress/annotations/proxy/main_test.go index 04ffbda53..8fa08ed34 100644 --- a/pkg/ingress/annotations/proxy/main_test.go +++ b/pkg/ingress/annotations/proxy/main_test.go @@ -97,9 +97,9 @@ func TestProxy(t *testing.T) { if err != nil { t.Fatalf("unexpected error parsing a valid") } - p, ok := i.(*Configuration) + p, ok := i.(*Config) if !ok { - t.Fatalf("expected a Configuration type") + t.Fatalf("expected a Config type") } if p.ConnectTimeout != 1 { t.Errorf("expected 1 as connect-timeout but returned %v", p.ConnectTimeout) @@ -137,9 +137,9 @@ func TestProxyWithNoAnnotation(t *testing.T) { if err != nil { t.Fatalf("unexpected error parsing a valid") } - p, ok := i.(*Configuration) + p, ok := i.(*Config) if !ok { - t.Fatalf("expected a Configuration type") + t.Fatalf("expected a Config type") } if p.ConnectTimeout != 10 { t.Errorf("expected 10 as connect-timeout but returned %v", p.ConnectTimeout) diff --git a/pkg/ingress/annotations/ratelimit/main.go b/pkg/ingress/annotations/ratelimit/main.go index 651b71859..6118e8a04 100644 --- a/pkg/ingress/annotations/ratelimit/main.go +++ b/pkg/ingress/annotations/ratelimit/main.go @@ -45,11 +45,11 @@ const ( defSharedSize = 5 ) -// RateLimit returns rate limit configuration for an Ingress rule limiting the +// Config returns rate limit configuration for an Ingress rule limiting the // number of connections per IP address and/or connections per second. // If you both annotations are specified in a single Ingress rule, RPS limits // takes precedence -type RateLimit struct { +type Config struct { // Connections indicates a limit with the number of connections per IP address Connections Zone `json:"connections"` // RPS indicates a limit with the number of connections per second @@ -69,7 +69,7 @@ type RateLimit struct { } // Equal tests for equality between two RateLimit types -func (rt1 *RateLimit) Equal(rt2 *RateLimit) bool { +func (rt1 *Config) Equal(rt2 *Config) bool { if rt1 == rt2 { return true } @@ -185,7 +185,7 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) { } if rpm == 0 && rps == 0 && conn == 0 { - return &RateLimit{ + return &Config{ Connections: Zone{}, RPS: Zone{}, RPM: Zone{}, @@ -196,7 +196,7 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) { zoneName := fmt.Sprintf("%v_%v", ing.GetNamespace(), ing.GetName()) - return &RateLimit{ + return &Config{ Connections: Zone{ Name: fmt.Sprintf("%v_conn", zoneName), Limit: conn, diff --git a/pkg/ingress/annotations/ratelimit/main_test.go b/pkg/ingress/annotations/ratelimit/main_test.go index 9b079f293..bf21e30dc 100644 --- a/pkg/ingress/annotations/ratelimit/main_test.go +++ b/pkg/ingress/annotations/ratelimit/main_test.go @@ -107,7 +107,7 @@ func TestBadRateLimiting(t *testing.T) { if err != nil { t.Errorf("unexpected error: %v", err) } - rateLimit, ok := i.(*RateLimit) + rateLimit, ok := i.(*Config) if !ok { t.Errorf("expected a RateLimit type") } diff --git a/pkg/ingress/annotations/redirect/redirect.go b/pkg/ingress/annotations/redirect/redirect.go index 41074ca7c..d5f46fd4e 100644 --- a/pkg/ingress/annotations/redirect/redirect.go +++ b/pkg/ingress/annotations/redirect/redirect.go @@ -33,8 +33,8 @@ const ( www = "ingress.kubernetes.io/from-to-www-redirect" ) -// Redirect returns the redirect configuration for an Ingress rule -type Redirect struct { +// Config returns the redirect configuration for an Ingress rule +type Config struct { URL string `json:"url"` Code int `json:"code"` FromToWWW bool `json:"fromToWWW"` @@ -64,7 +64,7 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) { return nil, err } - return &Redirect{ + return &Config{ URL: tr, Code: http.StatusFound, FromToWWW: r3w, @@ -81,7 +81,7 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) { return nil, err } - return &Redirect{ + return &Config{ URL: pr, Code: http.StatusMovedPermanently, FromToWWW: r3w, @@ -89,7 +89,7 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) { } if r3w { - return &Redirect{ + return &Config{ FromToWWW: r3w, }, nil } @@ -98,7 +98,7 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) { } // Equal tests for equality between two Redirect types -func (r1 *Redirect) Equal(r2 *Redirect) bool { +func (r1 *Config) Equal(r2 *Config) bool { if r1 == r2 { return true } diff --git a/pkg/ingress/annotations/rewrite/main.go b/pkg/ingress/annotations/rewrite/main.go index ce97f657d..d396ec8c2 100644 --- a/pkg/ingress/annotations/rewrite/main.go +++ b/pkg/ingress/annotations/rewrite/main.go @@ -32,8 +32,8 @@ const ( appRoot = "ingress.kubernetes.io/app-root" ) -// Redirect describes the per location redirect config -type Redirect struct { +// Config describes the per location redirect config +type Config struct { // Target URI where the traffic must be redirected Target string `json:"target"` // AddBaseURL indicates if is required to add a base tag in the head @@ -50,7 +50,7 @@ type Redirect struct { } // Equal tests for equality between two Redirect types -func (r1 *Redirect) Equal(r2 *Redirect) bool { +func (r1 *Config) Equal(r2 *Config) bool { if r1 == r2 { return true } @@ -103,7 +103,7 @@ func (a rewrite) Parse(ing *extensions.Ingress) (interface{}, error) { abu, _ := parser.GetBoolAnnotation(addBaseURL, ing) bus, _ := parser.GetStringAnnotation(baseURLScheme, ing) ar, _ := parser.GetStringAnnotation(appRoot, ing) - return &Redirect{ + return &Config{ Target: rt, AddBaseURL: abu, BaseURLScheme: bus, diff --git a/pkg/ingress/annotations/rewrite/main_test.go b/pkg/ingress/annotations/rewrite/main_test.go index 5da0cfeee..3ad61f6cf 100644 --- a/pkg/ingress/annotations/rewrite/main_test.go +++ b/pkg/ingress/annotations/rewrite/main_test.go @@ -93,7 +93,7 @@ func TestRedirect(t *testing.T) { if err != nil { t.Errorf("Unexpected error with ingress: %v", err) } - redirect, ok := i.(*Redirect) + redirect, ok := i.(*Config) if !ok { t.Errorf("expected a Redirect type") } @@ -110,7 +110,7 @@ func TestSSLRedirect(t *testing.T) { ing.SetAnnotations(data) i, _ := NewParser(mockBackend{true}).Parse(ing) - redirect, ok := i.(*Redirect) + redirect, ok := i.(*Config) if !ok { t.Errorf("expected a Redirect type") } @@ -122,7 +122,7 @@ func TestSSLRedirect(t *testing.T) { ing.SetAnnotations(data) i, _ = NewParser(mockBackend{false}).Parse(ing) - redirect, ok = i.(*Redirect) + redirect, ok = i.(*Config) if !ok { t.Errorf("expected a Redirect type") } @@ -139,7 +139,7 @@ func TestForceSSLRedirect(t *testing.T) { ing.SetAnnotations(data) i, _ := NewParser(mockBackend{true}).Parse(ing) - redirect, ok := i.(*Redirect) + redirect, ok := i.(*Config) if !ok { t.Errorf("expected a Redirect type") } @@ -151,7 +151,7 @@ func TestForceSSLRedirect(t *testing.T) { ing.SetAnnotations(data) i, _ = NewParser(mockBackend{false}).Parse(ing) - redirect, ok = i.(*Redirect) + redirect, ok = i.(*Config) if !ok { t.Errorf("expected a Redirect type") } @@ -167,12 +167,11 @@ func TestAppRoot(t *testing.T) { ing.SetAnnotations(data) i, _ := NewParser(mockBackend{true}).Parse(ing) - redirect, ok := i.(*Redirect) + redirect, ok := i.(*Config) if !ok { t.Errorf("expected a App Context") } if redirect.AppRoot != "/app1" { t.Errorf("Unexpected value got in AppRoot") } - } diff --git a/pkg/ingress/annotations/secureupstream/main.go b/pkg/ingress/annotations/secureupstream/main.go index 119041cac..60b24eb95 100644 --- a/pkg/ingress/annotations/secureupstream/main.go +++ b/pkg/ingress/annotations/secureupstream/main.go @@ -31,8 +31,8 @@ const ( secureVerifyCASecret = "ingress.kubernetes.io/secure-verify-ca-secret" ) -// Secure describes SSL backend configuration -type Secure struct { +// Config describes SSL backend configuration +type Config struct { Secure bool `json:"secure"` CACert resolver.AuthSSLCert `json:"caCert"` } @@ -53,7 +53,7 @@ func NewParser(resolver resolver.AuthCertificate) parser.IngressAnnotation { func (a su) Parse(ing *extensions.Ingress) (interface{}, error) { s, _ := parser.GetBoolAnnotation(secureUpstream, ing) ca, _ := parser.GetStringAnnotation(secureVerifyCASecret, ing) - secure := &Secure{ + secure := &Config{ Secure: s, CACert: resolver.AuthSSLCert{}, } @@ -71,7 +71,7 @@ func (a su) Parse(ing *extensions.Ingress) (interface{}, error) { if caCert == nil { return secure, nil } - return &Secure{ + return &Config{ Secure: s, CACert: *caCert, }, nil diff --git a/pkg/ingress/annotations/secureupstream/main_test.go b/pkg/ingress/annotations/secureupstream/main_test.go index 27e4f22d7..35225285e 100644 --- a/pkg/ingress/annotations/secureupstream/main_test.go +++ b/pkg/ingress/annotations/secureupstream/main_test.go @@ -17,14 +17,13 @@ limitations under the License. package secureupstream import ( + "fmt" "testing" api "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "fmt" - "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/ingress-nginx/pkg/ingress/resolver" ) diff --git a/pkg/ingress/annotations/sessionaffinity/main.go b/pkg/ingress/annotations/sessionaffinity/main.go index c0b274b09..87a2e6f28 100644 --- a/pkg/ingress/annotations/sessionaffinity/main.go +++ b/pkg/ingress/annotations/sessionaffinity/main.go @@ -42,24 +42,24 @@ var ( affinityCookieHashRegex = regexp.MustCompile(`^(index|md5|sha1)$`) ) -// AffinityConfig describes the per ingress session affinity config -type AffinityConfig struct { +// Config describes the per ingress session affinity config +type Config struct { // The type of affinity that will be used - AffinityType string `json:"type"` - CookieConfig + Type string `json:"type"` + Cookie } -// CookieConfig describes the Config of cookie type affinity -type CookieConfig struct { +// Cookie describes the Config of cookie type affinity +type Cookie struct { // The name of the cookie that will be used in case of cookie affinity type. Name string `json:"name"` // The hash that will be used to encode the cookie in case of cookie affinity type Hash string `json:"hash"` } -// 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 -func CookieAffinityParse(ing *extensions.Ingress) *CookieConfig { +func cookieAffinityParse(ing *extensions.Ingress) *Cookie { sn, err := parser.GetStringAnnotation(annotationAffinityCookieName, ing) @@ -75,7 +75,7 @@ func CookieAffinityParse(ing *extensions.Ingress) *CookieConfig { sh = defaultAffinityCookieHash } - return &CookieConfig{ + return &Cookie{ Name: sn, Hash: sh, } @@ -92,7 +92,7 @@ type affinity struct { // ParseAnnotations parses the annotations contained in the ingress // rule used to configure the affinity directives func (a affinity) Parse(ing *extensions.Ingress) (interface{}, error) { - cookieAffinityConfig := &CookieConfig{} + cookie := &Cookie{} // Check the type of affinity that will be used at, err := parser.GetStringAnnotation(annotationAffinityType, ing) if err != nil { @@ -101,15 +101,14 @@ func (a affinity) Parse(ing *extensions.Ingress) (interface{}, error) { switch at { case "cookie": - cookieAffinityConfig = CookieAffinityParse(ing) - + cookie = cookieAffinityParse(ing) default: glog.V(3).Infof("No default affinity was found for Ingress %v", ing.Name) } - return &AffinityConfig{ - AffinityType: at, - CookieConfig: *cookieAffinityConfig, - }, nil + return &Config{ + Type: at, + Cookie: *cookie, + }, nil } diff --git a/pkg/ingress/annotations/sessionaffinity/main_test.go b/pkg/ingress/annotations/sessionaffinity/main_test.go index 5008c6b1e..625019827 100644 --- a/pkg/ingress/annotations/sessionaffinity/main_test.go +++ b/pkg/ingress/annotations/sessionaffinity/main_test.go @@ -70,20 +70,20 @@ func TestIngressAffinityCookieConfig(t *testing.T) { ing.SetAnnotations(data) affin, _ := NewParser().Parse(ing) - nginxAffinity, ok := affin.(*AffinityConfig) + nginxAffinity, ok := affin.(*Config) if !ok { t.Errorf("expected a Config type") } - if nginxAffinity.AffinityType != "cookie" { - t.Errorf("expected cookie as sticky-type but returned %v", nginxAffinity.AffinityType) + if nginxAffinity.Type != "cookie" { + t.Errorf("expected cookie as sticky-type but returned %v", nginxAffinity.Type) } - if nginxAffinity.CookieConfig.Hash != "md5" { - t.Errorf("expected md5 as sticky-hash but returned %v", nginxAffinity.CookieConfig.Hash) + if nginxAffinity.Cookie.Hash != "md5" { + t.Errorf("expected md5 as sticky-hash but returned %v", nginxAffinity.Cookie.Hash) } - if nginxAffinity.CookieConfig.Name != "INGRESSCOOKIE" { - t.Errorf("expected route as sticky-name but returned %v", nginxAffinity.CookieConfig.Name) + if nginxAffinity.Cookie.Name != "INGRESSCOOKIE" { + t.Errorf("expected route as sticky-name but returned %v", nginxAffinity.Cookie.Name) } } diff --git a/pkg/ingress/controller/backend_ssl.go b/pkg/ingress/controller/backend_ssl.go index fd88caa23..09affd0ff 100644 --- a/pkg/ingress/controller/backend_ssl.go +++ b/pkg/ingress/controller/backend_ssl.go @@ -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) -} diff --git a/pkg/ingress/controller/controller.go b/pkg/ingress/controller/controller.go index dc1c0d1c0..0c46c85b0 100644 --- a/pkg/ingress/controller/controller.go +++ b/pkg/ingress/controller/controller.go @@ -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) +} diff --git a/pkg/ingress/controller/listers.go b/pkg/ingress/controller/listers.go index 6ccc6d318..766a19640 100644 --- a/pkg/ingress/controller/listers.go +++ b/pkg/ingress/controller/listers.go @@ -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 } diff --git a/pkg/ingress/controller/nginx.go b/pkg/ingress/controller/nginx.go index 7428c53dc..8b1af612d 100644 --- a/pkg/ingress/controller/nginx.go +++ b/pkg/ingress/controller/nginx.go @@ -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 { diff --git a/pkg/ingress/controller/template/template.go b/pkg/ingress/controller/template/template.go index ef640242c..8a8e8463a 100644 --- a/pkg/ingress/controller/template/template.go +++ b/pkg/ingress/controller/template/template.go @@ -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) diff --git a/pkg/ingress/controller/template/template_test.go b/pkg/ingress/controller/template/template_test.go index 3d0b99991..ada3f1ec3 100644 --- a/pkg/ingress/controller/template/template_test.go +++ b/pkg/ingress/controller/template/template_test.go @@ -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{ diff --git a/pkg/ingress/controller/util.go b/pkg/ingress/controller/util.go index c527ceaeb..0961c61cc 100644 --- a/pkg/ingress/controller/util.go +++ b/pkg/ingress/controller/util.go @@ -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 diff --git a/pkg/ingress/controller/util_test.go b/pkg/ingress/controller/util_test.go index be3e382f6..dc02bf0dc 100644 --- a/pkg/ingress/controller/util_test.go +++ b/pkg/ingress/controller/util_test.go @@ -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 diff --git a/pkg/ingress/store/main.go b/pkg/ingress/store/main.go index 166fd2ada..299f54c0b 100644 --- a/pkg/ingress/store/main.go +++ b/pkg/ingress/store/main.go @@ -28,6 +28,11 @@ type IngressLister struct { cache.Store } +// IngressAnnotationsLister makes a Store that lists annotations in Ingress rules. +type IngressAnnotationsLister struct { + cache.Store +} + // SecretLister makes a Store that lists Secrets. type SecretLister struct { cache.Store @@ -94,3 +99,15 @@ func (s *EndpointLister) GetServiceEndpoints(svc *apiv1.Service) (*apiv1.Endpoin } return nil, fmt.Errorf("could not find endpoints for service: %v", svc.Name) } + +// SSLCertTracker holds a store of referenced Secrets in Ingress rules +type SSLCertTracker struct { + cache.ThreadSafeStore +} + +// NewSSLCertTracker creates a new SSLCertTracker store +func NewSSLCertTracker() *SSLCertTracker { + return &SSLCertTracker{ + cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}), + } +} diff --git a/pkg/ingress/types.go b/pkg/ingress/types.go index 73e53fa64..4622860a5 100644 --- a/pkg/ingress/types.go +++ b/pkg/ingress/types.go @@ -47,11 +47,12 @@ var ( // StoreLister returns the configured stores for ingresses, services, // endpoints, secrets and configmaps. type StoreLister struct { - Ingress store.IngressLister - Service store.ServiceLister - Endpoint store.EndpointLister - Secret store.SecretLister - ConfigMap store.ConfigMapLister + Ingress store.IngressLister + Service store.ServiceLister + Endpoint store.EndpointLister + Secret store.SecretLister + ConfigMap store.ConfigMapLister + IngressAnnotation store.IngressAnnotationsLister } // Configuration holds the definition of all the parts required to describe all @@ -165,7 +166,7 @@ type Server struct { RedirectFromToWWW bool `json:"redirectFromToWWW,omitempty"` // CertificateAuth indicates the this server requires mutual authentication // +optional - CertificateAuth authtls.AuthSSLConfig `json:"certificateAuth"` + CertificateAuth authtls.Config `json:"certificateAuth"` // ServerSnippet returns the snippet of server // +optional @@ -211,28 +212,28 @@ type Location struct { // BasicDigestAuth returns authentication configuration for // an Ingress rule. // +optional - BasicDigestAuth auth.BasicDigest `json:"basicDigestAuth,omitempty"` + BasicDigestAuth auth.Config `json:"basicDigestAuth,omitempty"` // Denied returns an error when this location cannot not be allowed // Requesting a denied location should return HTTP code 403. Denied error `json:"denied,omitempty"` // CorsConfig returns the Cors Configration for the ingress rule // +optional - CorsConfig cors.CorsConfig `json:"corsConfig,omitempty"` + CorsConfig cors.Config `json:"corsConfig,omitempty"` // ExternalAuth indicates the access to this location requires // authentication using an external provider // +optional - ExternalAuth authreq.External `json:"externalAuth,omitempty"` + ExternalAuth authreq.Config `json:"externalAuth,omitempty"` // RateLimit describes a limit in the number of connections per IP // address or connections per second. // The Redirect annotation precedes RateLimit // +optional - RateLimit ratelimit.RateLimit `json:"rateLimit,omitempty"` + RateLimit ratelimit.Config `json:"rateLimit,omitempty"` // Redirect describes a temporal o permanent redirection this location. // +optional - Redirect redirect.Redirect `json:"redirect,omitempty"` + Redirect redirect.Config `json:"redirect,omitempty"` // Rewrite describes the redirection this location. // +optional - Rewrite rewrite.Redirect `json:"rewrite,omitempty"` + Rewrite rewrite.Config `json:"rewrite,omitempty"` // Whitelist indicates only connections from certain client // addresses or networks are allowed. // +optional @@ -240,7 +241,7 @@ type Location struct { // Proxy contains information about timeouts and buffer sizes // to be used in connections against endpoints // +optional - Proxy proxy.Configuration `json:"proxy,omitempty"` + Proxy proxy.Config `json:"proxy,omitempty"` // UsePortInRedirects indicates if redirects must specify the port // +optional UsePortInRedirects bool `json:"usePortInRedirects"`