This commit is contained in:
Manuel Alejandro de Brito Fontes 2017-11-19 19:10:31 +00:00 committed by GitHub
commit 01e7d43f13
72 changed files with 1896 additions and 801 deletions

View file

@ -34,6 +34,9 @@ jobs:
- go get github.com/golang/lint/golint - go get github.com/golang/lint/golint
- make fmt lint vet - make fmt lint vet
- stage: Coverage - stage: Coverage
before_script:
# start minikube
- test/e2e/up.sh
script: script:
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
- go get github.com/modocache/gover - go get github.com/modocache/gover
@ -44,6 +47,7 @@ jobs:
before_script: before_script:
- make e2e-image - make e2e-image
- test/e2e/up.sh - test/e2e/up.sh
- test/e2e/wait-for-nginx.sh
script: script:
- make e2e-test - make e2e-test
# split builds to avoid job timeouts # split builds to avoid job timeouts

View file

@ -27,15 +27,13 @@ import (
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/controller" "k8s.io/ingress-nginx/internal/ingress/controller"
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config" ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
ing_net "k8s.io/ingress-nginx/internal/net" ing_net "k8s.io/ingress-nginx/internal/net"
) )
const (
defIngressClass = "nginx"
)
func parseFlags() (bool, *controller.Configuration, error) { func parseFlags() (bool, *controller.Configuration, error) {
var ( var (
flags = pflag.NewFlagSet("", pflag.ExitOnError) flags = pflag.NewFlagSet("", pflag.ExitOnError)
@ -152,7 +150,7 @@ func parseFlags() (bool, *controller.Configuration, error) {
if *ingressClass != "" { if *ingressClass != "" {
glog.Infof("Watching for ingress class: %s", *ingressClass) glog.Infof("Watching for ingress class: %s", *ingressClass)
if *ingressClass != defIngressClass { if *ingressClass != class.IngressClass {
glog.Warningf("only Ingress with class \"%v\" will be processed by this ingress controller", *ingressClass) glog.Warningf("only Ingress with class \"%v\" will be processed by this ingress controller", *ingressClass)
} }
} }
@ -187,8 +185,10 @@ func parseFlags() (bool, *controller.Configuration, error) {
glog.Warningf("Check of SSL certificate chain is disabled (--enable-ssl-chain-completion=false)") glog.Warningf("Check of SSL certificate chain is disabled (--enable-ssl-chain-completion=false)")
} }
parser.AnnotationsPrefix = *annotationsPrefix
class.IngressClass = *ingressClass
config := &controller.Configuration{ config := &controller.Configuration{
AnnotationsPrefix: *annotationsPrefix,
APIServerHost: *apiserverHost, APIServerHost: *apiserverHost,
KubeConfigFile: *kubeConfigFile, KubeConfigFile: *kubeConfigFile,
UpdateStatus: *updateStatus, UpdateStatus: *updateStatus,
@ -198,7 +198,6 @@ func parseFlags() (bool, *controller.Configuration, error) {
EnableSSLChainCompletion: *enableSSLChainCompletion, EnableSSLChainCompletion: *enableSSLChainCompletion,
ResyncPeriod: *resyncPeriod, ResyncPeriod: *resyncPeriod,
DefaultService: *defaultSvc, DefaultService: *defaultSvc,
IngressClass: *ingressClass,
Namespace: *watchNamespace, Namespace: *watchNamespace,
ConfigMapName: *configMap, ConfigMapName: *configMap,
TCPConfigMapName: *tcpConfigMapName, TCPConfigMapName: *tcpConfigMapName,

View file

@ -128,7 +128,6 @@ func main() {
conf.FakeCertificateSHA = c.PemSHA conf.FakeCertificateSHA = c.PemSHA
conf.Client = kubeClient conf.Client = kubeClient
conf.DefaultIngressClass = defIngressClass
ngx := controller.NewNGINXController(conf) ngx := controller.NewNGINXController(conf)

View file

@ -24,16 +24,15 @@ import (
) )
type alias struct { type alias struct {
r resolver.Resolver
} }
// NewParser creates a new Alias annotation parser // NewParser creates a new Alias annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return alias{r} return alias{}
} }
// Parse parses the annotations contained in the ingress rule // Parse parses the annotations contained in the ingress rule
// used to add an alias to the provided hosts // used to add an alias to the provided hosts
func (a alias) Parse(ing *extensions.Ingress) (interface{}, error) { func (a alias) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation("server-alias", ing, a.r) return parser.GetStringAnnotation("server-alias", ing)
} }

View file

@ -22,10 +22,11 @@ import (
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const annotation = "nginx/server-alias" var annotation = parser.GetAnnotationWithPrefix("server-alias")
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
ap := NewParser(&resolver.Mock{}) ap := NewParser(&resolver.Mock{})

View file

@ -95,25 +95,25 @@ func NewAnnotationExtractor(cfg resolver.Resolver) Extractor {
"Alias": alias.NewParser(cfg), "Alias": alias.NewParser(cfg),
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg), "BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
"CertificateAuth": authtls.NewParser(cfg), "CertificateAuth": authtls.NewParser(cfg),
"ClientBodyBufferSize": clientbodybuffersize.NewParser(cfg), "ClientBodyBufferSize": clientbodybuffersize.NewParser(),
"ConfigurationSnippet": snippet.NewParser(cfg), "ConfigurationSnippet": snippet.NewParser(),
"CorsConfig": cors.NewParser(cfg), "CorsConfig": cors.NewParser(),
"DefaultBackend": defaultbackend.NewParser(cfg), "DefaultBackend": defaultbackend.NewParser(cfg),
"ExternalAuth": authreq.NewParser(cfg), "ExternalAuth": authreq.NewParser(),
"HealthCheck": healthcheck.NewParser(cfg), "HealthCheck": healthcheck.NewParser(cfg),
"Proxy": proxy.NewParser(cfg), "Proxy": proxy.NewParser(cfg),
"RateLimit": ratelimit.NewParser(cfg), "RateLimit": ratelimit.NewParser(cfg),
"Redirect": redirect.NewParser(cfg), "Redirect": redirect.NewParser(cfg),
"Rewrite": rewrite.NewParser(cfg), "Rewrite": rewrite.NewParser(cfg),
"SecureUpstream": secureupstream.NewParser(cfg), "SecureUpstream": secureupstream.NewParser(cfg),
"ServerSnippet": serversnippet.NewParser(cfg), "ServerSnippet": serversnippet.NewParser(),
"ServiceUpstream": serviceupstream.NewParser(cfg), "ServiceUpstream": serviceupstream.NewParser(),
"SessionAffinity": sessionaffinity.NewParser(cfg), "SessionAffinity": sessionaffinity.NewParser(cfg),
"SSLPassthrough": sslpassthrough.NewParser(cfg), "SSLPassthrough": sslpassthrough.NewParser(),
"UsePortInRedirects": portinredirect.NewParser(cfg), "UsePortInRedirects": portinredirect.NewParser(cfg),
"UpstreamHashBy": upstreamhashby.NewParser(cfg), "UpstreamHashBy": upstreamhashby.NewParser(),
"UpstreamVhost": upstreamvhost.NewParser(cfg), "UpstreamVhost": upstreamvhost.NewParser(),
"VtsFilterKey": vtsfilterkey.NewParser(cfg), "VtsFilterKey": vtsfilterkey.NewParser(),
"Whitelist": ipwhitelist.NewParser(cfg), "Whitelist": ipwhitelist.NewParser(cfg),
}, },
} }

View file

@ -24,27 +24,28 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const ( var (
annotationSecureUpstream = "nginx/secure-backends" annotationSecureUpstream = parser.GetAnnotationWithPrefix("secure-backends")
annotationSecureVerifyCACert = "nginx/secure-verify-ca-secret" annotationSecureVerifyCACert = parser.GetAnnotationWithPrefix("secure-verify-ca-secret")
annotationUpsMaxFails = "nginx/upstream-max-fails" annotationUpsMaxFails = parser.GetAnnotationWithPrefix("upstream-max-fails")
annotationUpsFailTimeout = "nginx/upstream-fail-timeout" annotationUpsFailTimeout = parser.GetAnnotationWithPrefix("upstream-fail-timeout")
annotationPassthrough = "nginx/ssl-passthrough" annotationPassthrough = parser.GetAnnotationWithPrefix("ssl-passthrough")
annotationAffinityType = "nginx/affinity" annotationAffinityType = parser.GetAnnotationWithPrefix("affinity")
annotationCorsEnabled = "nginx/enable-cors" annotationCorsEnabled = parser.GetAnnotationWithPrefix("enable-cors")
annotationCorsAllowOrigin = "nginx/cors-allow-origin" annotationCorsAllowOrigin = parser.GetAnnotationWithPrefix("cors-allow-origin")
annotationCorsAllowMethods = "nginx/cors-allow-methods" annotationCorsAllowMethods = parser.GetAnnotationWithPrefix("cors-allow-methods")
annotationCorsAllowHeaders = "nginx/cors-allow-headers" annotationCorsAllowHeaders = parser.GetAnnotationWithPrefix("cors-allow-headers")
annotationCorsAllowCredentials = "nginx/cors-allow-credentials" annotationCorsAllowCredentials = parser.GetAnnotationWithPrefix("cors-allow-credentials")
defaultCorsMethods = "GET, PUT, POST, DELETE, PATCH, OPTIONS" 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" defaultCorsHeaders = "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"
annotationAffinityCookieName = "nginx/session-cookie-name" annotationAffinityCookieName = parser.GetAnnotationWithPrefix("session-cookie-name")
annotationAffinityCookieHash = "nginx/session-cookie-hash" annotationAffinityCookieHash = parser.GetAnnotationWithPrefix("session-cookie-hash")
annotationUpstreamHashBy = "nginx/upstream-hash-by" annotationUpstreamHashBy = parser.GetAnnotationWithPrefix("upstream-hash-by")
) )
type mockCfg struct { type mockCfg struct {

View file

@ -102,7 +102,7 @@ func NewParser(authDirectory string, r resolver.Resolver) parser.IngressAnnotati
// and generated an htpasswd compatible file to be used as source // and generated an htpasswd compatible file to be used as source
// during the authentication process // during the authentication process
func (a auth) Parse(ing *extensions.Ingress) (interface{}, error) { func (a auth) Parse(ing *extensions.Ingress) (interface{}, error) {
at, err := parser.GetStringAnnotation("auth-type", ing, a.r) at, err := parser.GetStringAnnotation("auth-type", ing)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -111,7 +111,7 @@ func (a auth) Parse(ing *extensions.Ingress) (interface{}, error) {
return nil, ing_errors.NewLocationDenied("invalid authentication type") return nil, ing_errors.NewLocationDenied("invalid authentication type")
} }
s, err := parser.GetStringAnnotation("auth-secret", ing, a.r) s, err := parser.GetStringAnnotation("auth-secret", ing)
if err != nil { if err != nil {
return nil, ing_errors.LocationDenied{ return nil, ing_errors.LocationDenied{
Reason: errors.Wrap(err, "error reading secret name from annotation"), Reason: errors.Wrap(err, "error reading secret name from annotation"),
@ -126,7 +126,7 @@ func (a auth) Parse(ing *extensions.Ingress) (interface{}, error) {
} }
} }
realm, _ := parser.GetStringAnnotation("auth-realm", ing, a.r) realm, _ := parser.GetStringAnnotation("auth-realm", ing)
passFile := fmt.Sprintf("%v/%v-%v.passwd", a.authDirectory, ing.GetNamespace(), ing.GetName()) passFile := fmt.Sprintf("%v/%v-%v.passwd", a.authDirectory, ing.GetNamespace(), ing.GetName())
err = dumpSecret(passFile, secret) err = dumpSecret(passFile, secret)

View file

@ -29,6 +29,7 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -99,9 +100,9 @@ func TestIngressAuth(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/auth-type"] = "basic" data[parser.GetAnnotationWithPrefix("auth-type")] = "basic"
data["nginx/auth-secret"] = "demo-secret" data[parser.GetAnnotationWithPrefix("auth-secret")] = "demo-secret"
data["nginx/auth-realm"] = "-realm-" data[parser.GetAnnotationWithPrefix("auth-realm")] = "-realm-"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
@ -130,9 +131,9 @@ func TestIngressAuthWithoutSecret(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/auth-type"] = "basic" data[parser.GetAnnotationWithPrefix("auth-type")] = "basic"
data["nginx/auth-secret"] = "invalid-secret" data[parser.GetAnnotationWithPrefix("auth-secret")] = "invalid-secret"
data["nginx/auth-realm"] = "-realm-" data[parser.GetAnnotationWithPrefix("auth-realm")] = "-realm-"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)

View file

@ -25,7 +25,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
// Config returns external authentication configuration for an Ingress rule // Config returns external authentication configuration for an Ingress rule
@ -101,18 +100,17 @@ func validHeader(header string) bool {
} }
type authReq struct { type authReq struct {
r resolver.Resolver
} }
// NewParser creates a new authentication request annotation parser // NewParser creates a new authentication request annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return authReq{r} return authReq{}
} }
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
// rule used to use an Config 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) { func (a authReq) Parse(ing *extensions.Ingress) (interface{}, error) {
str, err := parser.GetStringAnnotation("auth-url", ing, a.r) str, err := parser.GetStringAnnotation("auth-url", ing)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -121,7 +119,7 @@ func (a authReq) Parse(ing *extensions.Ingress) (interface{}, error) {
return nil, ing_errors.NewLocationDenied("an empty string is not a valid URL") return nil, ing_errors.NewLocationDenied("an empty string is not a valid URL")
} }
signin, _ := parser.GetStringAnnotation("auth-signin", ing, a.r) signin, _ := parser.GetStringAnnotation("auth-signin", ing)
ur, err := url.Parse(str) ur, err := url.Parse(str)
if err != nil { if err != nil {
@ -138,13 +136,13 @@ func (a authReq) Parse(ing *extensions.Ingress) (interface{}, error) {
return nil, ing_errors.NewLocationDenied("invalid url host") return nil, ing_errors.NewLocationDenied("invalid url host")
} }
m, _ := parser.GetStringAnnotation("auth-method", ing, a.r) m, _ := parser.GetStringAnnotation("auth-method", ing)
if len(m) != 0 && !validMethod(m) { if len(m) != 0 && !validMethod(m) {
return nil, ing_errors.NewLocationDenied("invalid HTTP method") return nil, ing_errors.NewLocationDenied("invalid HTTP method")
} }
h := []string{} h := []string{}
hstr, _ := parser.GetStringAnnotation("auth-response-headers", ing, a.r) hstr, _ := parser.GetStringAnnotation("auth-response-headers", ing)
if len(hstr) != 0 { if len(hstr) != 0 {
harr := strings.Split(hstr, ",") harr := strings.Split(hstr, ",")

View file

@ -24,7 +24,7 @@ import (
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
) )
@ -87,11 +87,11 @@ func TestAnnotations(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
data["nginx/auth-url"] = test.url data[parser.GetAnnotationWithPrefix("auth-url")] = test.url
data["nginx/auth-signin"] = test.signinURL data[parser.GetAnnotationWithPrefix("auth-signin")] = test.signinURL
data["nginx/auth-method"] = fmt.Sprintf("%v", test.method) data[parser.GetAnnotationWithPrefix("auth-method")] = fmt.Sprintf("%v", test.method)
i, err := NewParser(&resolver.Mock{}).Parse(ing) i, err := NewParser().Parse(ing)
if test.expErr { if test.expErr {
if err == nil { if err == nil {
t.Errorf("%v: expected error but retuned nil", test.title) t.Errorf("%v: expected error but retuned nil", test.title)
@ -137,11 +137,11 @@ func TestHeaderAnnotations(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
data["nginx/auth-url"] = test.url data[parser.GetAnnotationWithPrefix("auth-url")] = test.url
data["nginx/auth-response-headers"] = test.headers data[parser.GetAnnotationWithPrefix("auth-response-headers")] = test.headers
data["nginx/auth-method"] = "GET" data[parser.GetAnnotationWithPrefix("auth-method")] = "GET"
i, err := NewParser(&resolver.Mock{}).Parse(ing) i, err := NewParser().Parse(ing)
if test.expErr { if test.expErr {
if err == nil { if err == nil {
t.Errorf("%v: expected error but retuned nil", err.Error()) t.Errorf("%v: expected error but retuned nil", err.Error())

View file

@ -87,7 +87,7 @@ type authTLS struct {
// rule used to use a Certificate as authentication method // rule used to use a Certificate as authentication method
func (a authTLS) Parse(ing *extensions.Ingress) (interface{}, error) { func (a authTLS) Parse(ing *extensions.Ingress) (interface{}, error) {
tlsauthsecret, err := parser.GetStringAnnotation(a.r.GetAnnotationWithPrefix("auth-tls-secret"), ing, a.r) tlsauthsecret, err := parser.GetStringAnnotation("auth-tls-secret", ing)
if err != nil { if err != nil {
return &Config{}, err return &Config{}, err
} }
@ -101,12 +101,12 @@ func (a authTLS) Parse(ing *extensions.Ingress) (interface{}, error) {
return &Config{}, ing_errors.NewLocationDenied(err.Error()) return &Config{}, ing_errors.NewLocationDenied(err.Error())
} }
tlsVerifyClient, err := parser.GetStringAnnotation("auth-tls-verify-client", ing, a.r) tlsVerifyClient, err := parser.GetStringAnnotation("auth-tls-verify-client", ing)
if err != nil || !authVerifyClientRegex.MatchString(tlsVerifyClient) { if err != nil || !authVerifyClientRegex.MatchString(tlsVerifyClient) {
tlsVerifyClient = defaultAuthVerifyClient tlsVerifyClient = defaultAuthVerifyClient
} }
tlsdepth, err := parser.GetIntAnnotation("auth-tls-verify-depth", ing, a.r) tlsdepth, err := parser.GetIntAnnotation("auth-tls-verify-depth", ing)
if err != nil || tlsdepth == 0 { if err != nil || tlsdepth == 0 {
tlsdepth = defaultAuthTLSDepth tlsdepth = defaultAuthTLSDepth
} }
@ -118,12 +118,12 @@ func (a authTLS) Parse(ing *extensions.Ingress) (interface{}, error) {
} }
} }
errorpage, err := parser.GetStringAnnotation("auth-tls-error-page", ing, a.r) errorpage, err := parser.GetStringAnnotation("auth-tls-error-page", ing)
if err != nil || errorpage == "" { if err != nil || errorpage == "" {
errorpage = "" errorpage = ""
} }
passCert, err := parser.GetBoolAnnotation("auth-tls-pass-certificate-to-upstream", ing, a.r) passCert, err := parser.GetBoolAnnotation("auth-tls-pass-certificate-to-upstream", ing)
if err != nil { if err != nil {
passCert = false passCert = false
} }

View file

@ -28,10 +28,20 @@ const (
IngressKey = "kubernetes.io/ingress.class" IngressKey = "kubernetes.io/ingress.class"
) )
var (
// DefaultClass defines the default class used in the nginx ingres controller
DefaultClass = "nginx"
// IngressClass sets the runtime ingress class to use
// An empty string means accept all ingresses without
// annotation and the ones configured with class nginx
IngressClass = ""
)
// IsValid returns true if the given Ingress either doesn't specify // IsValid returns true if the given Ingress either doesn't specify
// the ingress.class annotation, or it's set to the configured in the // the ingress.class annotation, or it's set to the configured in the
// ingress controller. // ingress controller.
func IsValid(ing *extensions.Ingress, controller, defClass string) bool { func IsValid(ing *extensions.Ingress) bool {
ingress, ok := ing.GetAnnotations()[IngressKey] ingress, ok := ing.GetAnnotations()[IngressKey]
if !ok { if !ok {
glog.V(3).Infof("annotation %v is not present in ingress %v/%v", IngressKey, ing.Namespace, ing.Name) glog.V(3).Infof("annotation %v is not present in ingress %v/%v", IngressKey, ing.Namespace, ing.Name)
@ -44,9 +54,9 @@ func IsValid(ing *extensions.Ingress, controller, defClass string) bool {
// and 2 invalid combinations // and 2 invalid combinations
// 3 - ingress with default class | fixed annotation on ingress // 3 - ingress with default class | fixed annotation on ingress
// 4 - ingress with specific class | different annotation on ingress // 4 - ingress with specific class | different annotation on ingress
if ingress == "" && controller == defClass { if ingress == "" && IngressClass == DefaultClass {
return true return true
} }
return ingress == controller return ingress == IngressClass
} }

View file

@ -25,6 +25,14 @@ import (
) )
func TestIsValidClass(t *testing.T) { func TestIsValidClass(t *testing.T) {
dc := DefaultClass
ic := IngressClass
// restore original values after the tests
defer func() {
DefaultClass = dc
IngressClass = ic
}()
tests := []struct { tests := []struct {
ingress string ingress string
controller string controller string
@ -51,7 +59,11 @@ func TestIsValidClass(t *testing.T) {
ing.SetAnnotations(data) ing.SetAnnotations(data)
for _, test := range tests { for _, test := range tests {
ing.Annotations[IngressKey] = test.ingress ing.Annotations[IngressKey] = test.ingress
b := IsValid(ing, test.controller, test.defClass)
IngressClass = test.controller
DefaultClass = test.defClass
b := IsValid(ing)
if b != test.isValid { if b != test.isValid {
t.Errorf("test %v - expected %v but %v was returned", test, test.isValid, b) t.Errorf("test %v - expected %v but %v was returned", test, test.isValid, b)
} }

View file

@ -20,20 +20,17 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
type clientBodyBufferSize struct { type clientBodyBufferSize struct{}
r resolver.Resolver
}
// NewParser creates a new clientBodyBufferSize annotation parser // NewParser creates a new clientBodyBufferSize annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return clientBodyBufferSize{r} return clientBodyBufferSize{}
} }
// Parse parses the annotations contained in the ingress rule // Parse parses the annotations contained in the ingress rule
// used to add an client-body-buffer-size to the provided locations // used to add an client-body-buffer-size to the provided locations
func (cbbs clientBodyBufferSize) Parse(ing *extensions.Ingress) (interface{}, error) { func (cbbs clientBodyBufferSize) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation("client-body-buffer-size", ing, cbbs.r) return parser.GetStringAnnotation("client-body-buffer-size", ing)
} }

View file

@ -22,12 +22,12 @@ import (
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
annotation := "nginx/client-body-buffer-size" annotation := parser.GetAnnotationWithPrefix("client-body-buffer-size")
ap := NewParser(&resolver.Mock{}) ap := NewParser()
if ap == nil { if ap == nil {
t.Fatalf("expected a parser.IngressAnnotation but returned nil") t.Fatalf("expected a parser.IngressAnnotation but returned nil")
} }

View file

@ -22,7 +22,6 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const ( const (
@ -44,9 +43,7 @@ var (
corsHeadersRegex = regexp.MustCompile(`^([A-Za-z0-9\-\_]+,?\s?)+$`) corsHeadersRegex = regexp.MustCompile(`^([A-Za-z0-9\-\_]+,?\s?)+$`)
) )
type cors struct { type cors struct{}
r resolver.Resolver
}
// Config contains the Cors configuration to be used in the Ingress // Config contains the Cors configuration to be used in the Ingress
type Config struct { type Config struct {
@ -58,8 +55,8 @@ type Config struct {
} }
// NewParser creates a new CORS annotation parser // NewParser creates a new CORS annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return cors{r} return cors{}
} }
// Equal tests for equality between two External types // Equal tests for equality between two External types
@ -92,27 +89,27 @@ func (c1 *Config) Equal(c2 *Config) bool {
// Parse parses the annotations contained in the ingress // Parse parses the annotations contained in the ingress
// rule used to indicate if the location/s should allows CORS // rule used to indicate if the location/s should allows CORS
func (c cors) Parse(ing *extensions.Ingress) (interface{}, error) { func (c cors) Parse(ing *extensions.Ingress) (interface{}, error) {
corsenabled, err := parser.GetBoolAnnotation("enable-cors", ing, c.r) corsenabled, err := parser.GetBoolAnnotation("enable-cors", ing)
if err != nil { if err != nil {
corsenabled = false corsenabled = false
} }
corsalloworigin, err := parser.GetStringAnnotation("cors-allow-origin", ing, c.r) corsalloworigin, err := parser.GetStringAnnotation("cors-allow-origin", ing)
if err != nil || corsalloworigin == "" || !corsOriginRegex.MatchString(corsalloworigin) { if err != nil || corsalloworigin == "" || !corsOriginRegex.MatchString(corsalloworigin) {
corsalloworigin = "*" corsalloworigin = "*"
} }
corsallowheaders, err := parser.GetStringAnnotation("cors-allow-headers", ing, c.r) corsallowheaders, err := parser.GetStringAnnotation("cors-allow-headers", ing)
if err != nil || corsallowheaders == "" || !corsHeadersRegex.MatchString(corsallowheaders) { if err != nil || corsallowheaders == "" || !corsHeadersRegex.MatchString(corsallowheaders) {
corsallowheaders = defaultCorsHeaders corsallowheaders = defaultCorsHeaders
} }
corsallowmethods, err := parser.GetStringAnnotation("cors-allow-methods", ing, c.r) corsallowmethods, err := parser.GetStringAnnotation("cors-allow-methods", ing)
if err != nil || corsallowmethods == "" || !corsMethodsRegex.MatchString(corsallowmethods) { if err != nil || corsallowmethods == "" || !corsMethodsRegex.MatchString(corsallowmethods) {
corsallowmethods = defaultCorsMethods corsallowmethods = defaultCorsMethods
} }
corsallowcredentials, err := parser.GetBoolAnnotation("cors-allow-credentials", ing, c.r) corsallowcredentials, err := parser.GetBoolAnnotation("cors-allow-credentials", ing)
if err != nil { if err != nil {
corsallowcredentials = true corsallowcredentials = true
} }

View file

@ -23,7 +23,7 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -65,14 +65,14 @@ func TestIngressCorsConfig(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/enable-cors"] = "true" data[parser.GetAnnotationWithPrefix("enable-cors")] = "true"
data["nginx/cors-allow-headers"] = "DNT,X-CustomHeader, Keep-Alive,User-Agent" data[parser.GetAnnotationWithPrefix("cors-allow-headers")] = "DNT,X-CustomHeader, Keep-Alive,User-Agent"
data["nginx/cors-allow-credentials"] = "false" data[parser.GetAnnotationWithPrefix("cors-allow-credentials")] = "false"
data["nginx/cors-allow-methods"] = "PUT, GET,OPTIONS, PATCH, $nginx_version" data[parser.GetAnnotationWithPrefix("cors-allow-methods")] = "PUT, GET,OPTIONS, PATCH, $nginx_version"
data["nginx/cors-allow-origin"] = "https://origin123.test.com:4443" data[parser.GetAnnotationWithPrefix("cors-allow-origin")] = "https://origin123.test.com:4443"
ing.SetAnnotations(data) ing.SetAnnotations(data)
corst, _ := NewParser(&resolver.Mock{}).Parse(ing) corst, _ := NewParser().Parse(ing)
nginxCors, ok := corst.(*Config) nginxCors, ok := corst.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")

View file

@ -38,7 +38,7 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// Parse parses the annotations contained in the ingress to use // Parse parses the annotations contained in the ingress to use
// a custom default backend // a custom default backend
func (db backend) Parse(ing *extensions.Ingress) (interface{}, error) { func (db backend) Parse(ing *extensions.Ingress) (interface{}, error) {
s, err := parser.GetStringAnnotation("default-backend", ing, db.r) s, err := parser.GetStringAnnotation("default-backend", ing)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -47,12 +47,12 @@ func (hc healthCheck) Parse(ing *extensions.Ingress) (interface{}, error) {
return &Config{defBackend.UpstreamMaxFails, defBackend.UpstreamFailTimeout}, nil return &Config{defBackend.UpstreamMaxFails, defBackend.UpstreamFailTimeout}, nil
} }
mf, err := parser.GetIntAnnotation("upstream-max-fails", ing, hc.r) mf, err := parser.GetIntAnnotation("upstream-max-fails", ing)
if err != nil { if err != nil {
mf = defBackend.UpstreamMaxFails mf = defBackend.UpstreamMaxFails
} }
ft, err := parser.GetIntAnnotation("upstream-fail-timeout", ing, hc.r) ft, err := parser.GetIntAnnotation("upstream-fail-timeout", ing)
if err != nil { if err != nil {
ft = defBackend.UpstreamFailTimeout ft = defBackend.UpstreamFailTimeout
} }

View file

@ -24,6 +24,7 @@ import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -75,7 +76,7 @@ func TestIngressHealthCheck(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/upstream-max-fails"] = "2" data[parser.GetAnnotationWithPrefix("upstream-max-fails")] = "2"
ing.SetAnnotations(data) ing.SetAnnotations(data)
hzi, _ := NewParser(mockBackend{}).Parse(ing) hzi, _ := NewParser(mockBackend{}).Parse(ing)

View file

@ -81,7 +81,7 @@ func (a ipwhitelist) Parse(ing *extensions.Ingress) (interface{}, error) {
defBackend := a.r.GetDefaultBackend() defBackend := a.r.GetDefaultBackend()
sort.Strings(defBackend.WhitelistSourceRange) sort.Strings(defBackend.WhitelistSourceRange)
val, err := parser.GetStringAnnotation("whitelist-source-range", ing, a.r) val, err := parser.GetStringAnnotation("whitelist-source-range", ing)
// A missing annotation is not a problem, just use the default // A missing annotation is not a problem, just use the default
if err == ing_errors.ErrMissingAnnotations { if err == ing_errors.ErrMissingAnnotations {
return &SourceRange{CIDR: defBackend.WhitelistSourceRange}, nil return &SourceRange{CIDR: defBackend.WhitelistSourceRange}, nil

View file

@ -23,6 +23,7 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -94,7 +95,7 @@ func TestParseAnnotations(t *testing.T) {
for testName, test := range tests { for testName, test := range tests {
data := map[string]string{} data := map[string]string{}
data["nginx/whitelist-source-range"] = test.net data[parser.GetAnnotationWithPrefix("whitelist-source-range")] = test.net
ing.SetAnnotations(data) ing.SetAnnotations(data)
p := NewParser(&resolver.Mock{}) p := NewParser(&resolver.Mock{})
i, err := p.Parse(ing) i, err := p.Parse(ing)
@ -166,7 +167,7 @@ func TestParseAnnotationsWithDefaultConfig(t *testing.T) {
for testName, test := range tests { for testName, test := range tests {
data := map[string]string{} data := map[string]string{}
data["nginx/whitelist-source-range"] = test.net data[parser.GetAnnotationWithPrefix("whitelist-source-range")] = test.net
ing.SetAnnotations(data) ing.SetAnnotations(data)
p := NewParser(mockBackend) p := NewParser(mockBackend)
i, err := p.Parse(ing) i, err := p.Parse(ing)

View file

@ -17,12 +17,17 @@ limitations under the License.
package parser package parser
import ( import (
"fmt"
"strconv" "strconv"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver" )
var (
// AnnotationsPrefix defines the common prefix used in the nginx ingress controller
AnnotationsPrefix = "nginx.ingress.kubernetes.io"
) )
// IngressAnnotation has a method to parse annotations located in Ingress // IngressAnnotation has a method to parse annotations located in Ingress
@ -76,8 +81,8 @@ func checkAnnotation(name string, ing *extensions.Ingress) error {
} }
// GetBoolAnnotation extracts a boolean from an Ingress annotation // GetBoolAnnotation extracts a boolean from an Ingress annotation
func GetBoolAnnotation(name string, ing *extensions.Ingress, r resolver.Resolver) (bool, error) { func GetBoolAnnotation(name string, ing *extensions.Ingress) (bool, error) {
v := r.GetAnnotationWithPrefix(name) v := GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing) err := checkAnnotation(v, ing)
if err != nil { if err != nil {
return false, err return false, err
@ -86,8 +91,8 @@ func GetBoolAnnotation(name string, ing *extensions.Ingress, r resolver.Resolver
} }
// GetStringAnnotation extracts a string from an Ingress annotation // GetStringAnnotation extracts a string from an Ingress annotation
func GetStringAnnotation(name string, ing *extensions.Ingress, r resolver.Resolver) (string, error) { func GetStringAnnotation(name string, ing *extensions.Ingress) (string, error) {
v := r.GetAnnotationWithPrefix(name) v := GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing) err := checkAnnotation(v, ing)
if err != nil { if err != nil {
return "", err return "", err
@ -96,11 +101,16 @@ func GetStringAnnotation(name string, ing *extensions.Ingress, r resolver.Resolv
} }
// GetIntAnnotation extracts an int from an Ingress annotation // GetIntAnnotation extracts an int from an Ingress annotation
func GetIntAnnotation(name string, ing *extensions.Ingress, r resolver.Resolver) (int, error) { func GetIntAnnotation(name string, ing *extensions.Ingress) (int, error) {
v := r.GetAnnotationWithPrefix(name) v := GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing) err := checkAnnotation(v, ing)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return ingAnnotations(ing.GetAnnotations()).parseInt(v) return ingAnnotations(ing.GetAnnotations()).parseInt(v)
} }
// GetAnnotationWithPrefix returns the prefix of ingress annotations
func GetAnnotationWithPrefix(suffix string) string {
return fmt.Sprintf("%v/%v", AnnotationsPrefix, suffix)
}

View file

@ -17,13 +17,11 @@ limitations under the License.
package parser package parser
import ( import (
"fmt"
"testing" "testing"
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -37,11 +35,9 @@ func buildIngress() *extensions.Ingress {
} }
func TestGetBoolAnnotation(t *testing.T) { func TestGetBoolAnnotation(t *testing.T) {
r := &resolver.Mock{}
ing := buildIngress() ing := buildIngress()
_, err := GetBoolAnnotation("", nil, r) _, err := GetBoolAnnotation("", nil)
if err == nil { if err == nil {
t.Errorf("expected error but retuned nil") t.Errorf("expected error but retuned nil")
} }
@ -61,9 +57,9 @@ func TestGetBoolAnnotation(t *testing.T) {
ing.SetAnnotations(data) ing.SetAnnotations(data)
for _, test := range tests { for _, test := range tests {
data[fmt.Sprintf("nginx/%v", test.field)] = test.value data[GetAnnotationWithPrefix(test.field)] = test.value
u, err := GetBoolAnnotation(test.field, ing, r) u, err := GetBoolAnnotation(test.field, ing)
if test.expErr { if test.expErr {
if err == nil { if err == nil {
t.Errorf("%v: expected error but retuned nil", test.name) t.Errorf("%v: expected error but retuned nil", test.name)
@ -79,11 +75,9 @@ func TestGetBoolAnnotation(t *testing.T) {
} }
func TestGetStringAnnotation(t *testing.T) { func TestGetStringAnnotation(t *testing.T) {
r := &resolver.Mock{}
ing := buildIngress() ing := buildIngress()
_, err := GetStringAnnotation("", nil, r) _, err := GetStringAnnotation("", nil)
if err == nil { if err == nil {
t.Errorf("expected error but retuned nil") t.Errorf("expected error but retuned nil")
} }
@ -103,9 +97,9 @@ func TestGetStringAnnotation(t *testing.T) {
ing.SetAnnotations(data) ing.SetAnnotations(data)
for _, test := range tests { for _, test := range tests {
data[fmt.Sprintf("nginx/%v", test.field)] = test.value data[GetAnnotationWithPrefix(test.field)] = test.value
s, err := GetStringAnnotation(test.field, ing, r) s, err := GetStringAnnotation(test.field, ing)
if test.expErr { if test.expErr {
if err == nil { if err == nil {
t.Errorf("%v: expected error but retuned nil", test.name) t.Errorf("%v: expected error but retuned nil", test.name)
@ -121,11 +115,9 @@ func TestGetStringAnnotation(t *testing.T) {
} }
func TestGetIntAnnotation(t *testing.T) { func TestGetIntAnnotation(t *testing.T) {
r := &resolver.Mock{}
ing := buildIngress() ing := buildIngress()
_, err := GetIntAnnotation("", nil, r) _, err := GetIntAnnotation("", nil)
if err == nil { if err == nil {
t.Errorf("expected error but retuned nil") t.Errorf("expected error but retuned nil")
} }
@ -145,9 +137,9 @@ func TestGetIntAnnotation(t *testing.T) {
ing.SetAnnotations(data) ing.SetAnnotations(data)
for _, test := range tests { for _, test := range tests {
data[fmt.Sprintf("nginx/%v", test.field)] = test.value data[GetAnnotationWithPrefix(test.field)] = test.value
s, err := GetIntAnnotation(test.field, ing, r) s, err := GetIntAnnotation(test.field, ing)
if test.expErr { if test.expErr {
if err == nil { if err == nil {
t.Errorf("%v: expected error but retuned nil", test.name) t.Errorf("%v: expected error but retuned nil", test.name)

View file

@ -35,7 +35,7 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// Parse parses the annotations contained in the ingress // Parse parses the annotations contained in the ingress
// rule used to indicate if the redirects must // rule used to indicate if the redirects must
func (a portInRedirect) Parse(ing *extensions.Ingress) (interface{}, error) { func (a portInRedirect) Parse(ing *extensions.Ingress) (interface{}, error) {
up, err := parser.GetBoolAnnotation("use-port-in-redirects", ing, a.r) up, err := parser.GetBoolAnnotation("use-port-in-redirects", ing)
if err != nil { if err != nil {
return a.r.GetDefaultBackend().UsePortInRedirects, nil return a.r.GetDefaultBackend().UsePortInRedirects, nil
} }

View file

@ -25,6 +25,7 @@ import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -92,7 +93,7 @@ func TestPortInRedirect(t *testing.T) {
data := map[string]string{} data := map[string]string{}
if test.usePort != nil { if test.usePort != nil {
data["nginx/use-port-in-redirects"] = fmt.Sprintf("%v", *test.usePort) data[parser.GetAnnotationWithPrefix("use-port-in-redirects")] = fmt.Sprintf("%v", *test.usePort)
} }
ing.SetAnnotations(data) ing.SetAnnotations(data)

View file

@ -100,62 +100,62 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// rule used to configure upstream check parameters // rule used to configure upstream check parameters
func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) { func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) {
defBackend := a.r.GetDefaultBackend() defBackend := a.r.GetDefaultBackend()
ct, err := parser.GetIntAnnotation("proxy-connect-timeout", ing, a.r) ct, err := parser.GetIntAnnotation("proxy-connect-timeout", ing)
if err != nil { if err != nil {
ct = defBackend.ProxyConnectTimeout ct = defBackend.ProxyConnectTimeout
} }
st, err := parser.GetIntAnnotation("proxy-send-timeout", ing, a.r) st, err := parser.GetIntAnnotation("proxy-send-timeout", ing)
if err != nil { if err != nil {
st = defBackend.ProxySendTimeout st = defBackend.ProxySendTimeout
} }
rt, err := parser.GetIntAnnotation("proxy-read-timeout", ing, a.r) rt, err := parser.GetIntAnnotation("proxy-read-timeout", ing)
if err != nil { if err != nil {
rt = defBackend.ProxyReadTimeout rt = defBackend.ProxyReadTimeout
} }
bufs, err := parser.GetStringAnnotation("proxy-buffer-size", ing, a.r) bufs, err := parser.GetStringAnnotation("proxy-buffer-size", ing)
if err != nil || bufs == "" { if err != nil || bufs == "" {
bufs = defBackend.ProxyBufferSize bufs = defBackend.ProxyBufferSize
} }
cp, err := parser.GetStringAnnotation("proxy-cookie-path", ing, a.r) cp, err := parser.GetStringAnnotation("proxy-cookie-path", ing)
if err != nil || cp == "" { if err != nil || cp == "" {
cp = defBackend.ProxyCookiePath cp = defBackend.ProxyCookiePath
} }
cd, err := parser.GetStringAnnotation("proxy-cookie-domain", ing, a.r) cd, err := parser.GetStringAnnotation("proxy-cookie-domain", ing)
if err != nil || cd == "" { if err != nil || cd == "" {
cd = defBackend.ProxyCookieDomain cd = defBackend.ProxyCookieDomain
} }
bs, err := parser.GetStringAnnotation("proxy-body-size", ing, a.r) bs, err := parser.GetStringAnnotation("proxy-body-size", ing)
if err != nil || bs == "" { if err != nil || bs == "" {
bs = defBackend.ProxyBodySize bs = defBackend.ProxyBodySize
} }
nu, err := parser.GetStringAnnotation("proxy-next-upstream", ing, a.r) nu, err := parser.GetStringAnnotation("proxy-next-upstream", ing)
if err != nil || nu == "" { if err != nil || nu == "" {
nu = defBackend.ProxyNextUpstream nu = defBackend.ProxyNextUpstream
} }
pp, err := parser.GetStringAnnotation("proxy-pass-params", ing, a.r) pp, err := parser.GetStringAnnotation("proxy-pass-params", ing)
if err != nil || pp == "" { if err != nil || pp == "" {
pp = defBackend.ProxyPassParams pp = defBackend.ProxyPassParams
} }
rb, err := parser.GetStringAnnotation("proxy-request-buffering", ing, a.r) rb, err := parser.GetStringAnnotation("proxy-request-buffering", ing)
if err != nil || rb == "" { if err != nil || rb == "" {
rb = defBackend.ProxyRequestBuffering rb = defBackend.ProxyRequestBuffering
} }
prf, err := parser.GetStringAnnotation("proxy-redirect-from", ing, a.r) prf, err := parser.GetStringAnnotation("proxy-redirect-from", ing)
if err != nil || rb == "" { if err != nil || rb == "" {
prf = defBackend.ProxyRedirectFrom prf = defBackend.ProxyRedirectFrom
} }
prt, err := parser.GetStringAnnotation("proxy-redirect-to", ing, a.r) prt, err := parser.GetStringAnnotation("proxy-redirect-to", ing)
if err != nil || rb == "" { if err != nil || rb == "" {
prt = defBackend.ProxyRedirectTo prt = defBackend.ProxyRedirectTo
} }

View file

@ -24,6 +24,7 @@ import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -85,14 +86,14 @@ func TestProxy(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/proxy-connect-timeout"] = "1" data[parser.GetAnnotationWithPrefix("proxy-connect-timeout")] = "1"
data["nginx/proxy-send-timeout"] = "2" data[parser.GetAnnotationWithPrefix("proxy-send-timeout")] = "2"
data["nginx/proxy-read-timeout"] = "3" data[parser.GetAnnotationWithPrefix("proxy-read-timeout")] = "3"
data["nginx/proxy-buffer-size"] = "1k" data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k"
data["nginx/proxy-body-size"] = "2k" data[parser.GetAnnotationWithPrefix("proxy-body-size")] = "2k"
data["nginx/proxy-next-upstream"] = "off" data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = "off"
data["nginx/proxy-pass-params"] = "smax=5 max=10" data[parser.GetAnnotationWithPrefix("proxy-pass-params")] = "smax=5 max=10"
data["nginx/proxy-request-buffering"] = "off" data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, err := NewParser(mockBackend{}).Parse(ing) i, err := NewParser(mockBackend{}).Parse(ing)

View file

@ -157,20 +157,20 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// rule used to rewrite the defined paths // rule used to rewrite the defined paths
func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) { func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
defBackend := a.r.GetDefaultBackend() defBackend := a.r.GetDefaultBackend()
lr, err := parser.GetIntAnnotation("limit-rate", ing, a.r) lr, err := parser.GetIntAnnotation("limit-rate", ing)
if err != nil { if err != nil {
lr = defBackend.LimitRate lr = defBackend.LimitRate
} }
lra, err := parser.GetIntAnnotation("limit-rate-after", ing, a.r) lra, err := parser.GetIntAnnotation("limit-rate-after", ing)
if err != nil { if err != nil {
lra = defBackend.LimitRateAfter lra = defBackend.LimitRateAfter
} }
rpm, _ := parser.GetIntAnnotation("limit-rpm", ing, a.r) rpm, _ := parser.GetIntAnnotation("limit-rpm", ing)
rps, _ := parser.GetIntAnnotation("limit-rps", ing, a.r) rps, _ := parser.GetIntAnnotation("limit-rps", ing)
conn, _ := parser.GetIntAnnotation("limit-connections", ing, a.r) conn, _ := parser.GetIntAnnotation("limit-connections", ing)
val, _ := parser.GetStringAnnotation("limit-whitelist", ing, a.r) val, _ := parser.GetStringAnnotation("limit-whitelist", ing)
cidrs, err := parseCIDRs(val) cidrs, err := parseCIDRs(val)
if err != nil { if err != nil {

View file

@ -24,6 +24,7 @@ import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -86,9 +87,9 @@ func TestBadRateLimiting(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/limit-connections"] = "0" data[parser.GetAnnotationWithPrefix("limit-connections")] = "0"
data["nginx/limit-rps"] = "0" data[parser.GetAnnotationWithPrefix("limit-rps")] = "0"
data["nginx/limit-rpm"] = "0" data[parser.GetAnnotationWithPrefix("limit-rpm")] = "0"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, err := NewParser(mockBackend{}).Parse(ing) _, err := NewParser(mockBackend{}).Parse(ing)
@ -97,11 +98,11 @@ func TestBadRateLimiting(t *testing.T) {
} }
data = map[string]string{} data = map[string]string{}
data["nginx/limit-connections"] = "5" data[parser.GetAnnotationWithPrefix("limit-connections")] = "5"
data["nginx/limit-rps"] = "100" data[parser.GetAnnotationWithPrefix("limit-rps")] = "100"
data["nginx/limit-rpm"] = "10" data[parser.GetAnnotationWithPrefix("limit-rpm")] = "10"
data["nginx/limit-rate-after"] = "100" data[parser.GetAnnotationWithPrefix("limit-rate-after")] = "100"
data["nginx/limit-rate"] = "10" data[parser.GetAnnotationWithPrefix("limit-rate")] = "10"
ing.SetAnnotations(data) ing.SetAnnotations(data)

View file

@ -49,9 +49,9 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// If the Ingress contains both annotations the execution order is // If the Ingress contains both annotations the execution order is
// temporal and then permanent // temporal and then permanent
func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) { func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) {
r3w, _ := parser.GetBoolAnnotation("from-to-www-redirect", ing, a.r) r3w, _ := parser.GetBoolAnnotation("from-to-www-redirect", ing)
tr, err := parser.GetStringAnnotation("temporal-redirect", ing, a.r) tr, err := parser.GetStringAnnotation("temporal-redirect", ing)
if err != nil && !errors.IsMissingAnnotations(err) { if err != nil && !errors.IsMissingAnnotations(err) {
return nil, err return nil, err
} }
@ -68,7 +68,7 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) {
}, nil }, nil
} }
pr, err := parser.GetStringAnnotation("permanent-redirect", ing, a.r) pr, err := parser.GetStringAnnotation("permanent-redirect", ing)
if err != nil && !errors.IsMissingAnnotations(err) { if err != nil && !errors.IsMissingAnnotations(err) {
return nil, err return nil, err
} }

View file

@ -82,18 +82,18 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
// rule used to rewrite the defined paths // rule used to rewrite the defined paths
func (a rewrite) Parse(ing *extensions.Ingress) (interface{}, error) { func (a rewrite) Parse(ing *extensions.Ingress) (interface{}, error) {
rt, _ := parser.GetStringAnnotation("rewrite-target", ing, a.r) rt, _ := parser.GetStringAnnotation("rewrite-target", ing)
sslRe, err := parser.GetBoolAnnotation("ssl-redirect", ing, a.r) sslRe, err := parser.GetBoolAnnotation("ssl-redirect", ing)
if err != nil { if err != nil {
sslRe = a.r.GetDefaultBackend().SSLRedirect sslRe = a.r.GetDefaultBackend().SSLRedirect
} }
fSslRe, err := parser.GetBoolAnnotation("force-ssl-redirect", ing, a.r) fSslRe, err := parser.GetBoolAnnotation("force-ssl-redirect", ing)
if err != nil { if err != nil {
fSslRe = a.r.GetDefaultBackend().ForceSSLRedirect fSslRe = a.r.GetDefaultBackend().ForceSSLRedirect
} }
abu, _ := parser.GetBoolAnnotation("add-base-url", ing, a.r) abu, _ := parser.GetBoolAnnotation("add-base-url", ing)
bus, _ := parser.GetStringAnnotation("base-url-scheme", ing, a.r) bus, _ := parser.GetStringAnnotation("base-url-scheme", ing)
ar, _ := parser.GetStringAnnotation("app-root", ing, a.r) ar, _ := parser.GetStringAnnotation("app-root", ing)
return &Config{ return &Config{
Target: rt, Target: rt,

View file

@ -24,6 +24,7 @@ import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -88,7 +89,7 @@ func TestRedirect(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/rewrite-target"] = defRoute data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, err := NewParser(mockBackend{}).Parse(ing) i, err := NewParser(mockBackend{}).Parse(ing)
@ -108,7 +109,7 @@ func TestSSLRedirect(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/rewrite-target"] = defRoute data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing) i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
@ -120,7 +121,7 @@ func TestSSLRedirect(t *testing.T) {
t.Errorf("Expected true but returned false") t.Errorf("Expected true but returned false")
} }
data["nginx/ssl-redirect"] = "false" data[parser.GetAnnotationWithPrefix("ssl-redirect")] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: false}).Parse(ing) i, _ = NewParser(mockBackend{redirect: false}).Parse(ing)
@ -137,7 +138,7 @@ func TestForceSSLRedirect(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/rewrite-target"] = defRoute data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing) i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
@ -149,7 +150,7 @@ func TestForceSSLRedirect(t *testing.T) {
t.Errorf("Expected false but returned true") t.Errorf("Expected false but returned true")
} }
data["nginx/force-ssl-redirect"] = "true" data[parser.GetAnnotationWithPrefix("force-ssl-redirect")] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: false}).Parse(ing) i, _ = NewParser(mockBackend{redirect: false}).Parse(ing)
@ -165,7 +166,7 @@ func TestAppRoot(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/app-root"] = "/app1" data[parser.GetAnnotationWithPrefix("app-root")] = "/app1"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing) i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)

View file

@ -44,8 +44,8 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// Parse parses the annotations contained in the ingress // Parse parses the annotations contained in the ingress
// rule used to indicate if the upstream servers should use SSL // rule used to indicate if the upstream servers should use SSL
func (a su) Parse(ing *extensions.Ingress) (interface{}, error) { func (a su) Parse(ing *extensions.Ingress) (interface{}, error) {
s, _ := parser.GetBoolAnnotation("secure-backends", ing, a.r) s, _ := parser.GetBoolAnnotation("secure-backends", ing)
ca, _ := parser.GetStringAnnotation("secure-verify-ca-secret", ing, a.r) ca, _ := parser.GetStringAnnotation("secure-verify-ca-secret", ing)
secure := &Config{ secure := &Config{
Secure: s, Secure: s,
CACert: resolver.AuthSSLCert{}, CACert: resolver.AuthSSLCert{},

View file

@ -25,6 +25,7 @@ import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -78,8 +79,8 @@ func (cfg mockCfg) GetAuthCertificate(secret string) (*resolver.AuthSSLCert, err
func TestAnnotations(t *testing.T) { func TestAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/secure-backends"] = "true" data[parser.GetAnnotationWithPrefix("secure-backends")] = "true"
data["nginx/secure-verify-ca-secret"] = "secure-verify-ca" data[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = "secure-verify-ca"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, err := NewParser(mockCfg{ _, err := NewParser(mockCfg{
@ -95,8 +96,8 @@ func TestAnnotations(t *testing.T) {
func TestSecretNotFound(t *testing.T) { func TestSecretNotFound(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/secure-backends"] = "true" data[parser.GetAnnotationWithPrefix("secure-backends")] = "true"
data["nginx/secure-verify-ca-secret"] = "secure-verify-ca" data[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = "secure-verify-ca"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, err := NewParser(mockCfg{}).Parse(ing) _, err := NewParser(mockCfg{}).Parse(ing)
if err == nil { if err == nil {
@ -107,8 +108,8 @@ func TestSecretNotFound(t *testing.T) {
func TestSecretOnNonSecure(t *testing.T) { func TestSecretOnNonSecure(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/secure-backends"] = "false" data[parser.GetAnnotationWithPrefix("secure-backends")] = "false"
data["nginx/secure-verify-ca-secret"] = "secure-verify-ca" data[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = "secure-verify-ca"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, err := NewParser(mockCfg{ _, err := NewParser(mockCfg{
certs: map[string]resolver.AuthSSLCert{ certs: map[string]resolver.AuthSSLCert{

View file

@ -20,21 +20,18 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
type serverSnippet struct { type serverSnippet struct{}
r resolver.Resolver
}
// NewParser creates a new server snippet annotation parser // NewParser creates a new server snippet annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return serverSnippet{r} return serverSnippet{}
} }
// Parse parses the annotations contained in the ingress rule // Parse parses the annotations contained in the ingress rule
// used to indicate if the location/s contains a fragment of // used to indicate if the location/s contains a fragment of
// configuration to be included inside the paths of the rules // configuration to be included inside the paths of the rules
func (a serverSnippet) Parse(ing *extensions.Ingress) (interface{}, error) { func (a serverSnippet) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation("server-snippet", ing, a.r) return parser.GetStringAnnotation("server-snippet", ing)
} }

View file

@ -22,13 +22,13 @@ import (
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
annotation := "nginx/server-snippet" annotation := parser.GetAnnotationWithPrefix("server-snippet")
ap := NewParser(&resolver.Mock{}) ap := NewParser()
if ap == nil { if ap == nil {
t.Fatalf("expected a parser.IngressAnnotation but returned nil") t.Fatalf("expected a parser.IngressAnnotation but returned nil")
} }

View file

@ -20,18 +20,15 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
type serviceUpstream struct { type serviceUpstream struct{}
r resolver.Resolver
}
// NewParser creates a new serviceUpstream annotation parser // NewParser creates a new serviceUpstream annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return serviceUpstream{r} return serviceUpstream{}
} }
func (s serviceUpstream) Parse(ing *extensions.Ingress) (interface{}, error) { func (s serviceUpstream) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetBoolAnnotation("service-upstream", ing, s.r) return parser.GetBoolAnnotation("service-upstream", ing)
} }

View file

@ -23,7 +23,7 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -65,10 +65,10 @@ func TestIngressAnnotationServiceUpstreamEnabled(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data["nginx/service-upstream"] = "true" data[parser.GetAnnotationWithPrefix("service-upstream")] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, _ := NewParser().Parse(ing)
enabled, ok := val.(bool) enabled, ok := val.(bool)
if !ok { if !ok {
t.Errorf("expected a bool type") t.Errorf("expected a bool type")
@ -84,10 +84,10 @@ func TestIngressAnnotationServiceUpstreamSetFalse(t *testing.T) {
// Test with explicitly set to false // Test with explicitly set to false
data := map[string]string{} data := map[string]string{}
data["nginx/service-upstream"] = "false" data[parser.GetAnnotationWithPrefix("service-upstream")] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing) val, _ := NewParser().Parse(ing)
enabled, ok := val.(bool) enabled, ok := val.(bool)
if !ok { if !ok {
t.Errorf("expected a bool type") t.Errorf("expected a bool type")
@ -101,7 +101,7 @@ func TestIngressAnnotationServiceUpstreamSetFalse(t *testing.T) {
data = map[string]string{} data = map[string]string{}
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ = NewParser(&resolver.Mock{}).Parse(ing) val, _ = NewParser().Parse(ing)
enabled, ok = val.(bool) enabled, ok = val.(bool)
if !ok { if !ok {
t.Errorf("expected a bool type") t.Errorf("expected a bool type")

View file

@ -63,14 +63,14 @@ type Cookie struct {
// cookieAffinityParse gets the annotation values related to Cookie Affinity // cookieAffinityParse gets the annotation values related to Cookie Affinity
// It also sets default values when no value or incorrect value is found // It also sets default values when no value or incorrect value is found
func (a affinity) cookieAffinityParse(ing *extensions.Ingress) *Cookie { func (a affinity) cookieAffinityParse(ing *extensions.Ingress) *Cookie {
sn, err := parser.GetStringAnnotation(annotationAffinityCookieName, ing, a.r) sn, err := parser.GetStringAnnotation(annotationAffinityCookieName, ing)
if err != nil || sn == "" { if err != nil || sn == "" {
glog.V(3).Infof("Ingress %v: No value found in annotation %v. Using the default %v", ing.Name, annotationAffinityCookieName, defaultAffinityCookieName) glog.V(3).Infof("Ingress %v: No value found in annotation %v. Using the default %v", ing.Name, annotationAffinityCookieName, defaultAffinityCookieName)
sn = defaultAffinityCookieName sn = defaultAffinityCookieName
} }
sh, err := parser.GetStringAnnotation(annotationAffinityCookieHash, ing, a.r) sh, err := parser.GetStringAnnotation(annotationAffinityCookieHash, ing)
if err != nil || !affinityCookieHashRegex.MatchString(sh) { if err != nil || !affinityCookieHashRegex.MatchString(sh) {
glog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Setting it to default %v", ing.Name, annotationAffinityCookieHash, defaultAffinityCookieHash) glog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Setting it to default %v", ing.Name, annotationAffinityCookieHash, defaultAffinityCookieHash)
@ -97,7 +97,7 @@ type affinity struct {
func (a affinity) Parse(ing *extensions.Ingress) (interface{}, error) { func (a affinity) Parse(ing *extensions.Ingress) (interface{}, error) {
cookie := &Cookie{} cookie := &Cookie{}
// Check the type of affinity that will be used // Check the type of affinity that will be used
at, err := parser.GetStringAnnotation(annotationAffinityType, ing, a.r) at, err := parser.GetStringAnnotation(annotationAffinityType, ing)
if err != nil { if err != nil {
at = "" at = ""
} }

View file

@ -17,13 +17,13 @@ limitations under the License.
package sessionaffinity package sessionaffinity
import ( import (
"fmt"
"testing" "testing"
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
@ -66,9 +66,9 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[fmt.Sprintf("nginx/%v", annotationAffinityType)] = "cookie" data[parser.GetAnnotationWithPrefix(annotationAffinityType)] = "cookie"
data[fmt.Sprintf("nginx/%v", annotationAffinityCookieHash)] = "sha123" data[parser.GetAnnotationWithPrefix(annotationAffinityCookieHash)] = "sha123"
data[fmt.Sprintf("nginx/%v", annotationAffinityCookieName)] = "INGRESSCOOKIE" data[parser.GetAnnotationWithPrefix(annotationAffinityCookieName)] = "INGRESSCOOKIE"
ing.SetAnnotations(data) ing.SetAnnotations(data)
affin, _ := NewParser(&resolver.Mock{}).Parse(ing) affin, _ := NewParser(&resolver.Mock{}).Parse(ing)

View file

@ -20,21 +20,18 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
type snippet struct { type snippet struct{}
r resolver.Resolver
}
// NewParser creates a new CORS annotation parser // NewParser creates a new snippet annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return snippet{r} return snippet{}
} }
// Parse parses the annotations contained in the ingress rule // Parse parses the annotations contained in the ingress rule
// used to indicate if the location/s contains a fragment of // used to indicate if the location/s contains a fragment of
// configuration to be included inside the paths of the rules // configuration to be included inside the paths of the rules
func (a snippet) Parse(ing *extensions.Ingress) (interface{}, error) { func (a snippet) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation("configuration-snippet", ing, a.r) return parser.GetStringAnnotation("configuration-snippet", ing)
} }

View file

@ -22,13 +22,13 @@ import (
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
annotation := "nginx/configuration-snippet" annotation := parser.GetAnnotationWithPrefix("configuration-snippet")
ap := NewParser(&resolver.Mock{}) ap := NewParser()
if ap == nil { if ap == nil {
t.Fatalf("expected a parser.IngressAnnotation but returned nil") t.Fatalf("expected a parser.IngressAnnotation but returned nil")
} }

View file

@ -21,16 +21,13 @@ import (
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
type sslpt struct { type sslpt struct{}
r resolver.Resolver
}
// NewParser creates a new SSL passthrough annotation parser // NewParser creates a new SSL passthrough annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return sslpt{r} return sslpt{}
} }
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
@ -40,5 +37,5 @@ func (a sslpt) Parse(ing *extensions.Ingress) (interface{}, error) {
return false, ing_errors.ErrMissingAnnotations return false, ing_errors.ErrMissingAnnotations
} }
return parser.GetBoolAnnotation("ssl-passthrough", ing, a.r) return parser.GetBoolAnnotation("ssl-passthrough", ing)
} }

View file

@ -22,7 +22,7 @@ import (
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
) )
@ -45,16 +45,16 @@ func buildIngress() *extensions.Ingress {
func TestParseAnnotations(t *testing.T) { func TestParseAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
_, err := NewParser(&resolver.Mock{}).Parse(ing) _, err := NewParser().Parse(ing)
if err == nil { if err == nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
data := map[string]string{} data := map[string]string{}
data["nginx/ssl-passthrough"] = "true" data[parser.GetAnnotationWithPrefix("ssl-passthrough")] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
// test ingress using the annotation without a TLS section // test ingress using the annotation without a TLS section
_, err = NewParser(&resolver.Mock{}).Parse(ing) _, err = NewParser().Parse(ing)
if err != nil { if err != nil {
t.Errorf("unexpected error parsing ingress with sslpassthrough") t.Errorf("unexpected error parsing ingress with sslpassthrough")
} }
@ -65,7 +65,7 @@ func TestParseAnnotations(t *testing.T) {
Hosts: []string{"foo.bar.com"}, Hosts: []string{"foo.bar.com"},
}, },
} }
i, err := NewParser(&resolver.Mock{}).Parse(ing) i, err := NewParser().Parse(ing)
if err != nil { if err != nil {
t.Errorf("expected error parsing ingress with sslpassthrough") t.Errorf("expected error parsing ingress with sslpassthrough")
} }

View file

@ -20,21 +20,18 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
type upstreamhashby struct { type upstreamhashby struct{}
r resolver.Resolver
}
// NewParser creates a new CORS annotation parser // NewParser creates a new CORS annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return upstreamhashby{r} return upstreamhashby{}
} }
// Parse parses the annotations contained in the ingress rule // Parse parses the annotations contained in the ingress rule
// used to indicate if the location/s contains a fragment of // used to indicate if the location/s contains a fragment of
// configuration to be included inside the paths of the rules // configuration to be included inside the paths of the rules
func (a upstreamhashby) Parse(ing *extensions.Ingress) (interface{}, error) { func (a upstreamhashby) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation("upstream-hash-by", ing, a.r) return parser.GetStringAnnotation("upstream-hash-by", ing)
} }

View file

@ -22,13 +22,13 @@ import (
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
annotation := "nginx/upstream-hash-by" annotation := parser.GetAnnotationWithPrefix("upstream-hash-by")
ap := NewParser(&resolver.Mock{}) ap := NewParser()
if ap == nil { if ap == nil {
t.Fatalf("expected a parser.IngressAnnotation but returned nil") t.Fatalf("expected a parser.IngressAnnotation but returned nil")
} }

View file

@ -20,21 +20,18 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
type upstreamVhost struct { type upstreamVhost struct{}
r resolver.Resolver
}
// NewParser creates a new upstream VHost annotation parser // NewParser creates a new upstream VHost annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return upstreamVhost{r} return upstreamVhost{}
} }
// Parse parses the annotations contained in the ingress rule // Parse parses the annotations contained in the ingress rule
// used to indicate if the location/s contains a fragment of // used to indicate if the location/s contains a fragment of
// configuration to be included inside the paths of the rules // configuration to be included inside the paths of the rules
func (a upstreamVhost) Parse(ing *extensions.Ingress) (interface{}, error) { func (a upstreamVhost) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation("upstream-vhost", ing, a.r) return parser.GetStringAnnotation("upstream-vhost", ing)
} }

View file

@ -20,21 +20,18 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
type vtsFilterKey struct { type vtsFilterKey struct{}
r resolver.Resolver
}
// NewParser creates a new vts filter key annotation parser // NewParser creates a new vts filter key annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation { func NewParser() parser.IngressAnnotation {
return vtsFilterKey{r} return vtsFilterKey{}
} }
// Parse parses the annotations contained in the ingress rule // (Parse parses the annotations contained in the ingress rule
// used toindicate if the location/s contains a fragment of // used toindicate if the location/s contains a fragment of
// configuration to be included inside the paths of the rules // configuration to be included inside the paths of the rules
func (a vtsFilterKey) Parse(ing *extensions.Ingress) (interface{}, error) { func (a vtsFilterKey) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation("vts-filter-key", ing, a.r) return parser.GetStringAnnotation("vts-filter-key", ing)
} }

View file

@ -28,7 +28,6 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress" "k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/net/ssl" "k8s.io/ingress-nginx/internal/net/ssl"
) )
@ -71,7 +70,7 @@ func (ic *NGINXController) syncSecret(key string) {
// getPemCertificate receives a secret, and creates a ingress.SSLCert as return. // getPemCertificate receives a secret, and creates a ingress.SSLCert as return.
// It parses the secret and verifies if it's a keypair, or a 'ca.crt' secret only. // It parses the secret and verifies if it's a keypair, or a 'ca.crt' secret only.
func (ic *NGINXController) getPemCertificate(secretName string) (*ingress.SSLCert, error) { func (ic *NGINXController) getPemCertificate(secretName string) (*ingress.SSLCert, error) {
secret, err := ic.listers.Secret.GetByName(secretName) secret, err := ic.storeLister.GetSecret(secretName)
if err != nil { if err != nil {
return nil, fmt.Errorf("error retrieving secret %v: %v", secretName, err) return nil, fmt.Errorf("error retrieving secret %v: %v", secretName, err)
} }
@ -169,13 +168,7 @@ func (ic *NGINXController) checkSSLChainIssues() {
// to a secret that is not present in the local secret store. // to a secret that is not present in the local secret store.
// In this case we call syncSecret. // In this case we call syncSecret.
func (ic *NGINXController) checkMissingSecrets() { func (ic *NGINXController) checkMissingSecrets() {
for _, obj := range ic.listers.Ingress.List() { for _, ing := range ic.storeLister.ListIngresses() {
ing := obj.(*extensions.Ingress)
if !class.IsValid(ing, ic.cfg.IngressClass, ic.cfg.DefaultIngressClass) {
continue
}
for _, tls := range ing.Spec.TLS { for _, tls := range ing.Spec.TLS {
if tls.SecretName == "" { if tls.SecretName == "" {
continue continue
@ -187,7 +180,7 @@ func (ic *NGINXController) checkMissingSecrets() {
} }
} }
key, _ := parser.GetStringAnnotation("auth-tls-secret", ing, ic) key, _ := parser.GetStringAnnotation("auth-tls-secret", ing)
if key == "" { if key == "" {
continue continue
} }

View file

@ -17,21 +17,8 @@ limitations under the License.
package controller package controller
import ( import (
"encoding/base64"
"fmt"
"io/ioutil"
"testing"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
testclient "k8s.io/client-go/kubernetes/fake" testclient "k8s.io/client-go/kubernetes/fake"
cache_client "k8s.io/client-go/tools/cache" cache_client "k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/store"
"k8s.io/ingress-nginx/internal/task"
"k8s.io/kubernetes/pkg/api"
) )
const ( const (
@ -66,6 +53,7 @@ func buildSimpleClientSetForBackendSSL() *testclient.Clientset {
return testclient.NewSimpleClientset() return testclient.NewSimpleClientset()
} }
/*
func buildIngListenerForBackendSSL() store.IngressLister { func buildIngListenerForBackendSSL() store.IngressLister {
ingLister := store.IngressLister{} ingLister := store.IngressLister{}
ingLister.Store = cache_client.NewStore(cache_client.DeletionHandlingMetaNamespaceKeyFunc) ingLister.Store = cache_client.NewStore(cache_client.DeletionHandlingMetaNamespaceKeyFunc)
@ -232,3 +220,4 @@ func TestGetPemCertificate(t *testing.T) {
}) })
} }
} }
*/

View file

@ -38,7 +38,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress" "k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations" "k8s.io/ingress-nginx/internal/ingress/annotations"
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
"k8s.io/ingress-nginx/internal/ingress/annotations/healthcheck" "k8s.io/ingress-nginx/internal/ingress/annotations/healthcheck"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/annotations/proxy" "k8s.io/ingress-nginx/internal/ingress/annotations/proxy"
@ -66,8 +65,6 @@ func init() {
// Configuration contains all the settings required by an Ingress controller // Configuration contains all the settings required by an Ingress controller
type Configuration struct { type Configuration struct {
AnnotationsPrefix string
APIServerHost string APIServerHost string
KubeConfigFile string KubeConfigFile string
Client clientset.Interface Client clientset.Interface
@ -76,7 +73,6 @@ type Configuration struct {
ConfigMapName string ConfigMapName string
DefaultService string DefaultService string
IngressClass string
Namespace string Namespace string
ForceNamespaceIsolation bool ForceNamespaceIsolation bool
@ -87,7 +83,6 @@ type Configuration struct {
UDPConfigMapName string UDPConfigMapName string
DefaultHealthzURL string DefaultHealthzURL string
DefaultIngressClass string
DefaultSSLCertificate string DefaultSSLCertificate string
// optional // optional
@ -119,7 +114,7 @@ func (n NGINXController) GetDefaultBackend() defaults.Backend {
// GetPublishService returns the configured service used to set ingress status // GetPublishService returns the configured service used to set ingress status
func (n NGINXController) GetPublishService() *apiv1.Service { func (n NGINXController) GetPublishService() *apiv1.Service {
s, err := n.listers.Service.GetByName(n.cfg.PublishService) s, err := n.storeLister.GetService(n.cfg.PublishService)
if err != nil { if err != nil {
return nil return nil
} }
@ -129,17 +124,12 @@ func (n NGINXController) GetPublishService() *apiv1.Service {
// GetSecret searches for a secret in the local secrets Store // GetSecret searches for a secret in the local secrets Store
func (n NGINXController) GetSecret(name string) (*apiv1.Secret, error) { func (n NGINXController) GetSecret(name string) (*apiv1.Secret, error) {
return n.listers.Secret.GetByName(name) return n.storeLister.GetSecret(name)
} }
// GetService searches for a service in the local secrets Store // GetService searches for a service in the local secrets Store
func (n NGINXController) GetService(name string) (*apiv1.Service, error) { func (n NGINXController) GetService(name string) (*apiv1.Service, error) {
return n.listers.Service.GetByName(name) return n.storeLister.GetService(name)
}
// GetAnnotationWithPrefix returns the prefix of ingress annotations
func (n NGINXController) GetAnnotationWithPrefix(suffix string) string {
return fmt.Sprintf("%v/%v", n.cfg.AnnotationsPrefix, suffix)
} }
// sync collects all the pieces required to assemble the configuration file and // sync collects all the pieces required to assemble the configuration file and
@ -154,32 +144,20 @@ func (n *NGINXController) syncIngress(item interface{}) error {
if element, ok := item.(task.Element); ok { if element, ok := item.(task.Element); ok {
if name, ok := element.Key.(string); ok { if name, ok := element.Key.(string); ok {
if obj, exists, _ := n.listers.Ingress.GetByKey(name); exists { if ing, err := n.storeLister.GetIngress(name); err == nil {
ing := obj.(*extensions.Ingress)
n.readSecrets(ing) n.readSecrets(ing)
} }
} }
} }
// Sort ingress rules using the ResourceVersion field // Sort ingress rules using the ResourceVersion field
ings := n.listers.Ingress.List() ingresses := n.storeLister.ListIngresses()
sort.SliceStable(ings, func(i, j int) bool { sort.SliceStable(ingresses, func(i, j int) bool {
ir := ings[i].(*extensions.Ingress).ResourceVersion ir := ingresses[i].ResourceVersion
jr := ings[j].(*extensions.Ingress).ResourceVersion jr := ingresses[j].ResourceVersion
return ir < jr return ir < jr
}) })
// filter ingress rules
var ingresses []*extensions.Ingress
for _, ingIf := range ings {
ing := ingIf.(*extensions.Ingress)
if !class.IsValid(ing, n.cfg.IngressClass, n.cfg.DefaultIngressClass) {
continue
}
ingresses = append(ingresses, ing)
}
upstreams, servers := n.getBackendServers(ingresses) upstreams, servers := n.getBackendServers(ingresses)
var passUpstreams []*ingress.SSLPassthroughBackend var passUpstreams []*ingress.SSLPassthroughBackend
@ -248,7 +226,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
return []ingress.L4Service{} return []ingress.L4Service{}
} }
configmap, err := n.listers.ConfigMap.GetByName(configmapName) configmap, err := n.storeLister.GetConfigMap(configmapName)
if err != nil { if err != nil {
glog.Errorf("unexpected error reading configmap %v: %v", configmapName, err) glog.Errorf("unexpected error reading configmap %v: %v", configmapName, err)
return []ingress.L4Service{} return []ingress.L4Service{}
@ -306,19 +284,12 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
continue continue
} }
svcObj, svcExists, err := n.listers.Service.GetByKey(nsName) svc, err := n.storeLister.GetService(nsName)
if err != nil { if err != nil {
glog.Warningf("error getting service %v: %v", nsName, err) glog.Warningf("error getting service %v: %v", nsName, err)
continue continue
} }
if !svcExists {
glog.Warningf("service %v was not found", nsName)
continue
}
svc := svcObj.(*apiv1.Service)
var endps []ingress.Endpoint var endps []ingress.Endpoint
targetPort, err := strconv.Atoi(svcPort) targetPort, err := strconv.Atoi(svcPort)
if err != nil { if err != nil {
@ -375,20 +346,13 @@ func (n *NGINXController) getDefaultUpstream() *ingress.Backend {
Name: defUpstreamName, Name: defUpstreamName,
} }
svcKey := n.cfg.DefaultService svcKey := n.cfg.DefaultService
svcObj, svcExists, err := n.listers.Service.GetByKey(svcKey) svc, err := n.storeLister.GetService(svcKey)
if err != nil { if err != nil {
glog.Warningf("unexpected error searching the default backend %v: %v", n.cfg.DefaultService, err) glog.Warningf("unexpected error searching the default backend %v: %v", n.cfg.DefaultService, err)
upstream.Endpoints = append(upstream.Endpoints, n.DefaultEndpoint()) upstream.Endpoints = append(upstream.Endpoints, n.DefaultEndpoint())
return upstream return upstream
} }
if !svcExists {
glog.Warningf("service %v does not exist", svcKey)
upstream.Endpoints = append(upstream.Endpoints, n.DefaultEndpoint())
return upstream
}
svc := svcObj.(*apiv1.Service)
endps := n.getEndpoints(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, &healthcheck.Config{}) endps := n.getEndpoints(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, &healthcheck.Config{})
if len(endps) == 0 { if len(endps) == 0 {
glog.Warningf("service %v does not have any active endpoints", svcKey) glog.Warningf("service %v does not have any active endpoints", svcKey)
@ -626,16 +590,16 @@ func (n NGINXController) GetAuthCertificate(name string) (*resolver.AuthSSLCert,
n.syncSecret(name) n.syncSecret(name)
} }
_, err := n.listers.Secret.GetByName(name) _, err := n.storeLister.GetSecret(name)
if err != nil { if err != nil {
return &resolver.AuthSSLCert{}, fmt.Errorf("unexpected error: %v", err) return &resolver.AuthSSLCert{}, fmt.Errorf("unexpected error: %v", err)
} }
bc, exists := n.sslCertTracker.Get(name) cert, err := n.storeLister.GetLocalSecret(name)
if !exists { if err != nil {
return &resolver.AuthSSLCert{}, fmt.Errorf("secret %v does not exist", name) return &resolver.AuthSSLCert{}, fmt.Errorf("secret %v does not exist", name)
} }
cert := bc.(*ingress.SSLCert)
return &resolver.AuthSSLCert{ return &resolver.AuthSSLCert{
Secret: name, Secret: name,
CAFileName: cert.CAFileName, CAFileName: cert.CAFileName,
@ -737,13 +701,13 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
upstreams[name].Endpoints = endp upstreams[name].Endpoints = endp
} }
s, err := n.listers.Service.GetByName(svcKey) svc, err := n.storeLister.GetService(svcKey)
if err != nil { if err != nil {
glog.Warningf("error obtaining service: %v", err) glog.Warningf("error obtaining service: %v", err)
continue continue
} }
upstreams[name].Service = s upstreams[name].Service = svc
} }
} }
} }
@ -752,13 +716,11 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
} }
func (n *NGINXController) getServiceClusterEndpoint(svcKey string, backend *extensions.IngressBackend) (endpoint ingress.Endpoint, err error) { func (n *NGINXController) getServiceClusterEndpoint(svcKey string, backend *extensions.IngressBackend) (endpoint ingress.Endpoint, err error) {
svcObj, svcExists, err := n.listers.Service.GetByKey(svcKey) svc, err := n.storeLister.GetService(svcKey)
if err != nil {
if !svcExists { return endpoint, err
return endpoint, fmt.Errorf("service %v does not exist", svcKey)
} }
svc := svcObj.(*apiv1.Service)
if svc.Spec.ClusterIP == "" || svc.Spec.ClusterIP == "None" { if svc.Spec.ClusterIP == "" || svc.Spec.ClusterIP == "None" {
return endpoint, fmt.Errorf("No ClusterIP found for service %s", svcKey) return endpoint, fmt.Errorf("No ClusterIP found for service %s", svcKey)
} }
@ -790,7 +752,7 @@ func (n *NGINXController) getServiceClusterEndpoint(svcKey string, backend *exte
// to a service. // to a service.
func (n *NGINXController) serviceEndpoints(svcKey, backendPort string, func (n *NGINXController) serviceEndpoints(svcKey, backendPort string,
hz *healthcheck.Config) ([]ingress.Endpoint, error) { hz *healthcheck.Config) ([]ingress.Endpoint, error) {
svc, err := n.listers.Service.GetByName(svcKey) svc, err := n.storeLister.GetService(svcKey)
var upstreams []ingress.Endpoint var upstreams []ingress.Endpoint
if err != nil { if err != nil {
@ -1107,7 +1069,7 @@ func (n *NGINXController) getEndpoints(
} }
glog.V(3).Infof("getting endpoints for service %v/%v and port %v", s.Namespace, s.Name, servicePort.String()) glog.V(3).Infof("getting endpoints for service %v/%v and port %v", s.Namespace, s.Name, servicePort.String())
ep, err := n.listers.Endpoint.GetServiceEndpoints(s) ep, err := n.storeLister.GetServiceEndpoints(s)
if err != nil { if err != nil {
glog.Warningf("unexpected error obtaining service endpoints: %v", err) glog.Warningf("unexpected error obtaining service endpoints: %v", err)
return upsServers return upsServers
@ -1167,7 +1129,7 @@ func (n *NGINXController) readSecrets(ing *extensions.Ingress) {
n.syncSecret(key) n.syncSecret(key)
} }
key, _ := parser.GetStringAnnotation("auth-tls-secret", ing, n) key, _ := parser.GetStringAnnotation("auth-tls-secret", ing)
if key == "" { if key == "" {
return return
} }
@ -1191,20 +1153,17 @@ func (n *NGINXController) SetForceReload(shouldReload bool) {
func (n *NGINXController) extractAnnotations(ing *extensions.Ingress) { func (n *NGINXController) extractAnnotations(ing *extensions.Ingress) {
anns := n.annotations.Extract(ing) anns := n.annotations.Extract(ing)
glog.V(3).Infof("updating annotations information for ingres %v/%v", anns.Namespace, anns.Name) glog.V(3).Infof("updating annotations information for ingres %v/%v", anns.Namespace, anns.Name)
n.listers.IngressAnnotation.Update(anns) // n.storeLister.UpdateIngressAnnotations(ing, anns)
} }
// getByIngress returns the parsed annotations from an Ingress // getByIngress returns the parsed annotations from an Ingress
func (n *NGINXController) getIngressAnnotations(ing *extensions.Ingress) *annotations.Ingress { func (n *NGINXController) getIngressAnnotations(ing *extensions.Ingress) *annotations.Ingress {
key := fmt.Sprintf("%v/%v", ing.Namespace, ing.Name) key := fmt.Sprintf("%v/%v", ing.Namespace, ing.Name)
item, exists, err := n.listers.IngressAnnotation.GetByKey(key) item, err := n.storeLister.GetIngressAnnotations(ing)
if err != nil { if err != nil {
glog.Errorf("unexpected error getting ingress annotation %v: %v", key, err) glog.Errorf("unexpected error getting ingress annotation %v: %v", key, err)
return &annotations.Ingress{} return &annotations.Ingress{}
} }
if !exists {
glog.Errorf("ingress annotation %v was not found", key) return item
return &annotations.Ingress{}
}
return item.(*annotations.Ingress)
} }

View file

@ -1,228 +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 (
"fmt"
"reflect"
"github.com/golang/glog"
apiv1 "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
"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/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
)
type cacheController struct {
Ingress cache.Controller
Endpoint cache.Controller
Service cache.Controller
Secret cache.Controller
Configmap cache.Controller
}
func (c *cacheController) Run(stopCh chan struct{}) {
go c.Ingress.Run(stopCh)
go c.Endpoint.Run(stopCh)
go c.Service.Run(stopCh)
go c.Secret.Run(stopCh)
go c.Configmap.Run(stopCh)
// Wait for all involved caches to be synced, before processing items from the queue is started
if !cache.WaitForCacheSync(stopCh,
c.Ingress.HasSynced,
c.Endpoint.HasSynced,
c.Service.HasSynced,
c.Secret.HasSynced,
c.Configmap.HasSynced,
) {
runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
}
}
func (n *NGINXController) createListers(stopCh chan struct{}) (*ingress.StoreLister, *cacheController) {
ingEventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
addIng := obj.(*extensions.Ingress)
if !class.IsValid(addIng, n.cfg.IngressClass, defIngressClass) {
a, _ := parser.GetStringAnnotation(class.IngressKey, addIng, n)
glog.Infof("ignoring add for ingress %v based on annotation %v with value %v", addIng.Name, class.IngressKey, a)
return
}
n.extractAnnotations(addIng)
n.recorder.Eventf(addIng, apiv1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", addIng.Namespace, addIng.Name))
n.syncQueue.Enqueue(obj)
},
DeleteFunc: func(obj interface{}) {
delIng, ok := obj.(*extensions.Ingress)
if !ok {
// If we reached here it means the ingress was deleted but its final state is unrecorded.
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
glog.Errorf("couldn't get object from tombstone %#v", obj)
return
}
delIng, ok = tombstone.Obj.(*extensions.Ingress)
if !ok {
glog.Errorf("Tombstone contained object that is not an Ingress: %#v", obj)
return
}
}
if !class.IsValid(delIng, n.cfg.IngressClass, defIngressClass) {
glog.Infof("ignoring delete for ingress %v based on annotation %v", delIng.Name, class.IngressKey)
return
}
n.recorder.Eventf(delIng, apiv1.EventTypeNormal, "DELETE", fmt.Sprintf("Ingress %s/%s", delIng.Namespace, delIng.Name))
n.listers.IngressAnnotation.Delete(delIng)
n.syncQueue.Enqueue(obj)
},
UpdateFunc: func(old, cur interface{}) {
oldIng := old.(*extensions.Ingress)
curIng := cur.(*extensions.Ingress)
validOld := class.IsValid(oldIng, n.cfg.IngressClass, defIngressClass)
validCur := class.IsValid(curIng, n.cfg.IngressClass, defIngressClass)
if !validOld && validCur {
glog.Infof("creating ingress %v based on annotation %v", curIng.Name, class.IngressKey)
n.recorder.Eventf(curIng, apiv1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
} else if validOld && !validCur {
glog.Infof("removing ingress %v based on annotation %v", curIng.Name, class.IngressKey)
n.recorder.Eventf(curIng, apiv1.EventTypeNormal, "DELETE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
} else if validCur && !reflect.DeepEqual(old, cur) {
n.recorder.Eventf(curIng, apiv1.EventTypeNormal, "UPDATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
}
n.extractAnnotations(curIng)
n.syncQueue.Enqueue(cur)
},
}
secrEventHandler := cache.ResourceEventHandlerFuncs{
UpdateFunc: func(old, cur interface{}) {
if !reflect.DeepEqual(old, cur) {
sec := cur.(*apiv1.Secret)
key := fmt.Sprintf("%v/%v", sec.Namespace, sec.Name)
_, exists := n.sslCertTracker.Get(key)
if exists {
n.syncSecret(key)
}
}
},
DeleteFunc: func(obj interface{}) {
sec, ok := obj.(*apiv1.Secret)
if !ok {
// If we reached here it means the secret was deleted but its final state is unrecorded.
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
glog.Errorf("couldn't get object from tombstone %#v", obj)
return
}
sec, ok = tombstone.Obj.(*apiv1.Secret)
if !ok {
glog.Errorf("Tombstone contained object that is not a Secret: %#v", obj)
return
}
}
key := fmt.Sprintf("%v/%v", sec.Namespace, sec.Name)
n.sslCertTracker.Delete(key)
n.syncQueue.Enqueue(key)
},
}
eventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
n.syncQueue.Enqueue(obj)
},
DeleteFunc: func(obj interface{}) {
n.syncQueue.Enqueue(obj)
},
UpdateFunc: func(old, cur interface{}) {
oep := old.(*apiv1.Endpoints)
ocur := cur.(*apiv1.Endpoints)
if !reflect.DeepEqual(ocur.Subsets, oep.Subsets) {
n.syncQueue.Enqueue(cur)
}
},
}
mapEventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
upCmap := obj.(*apiv1.ConfigMap)
mapKey := fmt.Sprintf("%s/%s", upCmap.Namespace, upCmap.Name)
if mapKey == n.cfg.ConfigMapName {
glog.V(2).Infof("adding configmap %v to backend", mapKey)
n.SetConfig(upCmap)
n.SetForceReload(true)
}
},
UpdateFunc: func(old, cur interface{}) {
if !reflect.DeepEqual(old, cur) {
upCmap := cur.(*apiv1.ConfigMap)
mapKey := fmt.Sprintf("%s/%s", upCmap.Namespace, upCmap.Name)
if mapKey == n.cfg.ConfigMapName {
glog.V(2).Infof("updating configmap backend (%v)", mapKey)
n.SetConfig(upCmap)
n.SetForceReload(true)
}
// updates to configuration configmaps can trigger an update
if mapKey == n.cfg.ConfigMapName || mapKey == n.cfg.TCPConfigMapName || mapKey == n.cfg.UDPConfigMapName {
n.recorder.Eventf(upCmap, apiv1.EventTypeNormal, "UPDATE", fmt.Sprintf("ConfigMap %v", mapKey))
n.syncQueue.Enqueue(cur)
}
}
},
}
watchNs := apiv1.NamespaceAll
if n.cfg.ForceNamespaceIsolation && n.cfg.Namespace != apiv1.NamespaceAll {
watchNs = n.cfg.Namespace
}
lister := &ingress.StoreLister{}
lister.IngressAnnotation.Store = cache_client.NewStore(cache_client.DeletionHandlingMetaNamespaceKeyFunc)
controller := &cacheController{}
lister.Ingress.Store, controller.Ingress = cache.NewInformer(
cache.NewListWatchFromClient(n.cfg.Client.ExtensionsV1beta1().RESTClient(), "ingresses", n.cfg.Namespace, fields.Everything()),
&extensions.Ingress{}, n.cfg.ResyncPeriod, ingEventHandler)
lister.Endpoint.Store, controller.Endpoint = cache.NewInformer(
cache.NewListWatchFromClient(n.cfg.Client.CoreV1().RESTClient(), "endpoints", n.cfg.Namespace, fields.Everything()),
&apiv1.Endpoints{}, n.cfg.ResyncPeriod, eventHandler)
lister.Secret.Store, controller.Secret = cache.NewInformer(
cache.NewListWatchFromClient(n.cfg.Client.CoreV1().RESTClient(), "secrets", watchNs, fields.Everything()),
&apiv1.Secret{}, n.cfg.ResyncPeriod, secrEventHandler)
lister.ConfigMap.Store, controller.Configmap = cache.NewInformer(
cache.NewListWatchFromClient(n.cfg.Client.CoreV1().RESTClient(), "configmaps", watchNs, fields.Everything()),
&apiv1.ConfigMap{}, n.cfg.ResyncPeriod, mapEventHandler)
lister.Service.Store, controller.Service = cache.NewInformer(
cache.NewListWatchFromClient(n.cfg.Client.CoreV1().RESTClient(), "services", n.cfg.Namespace, fields.Everything()),
&apiv1.Service{}, n.cfg.ResyncPeriod, cache.ResourceEventHandlerFuncs{})
return lister, controller
}

View file

@ -46,7 +46,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress" "k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations" "k8s.io/ingress-nginx/internal/ingress/annotations"
"k8s.io/ingress-nginx/internal/ingress/annotations/class" "k8s.io/ingress-nginx/internal/ingress/annotations/class"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config" ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
"k8s.io/ingress-nginx/internal/ingress/controller/process" "k8s.io/ingress-nginx/internal/ingress/controller/process"
ngx_template "k8s.io/ingress-nginx/internal/ingress/controller/template" ngx_template "k8s.io/ingress-nginx/internal/ingress/controller/template"
@ -113,27 +112,37 @@ func NewNGINXController(config *Configuration) *NGINXController {
}), }),
stopCh: make(chan struct{}), stopCh: make(chan struct{}),
updateCh: make(chan store.Event),
stopLock: &sync.Mutex{}, stopLock: &sync.Mutex{},
fileSystem: filesystem.DefaultFs{}, fileSystem: filesystem.DefaultFs{},
} }
n.listers, n.controllers = n.createListers(n.stopCh) n.stats = newStatsCollector(config.Namespace, class.IngressClass, n.binary, n.cfg.ListenPorts.Status)
n.stats = newStatsCollector(config.Namespace, config.IngressClass, n.binary, n.cfg.ListenPorts.Status)
n.syncQueue = task.NewTaskQueue(n.syncIngress) n.syncQueue = task.NewTaskQueue(n.syncIngress)
n.annotations = annotations.NewAnnotationExtractor(n) n.annotations = annotations.NewAnnotationExtractor(n)
n.storeLister = store.New(
n.cfg.Namespace,
n.cfg.ConfigMapName,
n.cfg.TCPConfigMapName,
n.cfg.UDPConfigMapName,
n.cfg.ResyncPeriod,
n.recorder,
n.cfg.Client,
n.annotations,
n.updateCh,
)
if config.UpdateStatus { if config.UpdateStatus {
n.syncStatus = status.NewStatusSyncer(status.Config{ n.syncStatus = status.NewStatusSyncer(status.Config{
Client: config.Client, Client: config.Client,
PublishService: config.PublishService, PublishService: config.PublishService,
IngressLister: n.listers.Ingress, // IngressLister: n.listers.Ingress,
ElectionID: config.ElectionID, ElectionID: config.ElectionID,
IngressClass: config.IngressClass,
DefaultIngressClass: config.DefaultIngressClass,
UpdateStatusOnShutdown: config.UpdateStatusOnShutdown, UpdateStatusOnShutdown: config.UpdateStatusOnShutdown,
UseNodeInternalIP: config.UseNodeInternalIP, UseNodeInternalIP: config.UseNodeInternalIP,
}) })
@ -174,9 +183,6 @@ Error loading new template : %v
type NGINXController struct { type NGINXController struct {
cfg *Configuration cfg *Configuration
listers *ingress.StoreLister
controllers *cacheController
annotations annotations.Extractor annotations annotations.Extractor
recorder record.EventRecorder recorder record.EventRecorder
@ -197,6 +203,7 @@ type NGINXController struct {
stopLock *sync.Mutex stopLock *sync.Mutex
stopCh chan struct{} stopCh chan struct{}
updateCh chan store.Event
// ngxErrCh channel used to detect errors with the nginx processes // ngxErrCh channel used to detect errors with the nginx processes
ngxErrCh chan error ngxErrCh chan error
@ -210,7 +217,7 @@ type NGINXController struct {
configmap *apiv1.ConfigMap configmap *apiv1.ConfigMap
storeLister *ingress.StoreLister storeLister store.Storer
binary string binary string
resolver []net.IP resolver []net.IP
@ -239,19 +246,11 @@ type NGINXController struct {
func (n *NGINXController) Start() { func (n *NGINXController) Start() {
glog.Infof("starting Ingress controller") glog.Infof("starting Ingress controller")
n.controllers.Run(n.stopCh) n.storeLister.Run(n.stopCh)
// initial sync of secrets to avoid unnecessary reloads // initial sync of secrets to avoid unnecessary reloads
glog.Info("running initial sync of secrets") glog.Info("running initial sync of secrets")
for _, obj := range n.listers.Ingress.List() { for _, ing := range n.storeLister.ListIngresses() {
ing := obj.(*extensions.Ingress)
if !class.IsValid(ing, n.cfg.IngressClass, n.cfg.DefaultIngressClass) {
a, _ := parser.GetStringAnnotation(class.IngressKey, ing, n)
glog.Infof("ignoring add for ingress %v based on annotation %v with value %v", ing.Name, class.IngressKey, a)
continue
}
n.readSecrets(ing) n.readSecrets(ing)
} }
@ -555,38 +554,33 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
setHeaders := map[string]string{} setHeaders := map[string]string{}
if cfg.ProxySetHeaders != "" { if cfg.ProxySetHeaders != "" {
cmap, exists, err := n.storeLister.ConfigMap.GetByKey(cfg.ProxySetHeaders) cmap, err := n.storeLister.GetConfigMap(cfg.ProxySetHeaders)
if err != nil { if err == nil {
glog.Warningf("unexpected error reading configmap %v: %v", cfg.ProxySetHeaders, err) setHeaders = cmap.Data
} } else {
glog.Warningf("unexpected error reading configmap %v: %v", cfg.AddHeaders, err)
if exists {
setHeaders = cmap.(*apiv1.ConfigMap).Data
} }
} }
addHeaders := map[string]string{} addHeaders := map[string]string{}
if cfg.AddHeaders != "" { if cfg.AddHeaders != "" {
cmap, exists, err := n.storeLister.ConfigMap.GetByKey(cfg.AddHeaders) cmap, err := n.storeLister.GetConfigMap(cfg.AddHeaders)
if err != nil { if err == nil {
addHeaders = cmap.Data
} else {
glog.Warningf("unexpected error reading configmap %v: %v", cfg.AddHeaders, err) glog.Warningf("unexpected error reading configmap %v: %v", cfg.AddHeaders, err)
} }
if exists {
addHeaders = cmap.(*apiv1.ConfigMap).Data
}
} }
sslDHParam := "" sslDHParam := ""
if cfg.SSLDHParam != "" { if cfg.SSLDHParam != "" {
secretName := cfg.SSLDHParam secretName := cfg.SSLDHParam
s, exists, err := n.storeLister.Secret.GetByKey(secretName) secret, err := n.storeLister.GetSecret(secretName)
if err != nil { if err != nil {
glog.Warningf("unexpected error reading secret %v: %v", secretName, err) glog.Warningf("unexpected error reading secret %v: %v", secretName, err)
} }
if exists { if secret != nil {
secret := s.(*apiv1.Secret)
nsSecName := strings.Replace(secretName, "/", "-", -1) nsSecName := strings.Replace(secretName, "/", "-", -1)
dh, ok := secret.Data["dhparam.pem"] dh, ok := secret.Data["dhparam.pem"]

View file

@ -37,9 +37,6 @@ type Resolver interface {
// GetService searches for services contenating the namespace and name using a the character / // GetService searches for services contenating the namespace and name using a the character /
GetService(string) (*apiv1.Service, error) GetService(string) (*apiv1.Service, error)
// GetAnnotationWithPrefix returns the prefix of the Ingress annotations
GetAnnotationWithPrefix(suffix string) string
} }
// AuthSSLCert contains the necessary information to do certificate based // AuthSSLCert contains the necessary information to do certificate based

View file

@ -314,7 +314,7 @@ func (s *statusSync) updateStatus(newIngressPoint []apiv1.LoadBalancerIngress) {
for _, cur := range ings { for _, cur := range ings {
ing := cur.(*extensions.Ingress) ing := cur.(*extensions.Ingress)
if !class.IsValid(ing, s.Config.IngressClass, s.Config.DefaultIngressClass) { if !class.IsValid(ing) {
continue continue
} }

View file

@ -0,0 +1,217 @@
/*
Copyright 2015 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 store
import (
"fmt"
"io/ioutil"
"strings"
"github.com/golang/glog"
"github.com/imdario/mergo"
apiv1 "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/net/ssl"
)
// syncSecret keeps in sync Secrets used by Ingress rules with the files on
// disk to allow copy of the content of the secret to disk to be used
// by external processes.
func (s k8sStore) syncSecret(key string) {
glog.V(3).Infof("starting syncing of secret %v", key)
// cert
_, err := s.getPemCertificate(key)
if err != nil {
glog.Warningf("error obtaining PEM from secret %v: %v", key, err)
return
}
/*
// create certificates and add or update the item in the store
cur, exists := s.GetSecret(key)
if exists {
s := cur.(*ingress.SSLCert)
if s.Equal(cert) {
// no need to update
return
}
glog.Infof("updating secret %v in the local store", key)
ic.store.UpdateLocalSecret(key, cert)
// this update must trigger an update
// (like an update event from a change in Ingress)
ic.syncQueue.Enqueue(&extensions.Ingress{})
return
}
glog.Infof("adding secret %v to the local store", key)
ic.store.AddLocalSecret(key, cert)
// this update must trigger an update
// (like an update event from a change in Ingress)
ic.syncQueue.Enqueue(&extensions.Ingress{})*/
}
// getPemCertificate receives a secret, and creates a ingress.SSLCert as return.
// It parses the secret and verifies if it's a keypair, or a 'ca.crt' secret only.
func (s k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error) {
secret, err := s.listers.Secret.ByKey(secretName)
if err != nil {
return nil, fmt.Errorf("error retrieving secret %v: %v", secretName, err)
}
cert, okcert := secret.Data[apiv1.TLSCertKey]
key, okkey := secret.Data[apiv1.TLSPrivateKeyKey]
ca := secret.Data["ca.crt"]
// namespace/secretName -> namespace-secretName
nsSecName := strings.Replace(secretName, "/", "-", -1)
var sslCert *ingress.SSLCert
if okcert && okkey {
if cert == nil {
return nil, fmt.Errorf("secret %v has no 'tls.crt'", secretName)
}
if key == nil {
return nil, fmt.Errorf("secret %v has no 'tls.key'", secretName)
}
// If 'ca.crt' is also present, it will allow this secret to be used in the
// 'nginx.ingress.kubernetes.io/auth-tls-secret' annotation
sslCert, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca)
if err != nil {
return nil, fmt.Errorf("unexpected error creating pem file: %v", err)
}
/*
glog.V(3).Infof("found 'tls.crt' and 'tls.key', configuring %v as a TLS Secret (CN: %v)", secretName, s.CN)
if ca != nil {
glog.V(3).Infof("found 'ca.crt', secret %v can also be used for Certificate Authentication", secretName)
}
*/
} else if ca != nil {
sslCert, err = ssl.AddCertAuth(nsSecName, ca)
if err != nil {
return nil, fmt.Errorf("unexpected error creating pem file: %v", err)
}
// makes this secret in 'syncSecret' to be used for Certificate Authentication
// this does not enable Certificate Authentication
glog.V(3).Infof("found only 'ca.crt', configuring %v as an Certificate Authentication Secret", secretName)
} else {
return nil, fmt.Errorf("no keypair or CA cert could be found in %v", secretName)
}
sslCert.Name = secret.Name
sslCert.Namespace = secret.Namespace
return sslCert, nil
}
func (s k8sStore) checkSSLChainIssues() {
for _, item := range s.ListLocalSecrets() {
secretName := fmt.Sprintf("%v/%v", item.Namespace, item.Name)
secret, err := s.GetLocalSecret(secretName)
if err != nil {
continue
}
if secret.FullChainPemFileName != "" {
// chain already checked
continue
}
data, err := ssl.FullChainCert(secret.PemFileName)
if err != nil {
glog.Errorf("unexpected error generating SSL certificate with full intermediate chain CA certs: %v", err)
continue
}
fullChainPemFileName := fmt.Sprintf("%v/%v-%v-full-chain.pem", ingress.DefaultSSLDirectory, secret.Namespace, secret.Name)
err = ioutil.WriteFile(fullChainPemFileName, data, 0655)
if err != nil {
glog.Errorf("unexpected error creating SSL certificate: %v", err)
continue
}
dst := &ingress.SSLCert{}
err = mergo.MergeWithOverwrite(dst, secret)
if err != nil {
glog.Errorf("unexpected error creating SSL certificate: %v", err)
continue
}
dst.FullChainPemFileName = fullChainPemFileName
glog.Infof("updating local copy of ssl certificate %v with missing intermediate CA certs", secretName)
s.sslStore.Update(secretName, dst)
// this update must trigger an update
// (like an update event from a change in Ingress)
//ic.syncQueue.Enqueue(&extensions.Ingress{})
}
}
// checkMissingSecrets verify if one or more ingress rules contains a reference
// to a secret that is not present in the local secret store.
// In this case we call syncSecret.
func (s k8sStore) checkMissingSecrets() {
for _, ing := range s.ListIngresses() {
for _, tls := range ing.Spec.TLS {
if tls.SecretName == "" {
continue
}
key := fmt.Sprintf("%v/%v", ing.Namespace, tls.SecretName)
if _, ok := s.sslStore.Get(key); !ok {
s.syncSecret(key)
}
}
/*
key, _ := parser.GetStringAnnotation("auth-tls-secret", ing, resolver)
if key == "" {
continue
}
if _, ok := ic.sslCertTracker.Get(key); !ok {
ic.syncSecret(key)
}*/
}
}
// readSecrets extracts information about secrets from an Ingress rule
func (s k8sStore) readSecrets(ing *extensions.Ingress) {
for _, tls := range ing.Spec.TLS {
if tls.SecretName == "" {
continue
}
key := fmt.Sprintf("%v/%v", ing.Namespace, tls.SecretName)
s.syncSecret(key)
}
key, _ := parser.GetStringAnnotation("auth-tls-secret", ing)
if key == "" {
return
}
s.syncSecret(key)
}

View file

@ -0,0 +1,234 @@
/*
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 store
import (
"encoding/base64"
"fmt"
"io/ioutil"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
testclient "k8s.io/client-go/kubernetes/fake"
cache_client "k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/pkg/api"
"k8s.io/ingress-nginx/internal/ingress"
)
const (
// openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
tlsCrt = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURIekNDQWdlZ0F3SUJBZ0lKQU1KZld6Mm81cWVnTUEwR0NTcUdTSWIzRFFFQkN3VUFNQ1l4RVRBUEJnTlYKQkFNTUNHNW5hVzU0YzNaak1SRXdEd1lEVlFRS0RBaHVaMmx1ZUhOMll6QWVGdzB4TnpBME1URXdNakF3TlRCYQpGdzB5TnpBME1Ea3dNakF3TlRCYU1DWXhFVEFQQmdOVkJBTU1DRzVuYVc1NGMzWmpNUkV3RHdZRFZRUUtEQWh1CloybHVlSE4yWXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUgzVTYvY3ArODAKU3hJRjltSnlUcGI5RzBodnhsM0JMaGdQWDBTWjZ3d1lISGJXeTh2dmlCZjVwWTdvVHd0b2FPaTN1VFNsL2RtVwpvUi9XNm9GVWM5a2l6NlNXc3p6YWRXL2l2Q21LMmxOZUFVc2gvaXY0aTAvNXlreDJRNXZUT2tVL1dra2JPOW1OCjdSVTF0QW1KT3M0T1BVc3hZZkw2cnJJUzZPYktHS2UvYUVkek9QS2NPMDJ5NUxDeHM0TFhhWDIzU1l6TG1XYVAKYVZBallrN1NRZm1xUm5mYlF4RWlpaDFQWTFRRXgxWWs0RzA0VmtHUitrSVVMaWF0L291ZjQxY0dXRTZHMTF4NQpkV1BHeS9XcGtqRGlaM0UwekdNZnJBVUZibnErN1dhRTJCRzVoUVV3ZG9SQUtWTnMzaVhLRlRkT3hoRll5bnBwCjA3cDJVNS96ZHRrQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZCL2U5UnVna0Mwc0VNTTZ6enRCSjI1U1JxalMKTUI4R0ExVWRJd1FZTUJhQUZCL2U5UnVna0Mwc0VNTTZ6enRCSjI1U1JxalNNQXdHQTFVZEV3UUZNQU1CQWY4dwpEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRys4MXdaSXRuMmFWSlFnejNkNmJvZW1nUXhSSHpaZDhNc1IrdFRvCnpJLy9ac1Nwc2FDR3F0TkdTaHVGKzB3TVZ4NjlpQ3lJTnJJb2J4K29NTHBsQzFQSk9uektSUUdvZEhYNFZaSUwKVlhxSFd2VStjK3ZtT0QxUEt3UjcwRi9rTXk2Yk4xMVI2amhIZ3RPZGdLKzdRczhRMVlUSC9RS2dMd3RJTFRHRwpTZlYxWFlmbnF1TXlZKzFzck00U3ZRSmRzdmFUQmJkZHE2RllpdjhXZFpIaG51ZGlSODdZcFgzOUlTSlFkOXF2CnR6OGthZTVqQVFEUWFiZnFsVWZNT1hmUnhyei96S2NvN3dMeWFMWTh1eVhEWUVIZmlHRWdablV0RjgxVlhDZUIKeU80UERBR0FuVmlXTndFM0NZcGI4RkNGelMyaVVVMDJaQWJRajlvUnYyUWNON1E9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
tlsKey = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRREI5MU92M0tmdk5Fc1MKQmZaaWNrNlcvUnRJYjhaZHdTNFlEMTlFbWVzTUdCeDIxc3ZMNzRnWCthV082RThMYUdqb3Q3azBwZjNabHFFZgoxdXFCVkhQWklzK2tsck04Mm5WdjRyd3BpdHBUWGdGTElmNHIrSXRQK2NwTWRrT2IwenBGUDFwSkd6dlpqZTBWCk5iUUppVHJPRGoxTE1XSHkrcTZ5RXVqbXloaW52MmhIY3pqeW5EdE5zdVN3c2JPQzEybDl0MG1NeTVsbWoybFEKSTJKTzBrSDVxa1ozMjBNUklvb2RUMk5VQk1kV0pPQnRPRlpCa2ZwQ0ZDNG1yZjZMbitOWEJsaE9odGRjZVhWagp4c3YxcVpJdzRtZHhOTXhqSDZ3RkJXNTZ2dTFtaE5nUnVZVUZNSGFFUUNsVGJONGx5aFUzVHNZUldNcDZhZE82CmRsT2Y4M2JaQWdNQkFBRUNnZ0VBRGU1WW1XSHN3ZFpzcWQrNXdYcGFRS2Z2SkxXNmRwTmdYeVFEZ0tiWlplWDUKYldPaUFZU3pycDBra2U0SGQxZEphYVdBYk5LYk45eUV1QWUwa2hOaHVxK3dZQzdlc3JreUJCWXgwMzRBamtwTApKMzFLaHhmejBZdXNSdStialg2UFNkZnlBUnd1b1VKN1M3R3V1NXlhbDZBWU1PVmNGcHFBbjVPU0hMbFpLZnNLClN3NXZyM3NKUjNyOENNWVZoUmQ0citGam9lMXlaczJhUHl2bno5c0U3T0ZCSVRGSVBKcE4veG53VUNpWW5vSEMKV2F2TzB5RCtPeTUyN2hBQ1FwaFVMVjRaZXV2bEZwd2ZlWkZveUhnc2YrM1FxeGhpdGtJb3NGakx2Y0xlL2xjZwpSVHNRUnU5OGJNUTdSakJpYU5kaURadjBaWEMvUUMvS054SEw0bXgxTFFLQmdRRHVDY0pUM2JBZmJRY2YvSGh3CjNxRzliNE9QTXpwOTl2ajUzWU1hZHo5Vlk1dm9RR3RGeFlwbTBRVm9MR1lkQ3BHK0lXaDdVVHBMV0JUeGtMSkYKd3EwcEFmRVhmdHB0anhmcyt0OExWVUFtSXJiM2hwUjZDUjJmYjFJWVZRWUJ4dUdzN0hWMmY3NnRZMVAzSEFnNwpGTDJNTnF3ZDd5VmlsVXdSTVptcmJKV3Qwd0tCZ1FEUW1qZlgzc1NWSWZtN1FQaVQvclhSOGJMM1B3V0lNa3NOCldJTVRYeDJmaG0vd0hOL0pNdCtEK2VWbGxjSXhLMmxSYlNTQ1NwU2hzRUVsMHNxWHZUa1FFQnJxN3RFZndRWU0KbGxNbDJQb0ovV2E5c2VYSTAzWWRNeC94Vm5sbzNaUG9MUGg4UmtKekJTWkhnMlB6cCs0VmlnUklBcGdYMXo3TwpMbHg0SEVtaEl3S0JnUURES1RVdVZYL2xCQnJuV3JQVXRuT2RRU1IzNytSeENtQXZYREgxTFBlOEpxTFkxSmdlCjZFc0U2VEtwcWwwK1NrQWJ4b0JIT3QyMGtFNzdqMHJhYnpaUmZNb1NIV3N3a0RWcGtuWDBjTHpiaDNMRGxvOTkKVHFQKzUrSkRHTktIK210a3Y2bStzaFcvU3NTNHdUN3VVWjdtcXB5TEhsdGtiRXVsZlNra3B5NUJDUUtCZ0RmUwpyVk1GZUZINGI1NGV1dWJQNk5Rdi9CYVNOT2JIbnJJSmw3b2RZQTRLcWZYMXBDVnhpY01Gb3MvV2pjc2V0T1puCmNMZTFRYVVyUjZQWmp3R2dUNTd1MEdWQ1Y1QkoxVmFVKzlkTEEwNmRFMXQ4T2VQT1F2TjVkUGplalVyMDBObjIKL3VBeTVTRm1wV0hKMVh1azJ0L0V1WFNUelNQRUpEaUV5NVlRNjl0RkFvR0JBT2tDcW1jVGZGYlpPTjJRK2JqdgpvVmQvSFpLR3YrbEhqcm5maVlhODVxcUszdWJmb0FSNGppR3V3TThqc3hZZW8vb0hQdHJDTkxINndsYlZNTUFGCmlRZG80ZUF3S0xxRHo1MUx4U2hMckwzUUtNQ1FuZVhkT0VjaEdqSW9zRG5Zekd5RTBpSzJGbWNvWHVSQU1QOHgKWDFreUlkazdENDFxSjQ5WlM1OEdBbXlLCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
tlscaName = "ca.crt"
)
type MockQueue struct {
cache_client.Store
Synced bool
}
func (f *MockQueue) HasSynced() bool {
return f.Synced
}
func (f *MockQueue) AddIfNotPresent(obj interface{}) error {
return nil
}
func (f *MockQueue) Pop(process cache_client.PopProcessFunc) (interface{}, error) {
return nil, nil
}
func (f *MockQueue) Close() {
// just mock
}
func buildSimpleClientSetForBackendSSL() *testclient.Clientset {
return testclient.NewSimpleClientset()
}
func buildIngListenerForBackendSSL() IngressLister {
ingLister := IngressLister{}
ingLister.Store = cache_client.NewStore(cache_client.DeletionHandlingMetaNamespaceKeyFunc)
return ingLister
}
func buildSecretForBackendSSL() *apiv1.Secret {
return &apiv1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "foo_secret",
Namespace: api.NamespaceDefault,
},
}
}
func buildSecrListerForBackendSSL() SecretLister {
secrLister := SecretLister{}
secrLister.Store = cache_client.NewStore(cache_client.DeletionHandlingMetaNamespaceKeyFunc)
return secrLister
}
/*
func buildListers() *ingress.StoreLister {
sl := &ingress.StoreLister{}
sl.Ingress.Store = buildIngListenerForBackendSSL()
sl.Secret.Store = buildSecrListerForBackendSSL()
return sl
}
*/
func buildControllerForBackendSSL() cache_client.Controller {
cfg := &cache_client.Config{
Queue: &MockQueue{Synced: true},
}
return cache_client.New(cfg)
}
/*
func buildGenericControllerForBackendSSL() *NGINXController {
gc := &NGINXController{
syncRateLimiter: flowcontrol.NewTokenBucketRateLimiter(0.3, 1),
cfg: &Configuration{
Client: buildSimpleClientSetForBackendSSL(),
},
listers: buildListers(),
sslCertTracker: NewSSLCertTracker(),
}
gc.syncQueue = task.NewTaskQueue(gc.syncIngress)
return gc
}
*/
func buildCrtKeyAndCA() ([]byte, []byte, []byte, error) {
// prepare
td, err := ioutil.TempDir("", "ssl")
if err != nil {
return nil, nil, nil, fmt.Errorf("error occurs while creating temp directory: %v", err)
}
ingress.DefaultSSLDirectory = td
dCrt, err := base64.StdEncoding.DecodeString(tlsCrt)
if err != nil {
return nil, nil, nil, err
}
dKey, err := base64.StdEncoding.DecodeString(tlsKey)
if err != nil {
return nil, nil, nil, err
}
dCa := dCrt
return dCrt, dKey, dCa, nil
}
/*
func TestSyncSecret(t *testing.T) {
// prepare for test
dCrt, dKey, dCa, err := buildCrtKeyAndCA()
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
foos := []struct {
tn string
secretName string
Data map[string][]byte
expectSuccess bool
}{
{"getPemCertificate_error", "default/foo_secret", map[string][]byte{api.TLSPrivateKeyKey: dKey}, false},
{"normal_test", "default/foo_secret", map[string][]byte{api.TLSCertKey: dCrt, api.TLSPrivateKeyKey: dKey, tlscaName: dCa}, true},
}
for _, foo := range foos {
t.Run(foo.tn, func(t *testing.T) {
ic := buildGenericControllerForBackendSSL()
// init secret for getPemCertificate
secret := buildSecretForBackendSSL()
secret.SetNamespace("default")
secret.SetName("foo_secret")
secret.Data = foo.Data
ic.listers.Secret.Add(secret)
key := "default/foo_secret"
// for add
ic.syncSecret(key)
if foo.expectSuccess {
// validate
_, exist := ic.sslCertTracker.Get(key)
if !exist {
t.Errorf("Failed to sync secret: %s", foo.secretName)
} else {
// for update
ic.syncSecret(key)
}
}
})
}
}
func TestGetPemCertificate(t *testing.T) {
// prepare
dCrt, dKey, dCa, err := buildCrtKeyAndCA()
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
foos := []struct {
tn string
secretName string
Data map[string][]byte
eErr bool
}{
{"sceret_not_exist", "default/foo_secret_not_exist", nil, true},
{"data_not_complete_all_not_exist", "default/foo_secret", map[string][]byte{}, true},
{"data_not_complete_TLSCertKey_not_exist", "default/foo_secret", map[string][]byte{api.TLSPrivateKeyKey: dKey, tlscaName: dCa}, false},
{"data_not_complete_TLSCertKeyAndCA_not_exist", "default/foo_secret", map[string][]byte{api.TLSPrivateKeyKey: dKey}, true},
{"data_not_complete_TLSPrivateKeyKey_not_exist", "default/foo_secret", map[string][]byte{api.TLSCertKey: dCrt, tlscaName: dCa}, false},
{"data_not_complete_TLSPrivateKeyKeyAndCA_not_exist", "default/foo_secret", map[string][]byte{api.TLSCertKey: dCrt}, true},
{"data_not_complete_CA_not_exist", "default/foo_secret", map[string][]byte{api.TLSCertKey: dCrt, api.TLSPrivateKeyKey: dKey}, false},
{"normal_test", "default/foo_secret", map[string][]byte{api.TLSCertKey: dCrt, api.TLSPrivateKeyKey: dKey, tlscaName: dCa}, false},
}
for _, foo := range foos {
t.Run(foo.tn, func(t *testing.T) {
ic := buildGenericControllerForBackendSSL()
secret := buildSecretForBackendSSL()
secret.Data = foo.Data
ic.listers.Secret.Add(secret)
sslCert, err := ic.getPemCertificate(foo.secretName)
if foo.eErr {
if err == nil {
t.Fatal("Expected error")
}
} else {
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if sslCert == nil {
t.Error("Expected an ingress.SSLCert")
}
}
})
}
}
*/

View file

@ -0,0 +1,41 @@
/*
Copyright 2015 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 store
import (
"fmt"
apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
)
// ConfigMapLister makes a Store that lists Configmaps.
type ConfigMapLister struct {
cache.Store
}
// ByKey searches for a configmap in the local configmaps Store
func (cml *ConfigMapLister) ByKey(key string) (*apiv1.ConfigMap, error) {
s, exists, err := cml.GetByKey(key)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("configmap %v was not found", key)
}
return s.(*apiv1.ConfigMap), nil
}

View file

@ -0,0 +1,40 @@
/*
Copyright 2015 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 store
import (
"fmt"
apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
)
// EndpointLister makes a Store that lists Endpoints.
type EndpointLister struct {
cache.Store
}
// GetServiceEndpoints returns the endpoints of a service, matched on service name.
func (s *EndpointLister) GetServiceEndpoints(svc *apiv1.Service) (*apiv1.Endpoints, error) {
for _, m := range s.Store.List() {
ep := m.(*apiv1.Endpoints)
if svc.Name == ep.Name && svc.Namespace == ep.Namespace {
return ep, nil
}
}
return nil, fmt.Errorf("could not find endpoints for service: %v", svc.Name)
}

View file

@ -0,0 +1,41 @@
/*
Copyright 2015 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 store
import (
"fmt"
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/client-go/tools/cache"
)
// IngressLister makes a Store that lists Ingress.
type IngressLister struct {
cache.Store
}
// ByKey searches for an ingress in the local ingress Store
func (il IngressLister) ByKey(key string) (*extensions.Ingress, error) {
i, exists, err := il.GetByKey(key)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("ingress %v was not found", key)
}
return i.(*extensions.Ingress), nil
}

View file

@ -0,0 +1,26 @@
/*
Copyright 2015 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 store
import (
"k8s.io/client-go/tools/cache"
)
// IngressAnnotationsLister makes a Store that lists annotations in Ingress rules.
type IngressAnnotationsLister struct {
cache.Store
}

View file

@ -0,0 +1,30 @@
package store
import (
"fmt"
"k8s.io/client-go/tools/cache"
"k8s.io/ingress-nginx/internal/ingress"
)
// 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{}),
}
}
// ByKey searches for an ingress in the local ingress Store
func (s SSLCertTracker) ByKey(key string) (*ingress.SSLCert, error) {
cert, exists := s.Get(key)
if !exists {
return nil, fmt.Errorf("local SSL certificate %v was not found", key)
}
return cert.(*ingress.SSLCert), nil
}

View file

@ -1,113 +0,0 @@
/*
Copyright 2015 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 store
import (
"fmt"
apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
)
// IngressLister makes a Store that lists Ingress.
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
}
// GetByName searches for a secret in the local secrets Store
func (sl *SecretLister) GetByName(name string) (*apiv1.Secret, error) {
s, exists, err := sl.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("secret %v was not found", name)
}
return s.(*apiv1.Secret), nil
}
// ConfigMapLister makes a Store that lists Configmaps.
type ConfigMapLister struct {
cache.Store
}
// GetByName searches for a configmap in the local configmaps Store
func (cml *ConfigMapLister) GetByName(name string) (*apiv1.ConfigMap, error) {
s, exists, err := cml.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("configmap %v was not found", name)
}
return s.(*apiv1.ConfigMap), nil
}
// ServiceLister makes a Store that lists Services.
type ServiceLister struct {
cache.Store
}
// GetByName searches for a service in the local secrets Store
func (sl *ServiceLister) GetByName(name string) (*apiv1.Service, error) {
s, exists, err := sl.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("service %v was not found", name)
}
return s.(*apiv1.Service), nil
}
// EndpointLister makes a Store that lists Endpoints.
type EndpointLister struct {
cache.Store
}
// GetServiceEndpoints returns the endpoints of a service, matched on service name.
func (s *EndpointLister) GetServiceEndpoints(svc *apiv1.Service) (*apiv1.Endpoints, error) {
for _, m := range s.Store.List() {
ep := m.(*apiv1.Endpoints)
if svc.Name == ep.Name && svc.Namespace == ep.Namespace {
return ep, nil
}
}
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{}),
}
}

View file

@ -0,0 +1,41 @@
/*
Copyright 2015 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 store
import (
"fmt"
apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
)
// SecretLister makes a Store that lists Secrets.
type SecretLister struct {
cache.Store
}
// ByKey searches for a secret in the local secrets Store
func (sl *SecretLister) ByKey(key string) (*apiv1.Secret, error) {
s, exists, err := sl.GetByKey(key)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("secret %v was not found", key)
}
return s.(*apiv1.Secret), nil
}

View file

@ -0,0 +1,41 @@
/*
Copyright 2015 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 store
import (
"fmt"
apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
)
// ServiceLister makes a Store that lists Services.
type ServiceLister struct {
cache.Store
}
// ByKey searches for a service in the local secrets Store
func (sl *ServiceLister) ByKey(key string) (*apiv1.Service, error) {
s, exists, err := sl.GetByKey(key)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("service %v was not found", key)
}
return s.(*apiv1.Service), nil
}

View file

@ -0,0 +1,472 @@
/*
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 store
import (
"fmt"
"reflect"
"time"
"github.com/golang/glog"
apiv1 "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
cache_client "k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations"
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
// Storer is the interface that wraps the required methods to gather information
// about ingresses, services, secrets and ingress annotations.
type Storer interface {
GetConfigMap(key string) (*apiv1.ConfigMap, error)
// GetSecret returns a Secret using the namespace and name as key
GetSecret(key string) (*apiv1.Secret, error)
// GetService returns a Service using the namespace and name as key
GetService(key string) (*apiv1.Service, error)
GetServiceEndpoints(svc *apiv1.Service) (*apiv1.Endpoints, error)
// GetSecret returns an Ingress using the namespace and name as key
GetIngress(key string) (*extensions.Ingress, error)
// ListIngresses returns the list of Ingresses
ListIngresses() []*extensions.Ingress
// GetIngressAnnotations returns the annotations associated to an Ingress
GetIngressAnnotations(ing *extensions.Ingress) (*annotations.Ingress, error)
// GetLocalSecret returns the local copy of a Secret
GetLocalSecret(name string) (*ingress.SSLCert, error)
// ListLocalSecrets returns the list of local Secrets
ListLocalSecrets() []*ingress.SSLCert
// GetAuthCertificate resolves a given secret name into an SSL certificate.
// The secret must contain 3 keys named:
// ca.crt: contains the certificate chain used for authentication
GetAuthCertificate(string) (*resolver.AuthSSLCert, error)
// GetDefaultBackend returns the default backend configuration
GetDefaultBackend() defaults.Backend
// SetDefaultBackend sets the default backend configuration
SetDefaultBackend(defaults.Backend)
// Run initiates the synchronization of the controllers
Run(stopCh chan struct{})
}
type EventType string
const (
CreateEvent EventType = "CREATE"
UpdateEvent EventType = "UPDATE"
DeleteEvent EventType = "DELETE"
)
type Event struct {
Type EventType
Obj interface{}
}
// Lister returns the stores for ingresses, services, endpoints, secrets and configmaps.
type Lister struct {
Ingress IngressLister
Service ServiceLister
Endpoint EndpointLister
Secret SecretLister
ConfigMap ConfigMapLister
IngressAnnotation IngressAnnotationsLister
}
// Controller defines the required controllers that interact agains the api server
type Controller struct {
Ingress cache.Controller
Endpoint cache.Controller
Service cache.Controller
Secret cache.Controller
Configmap cache.Controller
}
// Run initiates the synchronization of the controllers against the api server
func (c *Controller) Run(stopCh chan struct{}) {
go c.Ingress.Run(stopCh)
go c.Endpoint.Run(stopCh)
go c.Service.Run(stopCh)
go c.Secret.Run(stopCh)
go c.Configmap.Run(stopCh)
// wait for all involved caches to be synced, before processing items
// from the queue is started
if !cache.WaitForCacheSync(stopCh,
c.Ingress.HasSynced,
c.Endpoint.HasSynced,
c.Service.HasSynced,
c.Secret.HasSynced,
c.Configmap.HasSynced,
) {
runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
}
}
type k8sStore struct {
backendDefaults defaults.Backend
cache *Controller
// listers
listers *Lister
// sslStore local store of SSL certificates (certificates used in ingress)
sslStore *SSLCertTracker
// annotations parser
annotations annotations.Extractor
}
// New creates a new object store to be used in the ingress controller
func New(namespace, configmap, tcp, udp string,
resyncPeriod time.Duration,
recorder record.EventRecorder,
client clientset.Interface,
annotations annotations.Extractor,
updateCh chan Event) Storer {
store := &k8sStore{
cache: &Controller{},
listers: &Lister{},
sslStore: NewSSLCertTracker(),
annotations: annotations,
}
ingEventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
addIng := obj.(*extensions.Ingress)
if !class.IsValid(addIng) {
a, _ := parser.GetStringAnnotation(class.IngressKey, addIng)
glog.Infof("ignoring add for ingress %v based on annotation %v with value %v", addIng.Name, class.IngressKey, a)
return
}
store.extractAnnotations(addIng)
recorder.Eventf(addIng, apiv1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", addIng.Namespace, addIng.Name))
updateCh <- Event{
Type: CreateEvent,
Obj: obj,
}
},
DeleteFunc: func(obj interface{}) {
delIng, ok := obj.(*extensions.Ingress)
if !ok {
// If we reached here it means the ingress was deleted but its final state is unrecorded.
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
glog.Errorf("couldn't get object from tombstone %#v", obj)
return
}
delIng, ok = tombstone.Obj.(*extensions.Ingress)
if !ok {
glog.Errorf("Tombstone contained object that is not an Ingress: %#v", obj)
return
}
}
if !class.IsValid(delIng) {
glog.Infof("ignoring delete for ingress %v based on annotation %v", delIng.Name, class.IngressKey)
return
}
recorder.Eventf(delIng, apiv1.EventTypeNormal, "DELETE", fmt.Sprintf("Ingress %s/%s", delIng.Namespace, delIng.Name))
store.listers.IngressAnnotation.Delete(delIng)
updateCh <- Event{
Type: DeleteEvent,
Obj: obj,
}
},
UpdateFunc: func(old, cur interface{}) {
oldIng := old.(*extensions.Ingress)
curIng := cur.(*extensions.Ingress)
validOld := class.IsValid(oldIng)
validCur := class.IsValid(curIng)
if !validOld && validCur {
glog.Infof("creating ingress %v based on annotation %v", curIng.Name, class.IngressKey)
recorder.Eventf(curIng, apiv1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
} else if validOld && !validCur {
glog.Infof("removing ingress %v based on annotation %v", curIng.Name, class.IngressKey)
recorder.Eventf(curIng, apiv1.EventTypeNormal, "DELETE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
} else if validCur && !reflect.DeepEqual(old, cur) {
recorder.Eventf(curIng, apiv1.EventTypeNormal, "UPDATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
}
store.extractAnnotations(curIng)
updateCh <- Event{
Type: UpdateEvent,
Obj: cur,
}
},
}
secrEventHandler := cache.ResourceEventHandlerFuncs{
UpdateFunc: func(old, cur interface{}) {
if !reflect.DeepEqual(old, cur) {
sec := cur.(*apiv1.Secret)
key := fmt.Sprintf("%v/%v", sec.Namespace, sec.Name)
_, exists := store.sslStore.Get(key)
if exists {
updateCh <- Event{
Type: UpdateEvent,
Obj: cur,
}
}
}
},
DeleteFunc: func(obj interface{}) {
sec, ok := obj.(*apiv1.Secret)
if !ok {
// If we reached here it means the secret was deleted but its final state is unrecorded.
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
glog.Errorf("couldn't get object from tombstone %#v", obj)
return
}
sec, ok = tombstone.Obj.(*apiv1.Secret)
if !ok {
glog.Errorf("Tombstone contained object that is not a Secret: %#v", obj)
return
}
}
key := fmt.Sprintf("%v/%v", sec.Namespace, sec.Name)
store.sslStore.Delete(key)
updateCh <- Event{
Type: DeleteEvent,
Obj: obj,
}
},
}
eventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
updateCh <- Event{
Type: CreateEvent,
Obj: obj,
}
},
DeleteFunc: func(obj interface{}) {
updateCh <- Event{
Type: DeleteEvent,
Obj: obj,
}
},
UpdateFunc: func(old, cur interface{}) {
oep := old.(*apiv1.Endpoints)
ocur := cur.(*apiv1.Endpoints)
if !reflect.DeepEqual(ocur.Subsets, oep.Subsets) {
updateCh <- Event{
Type: UpdateEvent,
Obj: cur,
}
}
},
}
mapEventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
upCmap := obj.(*apiv1.ConfigMap)
mapKey := fmt.Sprintf("%s/%s", upCmap.Namespace, upCmap.Name)
if mapKey == configmap {
glog.V(2).Infof("adding configmap %v to backend", mapKey)
updateCh <- Event{
Type: CreateEvent,
Obj: obj,
}
}
},
UpdateFunc: func(old, cur interface{}) {
if !reflect.DeepEqual(old, cur) {
upCmap := cur.(*apiv1.ConfigMap)
mapKey := fmt.Sprintf("%s/%s", upCmap.Namespace, upCmap.Name)
if mapKey == configmap {
glog.V(2).Infof("updating configmap backend (%v)", mapKey)
updateCh <- Event{
Type: UpdateEvent,
Obj: cur,
}
}
// updates to configuration configmaps can trigger an update
if mapKey == configmap || mapKey == tcp || mapKey == udp {
recorder.Eventf(upCmap, apiv1.EventTypeNormal, "UPDATE", fmt.Sprintf("ConfigMap %v", mapKey))
updateCh <- Event{
Type: UpdateEvent,
Obj: cur,
}
}
}
},
}
store.listers.IngressAnnotation.Store = cache_client.NewStore(cache_client.DeletionHandlingMetaNamespaceKeyFunc)
store.listers.Ingress.Store, store.cache.Ingress = cache.NewInformer(
cache.NewListWatchFromClient(client.ExtensionsV1beta1().RESTClient(), "ingresses", namespace, fields.Everything()),
&extensions.Ingress{}, resyncPeriod, ingEventHandler)
store.listers.Endpoint.Store, store.cache.Endpoint = cache.NewInformer(
cache.NewListWatchFromClient(client.CoreV1().RESTClient(), "endpoints", namespace, fields.Everything()),
&apiv1.Endpoints{}, resyncPeriod, eventHandler)
store.listers.Secret.Store, store.cache.Secret = cache.NewInformer(
cache.NewListWatchFromClient(client.CoreV1().RESTClient(), "secrets", namespace, fields.Everything()),
&apiv1.Secret{}, resyncPeriod, secrEventHandler)
store.listers.ConfigMap.Store, store.cache.Configmap = cache.NewInformer(
cache.NewListWatchFromClient(client.CoreV1().RESTClient(), "configmaps", namespace, fields.Everything()),
&apiv1.ConfigMap{}, resyncPeriod, mapEventHandler)
store.listers.Service.Store, store.cache.Service = cache.NewInformer(
cache.NewListWatchFromClient(client.CoreV1().RESTClient(), "services", namespace, fields.Everything()),
&apiv1.Service{}, resyncPeriod, cache.ResourceEventHandlerFuncs{})
return store
}
func (s k8sStore) extractAnnotations(ing *extensions.Ingress) {
anns := s.annotations.Extract(ing)
glog.V(3).Infof("updating annotations information for ingres %v/%v", anns.Namespace, anns.Name)
s.listers.IngressAnnotation.Update(anns)
}
// GetSecret returns a Secret using the namespace and name as key
func (s k8sStore) GetSecret(key string) (*apiv1.Secret, error) {
return s.listers.Secret.ByKey(key)
}
// ListLocalSecrets returns the list of local Secrets
func (s k8sStore) ListLocalSecrets() []*ingress.SSLCert {
var certs []*ingress.SSLCert
for _, item := range s.sslStore.List() {
if s, ok := item.(*ingress.SSLCert); ok {
certs = append(certs, s)
}
}
return certs
}
// GetService returns a Service using the namespace and name as key
func (s k8sStore) GetService(key string) (*apiv1.Service, error) {
return s.listers.Service.ByKey(key)
}
// GetSecret returns an Ingress using the namespace and name as key
func (s k8sStore) GetIngress(key string) (*extensions.Ingress, error) {
return s.listers.Ingress.ByKey(key)
}
// ListIngresses returns the list of Ingresses
func (s k8sStore) ListIngresses() []*extensions.Ingress {
// filter ingress rules
var ingresses []*extensions.Ingress
for _, item := range s.listers.Ingress.List() {
ing := item.(*extensions.Ingress)
if !class.IsValid(ing) {
continue
}
ingresses = append(ingresses, ing)
}
return ingresses
}
// GetIngressAnnotations returns the annotations associated to an Ingress
func (s k8sStore) GetIngressAnnotations(ing *extensions.Ingress) (*annotations.Ingress, error) {
key := fmt.Sprintf("%v/%v", ing.Namespace, ing.Name)
item, exists, err := s.listers.IngressAnnotation.GetByKey(key)
if err != nil {
return nil, fmt.Errorf("unexpected error getting ingress annotation %v: %v", key, err)
}
if !exists {
return nil, fmt.Errorf("ingress annotation %v was not found", key)
}
return item.(*annotations.Ingress), nil
}
// GetLocalSecret returns the local copy of a Secret
func (s k8sStore) GetLocalSecret(key string) (*ingress.SSLCert, error) {
return s.sslStore.ByKey(key)
}
func (s k8sStore) GetConfigMap(key string) (*apiv1.ConfigMap, error) {
return s.listers.ConfigMap.ByKey(key)
}
func (s k8sStore) GetServiceEndpoints(svc *apiv1.Service) (*apiv1.Endpoints, error) {
return s.listers.Endpoint.GetServiceEndpoints(svc)
}
// GetAuthCertificate is used by the auth-tls annotations to get a cert from a secret
func (s k8sStore) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) {
if _, err := s.GetLocalSecret(name); err != nil {
s.syncSecret(name)
}
cert, err := s.GetLocalSecret(name)
if err != nil {
return nil, err
}
return &resolver.AuthSSLCert{
Secret: name,
CAFileName: cert.CAFileName,
PemSHA: cert.PemSHA,
}, nil
}
// GetDefaultBackend returns the default backend
func (s k8sStore) GetDefaultBackend() defaults.Backend {
return s.backendDefaults
}
func (s *k8sStore) SetDefaultBackend(bd defaults.Backend) {
s.backendDefaults = bd
}
// Run initiates the synchronization of the controllers
func (s k8sStore) Run(stopCh chan struct{}) {
// start controllers
s.cache.Run(stopCh)
// initial sync of secrets to avoid unnecessary reloads
glog.Info("running initial sync of secrets")
for _, ing := range s.ListIngresses() {
s.readSecrets(ing)
}
// start goroutine to check for missing local secrets
go wait.Until(s.checkMissingSecrets, 30*time.Second, stopCh)
}

View file

@ -0,0 +1,310 @@
/*
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 store
import (
"fmt"
"os"
"sync/atomic"
"testing"
"time"
apiv1 "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
extensions "k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record"
"k8s.io/ingress-nginx/internal/ingress/annotations"
"k8s.io/ingress-nginx/test/e2e/framework"
)
func TestStore(t *testing.T) {
// TODO: find a way to avoid the need to use a real api server
home := os.Getenv("HOME")
kubeConfigFile := fmt.Sprintf("%v/.kube/config", home)
kubeContext := ""
kubeConfig, err := framework.LoadConfig(kubeConfigFile, kubeContext)
if err != nil {
t.Errorf("unexpected error loading kubeconfig file: %v", err)
}
clientSet, err := kubernetes.NewForConfig(kubeConfig)
if err != nil {
t.Errorf("unexpected error creating ingress client: %v", err)
}
t.Run("should return an error searching for non existing objects", func(t *testing.T) {
ns := createNamespace(clientSet, t)
defer deleteNamespace(ns, clientSet, t)
stopCh := make(chan struct{})
defer close(stopCh)
updateCh := make(chan Event)
defer close(updateCh)
go func(ch chan Event) {
for {
<-ch
}
}(updateCh)
storer := New(ns.Name,
fmt.Sprintf("%v/config", ns.Name),
fmt.Sprintf("%v/tcp", ns.Name),
fmt.Sprintf("%v/udp", ns.Name),
10*time.Minute,
&record.FakeRecorder{},
clientSet,
annotations.Extractor{},
updateCh)
storer.Run(stopCh)
key := fmt.Sprintf("%v/anything", ns.Name)
ing, err := storer.GetIngress(key)
if err == nil {
t.Errorf("expected an error but none returned")
}
if ing != nil {
t.Errorf("expected an Ingres but none returned")
}
ls, err := storer.GetLocalSecret(key)
if err == nil {
t.Errorf("expected an error but none returned")
}
if ls != nil {
t.Errorf("expected an Ingres but none returned")
}
s, err := storer.GetSecret(key)
if err == nil {
t.Errorf("expected an error but none returned")
}
if s != nil {
t.Errorf("expected an Ingres but none returned")
}
svc, err := storer.GetService(key)
if err == nil {
t.Errorf("expected an error but none returned")
}
if svc != nil {
t.Errorf("expected an Ingres but none returned")
}
})
t.Run("should return ingress one event for add, update and delete", func(t *testing.T) {
ns := createNamespace(clientSet, t)
defer deleteNamespace(ns, clientSet, t)
stopCh := make(chan struct{})
defer close(stopCh)
updateCh := make(chan Event)
defer close(updateCh)
var add uint64
var upd uint64
var del uint64
go func(ch chan Event) {
for {
e := <-ch
if e.Obj == nil {
continue
}
if _, ok := e.Obj.(*extensions.Ingress); !ok {
t.Errorf("expected an Ingress type but %T returned", e.Obj)
}
switch e.Type {
case CreateEvent:
atomic.AddUint64(&add, 1)
break
case UpdateEvent:
atomic.AddUint64(&upd, 1)
break
case DeleteEvent:
atomic.AddUint64(&del, 1)
break
}
}
}(updateCh)
storer := New(ns.Name,
fmt.Sprintf("%v/config", ns.Name),
fmt.Sprintf("%v/tcp", ns.Name),
fmt.Sprintf("%v/udp", ns.Name),
10*time.Minute,
&record.FakeRecorder{},
clientSet,
annotations.Extractor{},
updateCh)
storer.Run(stopCh)
ing, err := ensureIngress(&v1beta1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "dummy",
Namespace: ns.Name,
},
Spec: v1beta1.IngressSpec{
Rules: []v1beta1.IngressRule{
{
Host: "dummy",
IngressRuleValue: v1beta1.IngressRuleValue{
HTTP: &v1beta1.HTTPIngressRuleValue{
Paths: []v1beta1.HTTPIngressPath{
{
Path: "/",
Backend: v1beta1.IngressBackend{
ServiceName: "http-svc",
ServicePort: intstr.FromInt(80),
},
},
},
},
},
},
},
},
}, clientSet)
if err != nil {
t.Errorf("unexpected error creating ingress: %v", err)
}
// create an invalid ingress (different class)
_, err = ensureIngress(&v1beta1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "custom-class",
Namespace: ns.Name,
Annotations: map[string]string{
"kubernetes.io/ingress.class": "something",
},
},
Spec: v1beta1.IngressSpec{
Rules: []v1beta1.IngressRule{
{
Host: "dummy",
IngressRuleValue: v1beta1.IngressRuleValue{
HTTP: &v1beta1.HTTPIngressRuleValue{
Paths: []v1beta1.HTTPIngressPath{
{
Path: "/",
Backend: v1beta1.IngressBackend{
ServiceName: "http-svc",
ServicePort: intstr.FromInt(80),
},
},
},
},
},
},
},
},
}, clientSet)
if err != nil {
t.Errorf("unexpected error creating ingress: %v", err)
}
ni := ing.DeepCopy()
ni.Spec.Rules[0].Host = "update-dummy"
_, err = ensureIngress(ni, clientSet)
if err != nil {
t.Errorf("unexpected error creating ingress: %v", err)
}
err = clientSet.ExtensionsV1beta1().
Ingresses(ni.Namespace).
Delete(ni.Name, &metav1.DeleteOptions{})
if err != nil {
t.Errorf("unexpected error creating ingress: %v", err)
}
waitForNoIngressInNamespace(clientSet, ni.Namespace, ni.Name)
if atomic.LoadUint64(&add) != 1 {
t.Errorf("expected 1 event of type Create but %v ocurred", add)
}
if atomic.LoadUint64(&upd) != 1 {
t.Errorf("expected 1 event of type Update but %v ocurred", upd)
}
if atomic.LoadUint64(&del) != 1 {
t.Errorf("expected 1 event of type Delete but %v ocurred", del)
}
})
}
func createNamespace(clientSet *kubernetes.Clientset, t *testing.T) *apiv1.Namespace {
t.Log("creating temporal namespace")
ns, err := framework.CreateKubeNamespace("store-test", clientSet)
if err != nil {
t.Errorf("unexpected error creating ingress client: %v", err)
}
t.Logf("temporal namespace %v created", ns.Name)
return ns
}
func deleteNamespace(ns *apiv1.Namespace, clientSet *kubernetes.Clientset, t *testing.T) {
t.Logf("deleting temporal namespace %v created", ns.Name)
err := framework.DeleteKubeNamespace(clientSet, ns.Name)
if err != nil {
t.Errorf("unexpected error creating ingress client: %v", err)
}
t.Logf("temporal namespace %v deleted", ns.Name)
}
func ensureIngress(ingress *extensions.Ingress, clientSet *kubernetes.Clientset) (*extensions.Ingress, error) {
s, err := clientSet.ExtensionsV1beta1().Ingresses(ingress.Namespace).Update(ingress)
if err != nil {
if k8sErrors.IsNotFound(err) {
return clientSet.ExtensionsV1beta1().Ingresses(ingress.Namespace).Create(ingress)
}
return nil, err
}
return s, nil
}
func waitForNoIngressInNamespace(c kubernetes.Interface, namespace, name string) error {
return wait.PollImmediate(1*time.Second, time.Minute*2, noIngressInNamespace(c, namespace, name))
}
func noIngressInNamespace(c kubernetes.Interface, namespace, name string) wait.ConditionFunc {
return func() (bool, error) {
ing, err := c.ExtensionsV1beta1().Ingresses(namespace).Get(name, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return true, nil
}
if err != nil {
return false, err
}
if ing == nil {
return true, nil
}
return false, nil
}
}

View file

@ -33,7 +33,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress/annotations/redirect" "k8s.io/ingress-nginx/internal/ingress/annotations/redirect"
"k8s.io/ingress-nginx/internal/ingress/annotations/rewrite" "k8s.io/ingress-nginx/internal/ingress/annotations/rewrite"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
"k8s.io/ingress-nginx/internal/ingress/store"
) )
var ( var (
@ -44,17 +43,6 @@ var (
DefaultSSLDirectory = "/ingress-controller/ssl" DefaultSSLDirectory = "/ingress-controller/ssl"
) )
// 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
IngressAnnotation store.IngressAnnotationsLister
}
// Configuration holds the definition of all the parts required to describe all // Configuration holds the definition of all the parts required to describe all
// ingresses reachable by the ingress controller (using a filter by namespace) // ingresses reachable by the ingress controller (using a filter by namespace)
type Configuration struct { type Configuration struct {

View file

@ -35,47 +35,3 @@ until kubectl get nodes -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True";
do do
sleep 1; sleep 1;
done done
echo "deploying NGINX Ingress controller"
cat deploy/namespace.yaml | kubectl apply -f -
cat deploy/default-backend.yaml | kubectl apply -f -
cat deploy/configmap.yaml | kubectl apply -f -
cat deploy/tcp-services-configmap.yaml | kubectl apply -f -
cat deploy/udp-services-configmap.yaml | kubectl apply -f -
cat deploy/without-rbac.yaml | kubectl apply -f -
cat deploy/provider/baremetal/service-nodeport.yaml | kubectl apply -f -
echo "updating image..."
kubectl set image \
deployments \
--namespace ingress-nginx \
--selector app=ingress-nginx \
nginx-ingress-controller=quay.io/kubernetes-ingress-controller/nginx-ingress-controller:test
sleep 5
echo "waiting NGINX ingress pod..."
function waitForPod() {
until kubectl get pods -n ingress-nginx -l app=ingress-nginx -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True";
do
sleep 1;
done
}
export -f waitForPod
timeout 10s bash -c waitForPod
if kubectl get pods -n ingress-nginx -l app=ingress-nginx -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True";
then
echo "Kubernetes deployments started"
else
echo "Kubernetes deployments with issues:"
kubectl get pods -n ingress-nginx
echo "Reason:"
kubectl describe pods -n ingress-nginx
kubectl logs -n ingress-nginx -l app=ingress-nginx
exit 1
fi

61
test/e2e/wait-for-nginx.sh Executable file
View file

@ -0,0 +1,61 @@
#!/bin/bash
# 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.
export JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}'
echo "deploying NGINX Ingress controller"
cat deploy/namespace.yaml | kubectl apply -f -
cat deploy/default-backend.yaml | kubectl apply -f -
cat deploy/configmap.yaml | kubectl apply -f -
cat deploy/tcp-services-configmap.yaml | kubectl apply -f -
cat deploy/udp-services-configmap.yaml | kubectl apply -f -
cat deploy/without-rbac.yaml | kubectl apply -f -
cat deploy/provider/baremetal/service-nodeport.yaml | kubectl apply -f -
echo "updating image..."
kubectl set image \
deployments \
--namespace ingress-nginx \
--selector app=ingress-nginx \
nginx-ingress-controller=quay.io/kubernetes-ingress-controller/nginx-ingress-controller:test
sleep 5
echo "waiting NGINX ingress pod..."
function waitForPod() {
until kubectl get pods -n ingress-nginx -l app=ingress-nginx -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True";
do
sleep 1;
done
}
export -f waitForPod
timeout 10s bash -c waitForPod
if kubectl get pods -n ingress-nginx -l app=ingress-nginx -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True";
then
echo "Kubernetes deployments started"
else
echo "Kubernetes deployments with issues:"
kubectl get pods -n ingress-nginx
echo "Reason:"
kubectl describe pods -n ingress-nginx
kubectl logs -n ingress-nginx -l app=ingress-nginx
exit 1
fi