Add prefix nginx to annotations

This commit is contained in:
Manuel de Brito Fontes 2017-11-08 17:58:57 -03:00
parent 97577c07a5
commit 8f1ff15a6e
54 changed files with 445 additions and 441 deletions

View file

@ -122,6 +122,8 @@ func parseFlags() (bool, *controller.Configuration, error) {
sslProxyPort = flags.Int("ssl-passtrough-proxy-port", 442, `Default port to use internally for SSL when SSL Passthgough is enabled`) sslProxyPort = flags.Int("ssl-passtrough-proxy-port", 442, `Default port to use internally for SSL when SSL Passthgough is enabled`)
defServerPort = flags.Int("default-server-port", 8181, `Default port to use for exposing the default server (catch all)`) defServerPort = flags.Int("default-server-port", 8181, `Default port to use for exposing the default server (catch all)`)
healthzPort = flags.Int("healthz-port", 10254, "port for healthz endpoint.") healthzPort = flags.Int("healthz-port", 10254, "port for healthz endpoint.")
annotationsPrefix = flags.String("annotations-prefix", "nginx.ingress.kubernetes.io", `Prefix of the ingress annotations.`)
) )
flag.Set("logtostderr", "true") flag.Set("logtostderr", "true")
@ -177,6 +179,7 @@ func parseFlags() (bool, *controller.Configuration, error) {
} }
config := &controller.Configuration{ config := &controller.Configuration{
AnnotationsPrefix: *annotationsPrefix,
APIServerHost: *apiserverHost, APIServerHost: *apiserverHost,
KubeConfigFile: *kubeConfigFile, KubeConfigFile: *kubeConfigFile,
UpdateStatus: *updateStatus, UpdateStatus: *updateStatus,

View file

@ -20,22 +20,20 @@ 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 (
annotation = "ingress.kubernetes.io/server-alias"
) )
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() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return alias{} return alias{r}
} }
// 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(annotation, ing) return parser.GetStringAnnotation("server-alias", ing, a.r)
} }

View file

@ -22,10 +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"
) )
const annotation = "nginx/server-alias"
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
ap := NewParser() ap := NewParser(&resolver.Mock{})
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

@ -54,13 +54,6 @@ import (
// DeniedKeyName name of the key that contains the reason to deny a location // DeniedKeyName name of the key that contains the reason to deny a location
const DeniedKeyName = "Denied" const DeniedKeyName = "Denied"
type config interface {
resolver.AuthCertificate
resolver.DefaultBackend
resolver.Secret
resolver.Service
}
// Ingress defines the valid annotations present in one NGINX Ingress rule // Ingress defines the valid annotations present in one NGINX Ingress rule
type Ingress struct { type Ingress struct {
metav1.ObjectMeta metav1.ObjectMeta
@ -91,37 +84,35 @@ type Ingress struct {
// Extractor defines the annotation parsers to be used in the extraction of annotations // Extractor defines the annotation parsers to be used in the extraction of annotations
type Extractor struct { type Extractor struct {
secretResolver resolver.Secret
annotations map[string]parser.IngressAnnotation annotations map[string]parser.IngressAnnotation
} }
// NewAnnotationExtractor creates a new annotations extractor // NewAnnotationExtractor creates a new annotations extractor
func NewAnnotationExtractor(cfg config) Extractor { func NewAnnotationExtractor(cfg resolver.Resolver) Extractor {
return Extractor{ return Extractor{
cfg,
map[string]parser.IngressAnnotation{ map[string]parser.IngressAnnotation{
"Alias": alias.NewParser(), "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(), "ClientBodyBufferSize": clientbodybuffersize.NewParser(cfg),
"ConfigurationSnippet": snippet.NewParser(), "ConfigurationSnippet": snippet.NewParser(cfg),
"CorsConfig": cors.NewParser(), "CorsConfig": cors.NewParser(cfg),
"DefaultBackend": defaultbackend.NewParser(cfg), "DefaultBackend": defaultbackend.NewParser(cfg),
"ExternalAuth": authreq.NewParser(), "ExternalAuth": authreq.NewParser(cfg),
"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(), "Redirect": redirect.NewParser(cfg),
"Rewrite": rewrite.NewParser(cfg), "Rewrite": rewrite.NewParser(cfg),
"SecureUpstream": secureupstream.NewParser(cfg), "SecureUpstream": secureupstream.NewParser(cfg),
"ServerSnippet": serversnippet.NewParser(), "ServerSnippet": serversnippet.NewParser(cfg),
"ServiceUpstream": serviceupstream.NewParser(), "ServiceUpstream": serviceupstream.NewParser(cfg),
"SessionAffinity": sessionaffinity.NewParser(), "SessionAffinity": sessionaffinity.NewParser(cfg),
"SSLPassthrough": sslpassthrough.NewParser(), "SSLPassthrough": sslpassthrough.NewParser(cfg),
"UsePortInRedirects": portinredirect.NewParser(cfg), "UsePortInRedirects": portinredirect.NewParser(cfg),
"UpstreamHashBy": upstreamhashby.NewParser(), "UpstreamHashBy": upstreamhashby.NewParser(cfg),
"UpstreamVhost": upstreamvhost.NewParser(), "UpstreamVhost": upstreamvhost.NewParser(cfg),
"VtsFilterKey": vtsfilterkey.NewParser(), "VtsFilterKey": vtsfilterkey.NewParser(cfg),
"Whitelist": ipwhitelist.NewParser(cfg), "Whitelist": ipwhitelist.NewParser(cfg),
}, },
} }

View file

@ -29,25 +29,26 @@ import (
) )
const ( const (
annotationSecureUpstream = "ingress.kubernetes.io/secure-backends" annotationSecureUpstream = "nginx/secure-backends"
annotationSecureVerifyCACert = "ingress.kubernetes.io/secure-verify-ca-secret" annotationSecureVerifyCACert = "nginx/secure-verify-ca-secret"
annotationUpsMaxFails = "ingress.kubernetes.io/upstream-max-fails" annotationUpsMaxFails = "nginx/upstream-max-fails"
annotationUpsFailTimeout = "ingress.kubernetes.io/upstream-fail-timeout" annotationUpsFailTimeout = "nginx/upstream-fail-timeout"
annotationPassthrough = "ingress.kubernetes.io/ssl-passthrough" annotationPassthrough = "nginx/ssl-passthrough"
annotationAffinityType = "ingress.kubernetes.io/affinity" annotationAffinityType = "nginx/affinity"
annotationCorsEnabled = "ingress.kubernetes.io/enable-cors" annotationCorsEnabled = "nginx/enable-cors"
annotationCorsAllowOrigin = "ingress.kubernetes.io/cors-allow-origin" annotationCorsAllowOrigin = "nginx/cors-allow-origin"
annotationCorsAllowMethods = "ingress.kubernetes.io/cors-allow-methods" annotationCorsAllowMethods = "nginx/cors-allow-methods"
annotationCorsAllowHeaders = "ingress.kubernetes.io/cors-allow-headers" annotationCorsAllowHeaders = "nginx/cors-allow-headers"
annotationCorsAllowCredentials = "ingress.kubernetes.io/cors-allow-credentials" annotationCorsAllowCredentials = "nginx/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 = "ingress.kubernetes.io/session-cookie-name" annotationAffinityCookieName = "nginx/session-cookie-name"
annotationAffinityCookieHash = "ingress.kubernetes.io/session-cookie-hash" annotationAffinityCookieHash = "nginx/session-cookie-hash"
annotationUpstreamHashBy = "ingress.kubernetes.io/upstream-hash-by" annotationUpstreamHashBy = "nginx/upstream-hash-by"
) )
type mockCfg struct { type mockCfg struct {
resolver.Mock
MockSecrets map[string]*apiv1.Secret MockSecrets map[string]*apiv1.Secret
MockServices map[string]*apiv1.Service MockServices map[string]*apiv1.Service
} }

View file

@ -33,12 +33,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
authType = "ingress.kubernetes.io/auth-type"
authSecret = "ingress.kubernetes.io/auth-secret"
authRealm = "ingress.kubernetes.io/auth-realm"
)
var ( var (
authTypeRegex = regexp.MustCompile(`basic|digest`) authTypeRegex = regexp.MustCompile(`basic|digest`)
// AuthDirectory default directory used to store files // AuthDirectory default directory used to store files
@ -83,12 +77,12 @@ func (bd1 *Config) Equal(bd2 *Config) bool {
} }
type auth struct { type auth struct {
secretResolver resolver.Secret r resolver.Resolver
authDirectory string authDirectory string
} }
// NewParser creates a new authentication annotation parser // NewParser creates a new authentication annotation parser
func NewParser(authDirectory string, sr resolver.Secret) parser.IngressAnnotation { func NewParser(authDirectory string, r resolver.Resolver) parser.IngressAnnotation {
os.MkdirAll(authDirectory, 0755) os.MkdirAll(authDirectory, 0755)
currPath := authDirectory currPath := authDirectory
@ -100,7 +94,7 @@ func NewParser(authDirectory string, sr resolver.Secret) parser.IngressAnnotatio
} }
} }
return auth{sr, authDirectory} return auth{r, authDirectory}
} }
// Parse parses the annotations contained in the ingress // Parse parses the annotations contained in the ingress
@ -108,7 +102,7 @@ func NewParser(authDirectory string, sr resolver.Secret) parser.IngressAnnotatio
// 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(authType, ing) at, err := parser.GetStringAnnotation("auth-type", ing, a.r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -117,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(authSecret, ing) s, err := parser.GetStringAnnotation("auth-secret", ing, a.r)
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"),
@ -125,14 +119,14 @@ func (a auth) Parse(ing *extensions.Ingress) (interface{}, error) {
} }
name := fmt.Sprintf("%v/%v", ing.Namespace, s) name := fmt.Sprintf("%v/%v", ing.Namespace, s)
secret, err := a.secretResolver.GetSecret(name) secret, err := a.r.GetSecret(name)
if err != nil { if err != nil {
return nil, ing_errors.LocationDenied{ return nil, ing_errors.LocationDenied{
Reason: errors.Wrapf(err, "unexpected error reading secret %v", name), Reason: errors.Wrapf(err, "unexpected error reading secret %v", name),
} }
} }
realm, _ := parser.GetStringAnnotation(authRealm, ing) realm, _ := parser.GetStringAnnotation("auth-realm", ing, a.r)
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/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -67,6 +68,7 @@ func buildIngress() *extensions.Ingress {
} }
type mockSecret struct { type mockSecret struct {
resolver.Mock
} }
func (m mockSecret) GetSecret(name string) (*api.Secret, error) { func (m mockSecret) GetSecret(name string) (*api.Secret, error) {
@ -87,7 +89,7 @@ func TestIngressWithoutAuth(t *testing.T) {
ing := buildIngress() ing := buildIngress()
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
_, err := NewParser(dir, mockSecret{}).Parse(ing) _, err := NewParser(dir, &mockSecret{}).Parse(ing)
if err == nil { if err == nil {
t.Error("Expected error with ingress without annotations") t.Error("Expected error with ingress without annotations")
} }
@ -97,15 +99,15 @@ func TestIngressAuth(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[authType] = "basic" data["nginx/auth-type"] = "basic"
data[authSecret] = "demo-secret" data["nginx/auth-secret"] = "demo-secret"
data[authRealm] = "-realm-" data["nginx/auth-realm"] = "-realm-"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
i, err := NewParser(dir, mockSecret{}).Parse(ing) i, err := NewParser(dir, &mockSecret{}).Parse(ing)
if err != nil { if err != nil {
t.Errorf("Uxpected error with ingress: %v", err) t.Errorf("Uxpected error with ingress: %v", err)
} }
@ -128,9 +130,9 @@ func TestIngressAuthWithoutSecret(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[authType] = "basic" data["nginx/auth-type"] = "basic"
data[authSecret] = "invalid-secret" data["nginx/auth-secret"] = "invalid-secret"
data[authRealm] = "-realm-" data["nginx/auth-realm"] = "-realm-"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t) _, dir, _ := dummySecretContent(t)

View file

@ -25,17 +25,10 @@ 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"
) )
const ( // Config returns external authentication configuration for an Ingress rule
// external URL that provides the authentication
authURL = "ingress.kubernetes.io/auth-url"
authSigninURL = "ingress.kubernetes.io/auth-signin"
authMethod = "ingress.kubernetes.io/auth-method"
authHeaders = "ingress.kubernetes.io/auth-response-headers"
)
// External returns external authentication configuration for an Ingress rule
type Config struct { type Config struct {
URL string `json:"url"` URL string `json:"url"`
// Host contains the hostname defined in the URL // Host contains the hostname defined in the URL
@ -108,17 +101,18 @@ 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() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return authReq{} return authReq{r}
} }
// 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(authURL, ing) str, err := parser.GetStringAnnotation("auth-url", ing, a.r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -127,7 +121,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(authSigninURL, ing) signin, _ := parser.GetStringAnnotation("auth-signin", ing, a.r)
ur, err := url.Parse(str) ur, err := url.Parse(str)
if err != nil { if err != nil {
@ -144,13 +138,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(authMethod, ing) m, _ := parser.GetStringAnnotation("auth-method", ing, a.r)
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(authHeaders, ing) hstr, _ := parser.GetStringAnnotation("auth-response-headers", ing, a.r)
if len(hstr) != 0 { if len(hstr) != 0 {
harr := strings.Split(hstr, ",") harr := strings.Split(hstr, ",")

View file

@ -24,6 +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/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
) )
@ -86,11 +87,11 @@ func TestAnnotations(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
data[authURL] = test.url data["nginx/auth-url"] = test.url
data[authSigninURL] = test.signinURL data["nginx/auth-signin"] = test.signinURL
data[authMethod] = fmt.Sprintf("%v", test.method) data["nginx/auth-method"] = fmt.Sprintf("%v", test.method)
i, err := NewParser().Parse(ing) i, err := NewParser(&resolver.Mock{}).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)
@ -136,11 +137,11 @@ func TestHeaderAnnotations(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
data[authURL] = test.url data["nginx/auth-url"] = test.url
data[authHeaders] = test.headers data["nginx/auth-response-headers"] = test.headers
data[authMethod] = "GET" data["nginx/auth-method"] = "GET"
i, err := NewParser().Parse(ing) i, err := NewParser(&resolver.Mock{}).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

@ -29,11 +29,6 @@ import (
) )
const ( const (
// name of the secret
annotationAuthTLSSecret = "ingress.kubernetes.io/auth-tls-secret"
annotationAuthVerifyClient = "ingress.kubernetes.io/auth-tls-verify-client"
annotationAuthTLSDepth = "ingress.kubernetes.io/auth-tls-verify-depth"
annotationAuthTLSErrorPage = "ingress.kubernetes.io/auth-tls-error-page"
defaultAuthTLSDepth = 1 defaultAuthTLSDepth = 1
defaultAuthVerifyClient = "on" defaultAuthVerifyClient = "on"
) )
@ -75,19 +70,19 @@ func (assl1 *Config) Equal(assl2 *Config) bool {
} }
// NewParser creates a new TLS authentication annotation parser // NewParser creates a new TLS authentication annotation parser
func NewParser(resolver resolver.AuthCertificate) parser.IngressAnnotation { func NewParser(resolver resolver.Resolver) parser.IngressAnnotation {
return authTLS{resolver} return authTLS{resolver}
} }
type authTLS struct { type authTLS struct {
certResolver resolver.AuthCertificate r resolver.Resolver
} }
// Parse parses the annotations contained in the ingress // Parse parses the annotations contained in the ingress
// 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(annotationAuthTLSSecret, ing) tlsauthsecret, err := parser.GetStringAnnotation(a.r.GetAnnotationWithPrefix("auth-tls-secret"), ing, a.r)
if err != nil { if err != nil {
return &Config{}, err return &Config{}, err
} }
@ -101,24 +96,24 @@ 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(annotationAuthVerifyClient, ing) tlsVerifyClient, err := parser.GetStringAnnotation("auth-tls-verify-client", ing, a.r)
if err != nil || !authVerifyClientRegex.MatchString(tlsVerifyClient) { if err != nil || !authVerifyClientRegex.MatchString(tlsVerifyClient) {
tlsVerifyClient = defaultAuthVerifyClient tlsVerifyClient = defaultAuthVerifyClient
} }
tlsdepth, err := parser.GetIntAnnotation(annotationAuthTLSDepth, ing) tlsdepth, err := parser.GetIntAnnotation("auth-tls-verify-depth", ing, a.r)
if err != nil || tlsdepth == 0 { if err != nil || tlsdepth == 0 {
tlsdepth = defaultAuthTLSDepth tlsdepth = defaultAuthTLSDepth
} }
authCert, err := a.certResolver.GetAuthCertificate(tlsauthsecret) authCert, err := a.r.GetAuthCertificate(tlsauthsecret)
if err != nil { if err != nil {
return &Config{}, ing_errors.LocationDenied{ return &Config{}, ing_errors.LocationDenied{
Reason: errors.Wrap(err, "error obtaining certificate"), Reason: errors.Wrap(err, "error obtaining certificate"),
} }
} }
errorpage, err := parser.GetStringAnnotation(annotationAuthTLSErrorPage, ing) errorpage, err := parser.GetStringAnnotation("auth-tls-error-page", ing, a.r)
if err != nil || errorpage == "" { if err != nil || errorpage == "" {
errorpage = "" errorpage = ""
} }

View file

@ -19,9 +19,6 @@ package class
import ( import (
"github.com/golang/glog" "github.com/golang/glog"
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/errors"
) )
const ( const (
@ -35,9 +32,9 @@ const (
// 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, controller, defClass string) bool {
ingress, err := parser.GetStringAnnotation(IngressKey, ing) ingress, ok := ing.GetAnnotations()[IngressKey]
if err != nil && !errors.IsMissingAnnotations(err) { if !ok {
glog.Warningf("unexpected error reading ingress annotation: %v", err) glog.V(3).Infof("annotation %v is not present in ingress %v/%v", IngressKey, ing.Namespace, ing.Name)
} }
// we have 2 valid combinations // we have 2 valid combinations

View file

@ -20,22 +20,20 @@ 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 (
annotation = "ingress.kubernetes.io/client-body-buffer-size"
) )
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() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return clientBodyBufferSize{} return clientBodyBufferSize{r}
} }
// 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 (a clientBodyBufferSize) Parse(ing *extensions.Ingress) (interface{}, error) { func (cbbs clientBodyBufferSize) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation(annotation, ing) return parser.GetStringAnnotation("client-body-buffer-size", ing, cbbs.r)
} }

View file

@ -22,10 +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"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
ap := NewParser() annotation := "nginx/client-body-buffer-size"
ap := NewParser(&resolver.Mock{})
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,14 +22,10 @@ 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 (
annotationCorsEnabled = "ingress.kubernetes.io/enable-cors"
annotationCorsAllowOrigin = "ingress.kubernetes.io/cors-allow-origin"
annotationCorsAllowMethods = "ingress.kubernetes.io/cors-allow-methods"
annotationCorsAllowHeaders = "ingress.kubernetes.io/cors-allow-headers"
annotationCorsAllowCredentials = "ingress.kubernetes.io/cors-allow-credentials"
// Default values // Default values
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"
@ -49,6 +45,7 @@ var (
) )
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
@ -61,8 +58,8 @@ type Config struct {
} }
// NewParser creates a new CORS annotation parser // NewParser creates a new CORS annotation parser
func NewParser() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return cors{} return cors{r}
} }
// Equal tests for equality between two External types // Equal tests for equality between two External types
@ -94,28 +91,28 @@ 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 (a cors) Parse(ing *extensions.Ingress) (interface{}, error) { func (c cors) Parse(ing *extensions.Ingress) (interface{}, error) {
corsenabled, err := parser.GetBoolAnnotation(annotationCorsEnabled, ing) corsenabled, err := parser.GetBoolAnnotation("enable-cors", ing, c.r)
if err != nil { if err != nil {
corsenabled = false corsenabled = false
} }
corsalloworigin, err := parser.GetStringAnnotation(annotationCorsAllowOrigin, ing) corsalloworigin, err := parser.GetStringAnnotation("cors-allow-origin", ing, c.r)
if err != nil || corsalloworigin == "" || !corsOriginRegex.MatchString(corsalloworigin) { if err != nil || corsalloworigin == "" || !corsOriginRegex.MatchString(corsalloworigin) {
corsalloworigin = "*" corsalloworigin = "*"
} }
corsallowheaders, err := parser.GetStringAnnotation(annotationCorsAllowHeaders, ing) corsallowheaders, err := parser.GetStringAnnotation("cors-allow-headers", ing, c.r)
if err != nil || corsallowheaders == "" || !corsHeadersRegex.MatchString(corsallowheaders) { if err != nil || corsallowheaders == "" || !corsHeadersRegex.MatchString(corsallowheaders) {
corsallowheaders = defaultCorsHeaders corsallowheaders = defaultCorsHeaders
} }
corsallowmethods, err := parser.GetStringAnnotation(annotationCorsAllowMethods, ing) corsallowmethods, err := parser.GetStringAnnotation("cors-allow-methods", ing, c.r)
if err != nil || corsallowmethods == "" || !corsMethodsRegex.MatchString(corsallowmethods) { if err != nil || corsallowmethods == "" || !corsMethodsRegex.MatchString(corsallowmethods) {
corsallowmethods = defaultCorsMethods corsallowmethods = defaultCorsMethods
} }
corsallowcredentials, err := parser.GetBoolAnnotation(annotationCorsAllowCredentials, ing) corsallowcredentials, err := parser.GetBoolAnnotation("cors-allow-credentials", ing, c.r)
if err != nil { if err != nil {
corsallowcredentials = true corsallowcredentials = true
} }

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/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -64,14 +65,14 @@ func TestIngressCorsConfig(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[annotationCorsEnabled] = "true" data["nginx/enable-cors"] = "true"
data[annotationCorsAllowHeaders] = "DNT,X-CustomHeader, Keep-Alive,User-Agent" data["nginx/cors-allow-headers"] = "DNT,X-CustomHeader, Keep-Alive,User-Agent"
data[annotationCorsAllowCredentials] = "false" data["nginx/cors-allow-credentials"] = "false"
data[annotationCorsAllowMethods] = "PUT, GET,OPTIONS, PATCH, $nginx_version" data["nginx/cors-allow-methods"] = "PUT, GET,OPTIONS, PATCH, $nginx_version"
data[annotationCorsAllowOrigin] = "https://origin123.test.com:4443" data["nginx/cors-allow-origin"] = "https://origin123.test.com:4443"
ing.SetAnnotations(data) ing.SetAnnotations(data)
corst, _ := NewParser().Parse(ing) corst, _ := NewParser(&resolver.Mock{}).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

@ -26,29 +26,25 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
defaultBackend = "ingress.kubernetes.io/default-backend"
)
type backend struct { type backend struct {
serviceResolver resolver.Service r resolver.Resolver
} }
// NewParser creates a new default backend annotation parser // NewParser creates a new default backend annotation parser
func NewParser(sr resolver.Service) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return backend{sr} return backend{r}
} }
// 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(defaultBackend, ing) s, err := parser.GetStringAnnotation("default-backend", ing, db.r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
name := fmt.Sprintf("%v/%v", ing.Namespace, s) name := fmt.Sprintf("%v/%v", ing.Namespace, s)
svc, err := db.serviceResolver.GetService(name) svc, err := db.r.GetService(name)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "unexpected error reading service %v", name) return nil, errors.Wrapf(err, "unexpected error reading service %v", name)
} }

View file

@ -23,11 +23,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
upsMaxFails = "ingress.kubernetes.io/upstream-max-fails"
upsFailTimeout = "ingress.kubernetes.io/upstream-fail-timeout"
)
// Config returns the URL and method to use check the status of // Config returns the URL and method to use check the status of
// the upstream server/s // the upstream server/s
type Config struct { type Config struct {
@ -36,28 +31,28 @@ type Config struct {
} }
type healthCheck struct { type healthCheck struct {
backendResolver resolver.DefaultBackend r resolver.Resolver
} }
// NewParser creates a new health check annotation parser // NewParser creates a new health check annotation parser
func NewParser(br resolver.DefaultBackend) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return healthCheck{br} return healthCheck{r}
} }
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
// rule used to configure upstream check parameters // rule used to configure upstream check parameters
func (a healthCheck) Parse(ing *extensions.Ingress) (interface{}, error) { func (hc healthCheck) Parse(ing *extensions.Ingress) (interface{}, error) {
defBackend := a.backendResolver.GetDefaultBackend() defBackend := hc.r.GetDefaultBackend()
if ing.GetAnnotations() == nil { if ing.GetAnnotations() == nil {
return &Config{defBackend.UpstreamMaxFails, defBackend.UpstreamFailTimeout}, nil return &Config{defBackend.UpstreamMaxFails, defBackend.UpstreamFailTimeout}, nil
} }
mf, err := parser.GetIntAnnotation(upsMaxFails, ing) mf, err := parser.GetIntAnnotation("upstream-max-fails", ing, hc.r)
if err != nil { if err != nil {
mf = defBackend.UpstreamMaxFails mf = defBackend.UpstreamMaxFails
} }
ft, err := parser.GetIntAnnotation(upsFailTimeout, ing) ft, err := parser.GetIntAnnotation("upstream-fail-timeout", ing, hc.r)
if err != nil { if err != nil {
ft = defBackend.UpstreamFailTimeout ft = defBackend.UpstreamFailTimeout
} }

View file

@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -63,6 +64,7 @@ func buildIngress() *extensions.Ingress {
} }
type mockBackend struct { type mockBackend struct {
resolver.Mock
} }
func (m mockBackend) GetDefaultBackend() defaults.Backend { func (m mockBackend) GetDefaultBackend() defaults.Backend {
@ -73,7 +75,7 @@ func TestIngressHealthCheck(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[upsMaxFails] = "2" data["nginx/upstream-max-fails"] = "2"
ing.SetAnnotations(data) ing.SetAnnotations(data)
hzi, _ := NewParser(mockBackend{}).Parse(ing) hzi, _ := NewParser(mockBackend{}).Parse(ing)

View file

@ -30,10 +30,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
whitelist = "ingress.kubernetes.io/whitelist-source-range"
)
// SourceRange returns the CIDR // SourceRange returns the CIDR
type SourceRange struct { type SourceRange struct {
CIDR []string `json:"cidr,omitEmpty"` CIDR []string `json:"cidr,omitEmpty"`
@ -69,12 +65,12 @@ func (sr1 *SourceRange) Equal(sr2 *SourceRange) bool {
} }
type ipwhitelist struct { type ipwhitelist struct {
backendResolver resolver.DefaultBackend r resolver.Resolver
} }
// NewParser creates a new whitelist annotation parser // NewParser creates a new whitelist annotation parser
func NewParser(br resolver.DefaultBackend) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return ipwhitelist{br} return ipwhitelist{r}
} }
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
@ -82,10 +78,10 @@ func NewParser(br resolver.DefaultBackend) parser.IngressAnnotation {
// Multiple ranges can specified using commas as separator // Multiple ranges can specified using commas as separator
// e.g. `18.0.0.0/8,56.0.0.0/8` // e.g. `18.0.0.0/8,56.0.0.0/8`
func (a ipwhitelist) Parse(ing *extensions.Ingress) (interface{}, error) { func (a ipwhitelist) Parse(ing *extensions.Ingress) (interface{}, error) {
defBackend := a.backendResolver.GetDefaultBackend() defBackend := a.r.GetDefaultBackend()
sort.Strings(defBackend.WhitelistSourceRange) sort.Strings(defBackend.WhitelistSourceRange)
val, err := parser.GetStringAnnotation(whitelist, ing) val, err := parser.GetStringAnnotation("whitelist-source-range", ing, a.r)
// 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,8 +23,8 @@ 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/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -62,14 +62,6 @@ func buildIngress() *extensions.Ingress {
} }
} }
type mockBackend struct {
defaults.Backend
}
func (m mockBackend) GetDefaultBackend() defaults.Backend {
return m.Backend
}
func TestParseAnnotations(t *testing.T) { func TestParseAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
tests := map[string]struct { tests := map[string]struct {
@ -102,9 +94,9 @@ func TestParseAnnotations(t *testing.T) {
for testName, test := range tests { for testName, test := range tests {
data := map[string]string{} data := map[string]string{}
data[whitelist] = test.net data["nginx/whitelist-source-range"] = test.net
ing.SetAnnotations(data) ing.SetAnnotations(data)
p := NewParser(mockBackend{}) p := NewParser(&resolver.Mock{})
i, err := p.Parse(ing) i, err := p.Parse(ing)
if err != nil && !test.expectErr { if err != nil && !test.expectErr {
t.Errorf("%v:unexpected error: %v", testName, err) t.Errorf("%v:unexpected error: %v", testName, err)
@ -126,12 +118,24 @@ func TestParseAnnotations(t *testing.T) {
} }
} }
type mockBackend struct {
resolver.Mock
}
// GetDefaultBackend returns the backend that must be used as default
func (m mockBackend) GetDefaultBackend() defaults.Backend {
return defaults.Backend{
WhitelistSourceRange: []string{"4.4.4.0/24", "1.2.3.4/32"},
}
}
// Test that when we have a whitelist set on the Backend that is used when we // Test that when we have a whitelist set on the Backend that is used when we
// don't have the annotation // don't have the annotation
func TestParseAnnotationsWithDefaultConfig(t *testing.T) { func TestParseAnnotationsWithDefaultConfig(t *testing.T) {
ing := buildIngress() ing := buildIngress()
mockBackend := mockBackend{} mockBackend := mockBackend{}
mockBackend.Backend.WhitelistSourceRange = []string{"4.4.4.0/24", "1.2.3.4/32"}
tests := map[string]struct { tests := map[string]struct {
net string net string
expectCidr []string expectCidr []string
@ -162,7 +166,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[whitelist] = test.net data["nginx/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

@ -22,6 +22,7 @@ import (
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"
) )
// IngressAnnotation has a method to parse annotations located in Ingress // IngressAnnotation has a method to parse annotations located in Ingress
@ -75,28 +76,31 @@ 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) (bool, error) { func GetBoolAnnotation(name string, ing *extensions.Ingress, r resolver.Resolver) (bool, error) {
err := checkAnnotation(name, ing) v := r.GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing)
if err != nil { if err != nil {
return false, err return false, err
} }
return ingAnnotations(ing.GetAnnotations()).parseBool(name) return ingAnnotations(ing.GetAnnotations()).parseBool(v)
} }
// GetStringAnnotation extracts a string from an Ingress annotation // GetStringAnnotation extracts a string from an Ingress annotation
func GetStringAnnotation(name string, ing *extensions.Ingress) (string, error) { func GetStringAnnotation(name string, ing *extensions.Ingress, r resolver.Resolver) (string, error) {
err := checkAnnotation(name, ing) v := r.GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing)
if err != nil { if err != nil {
return "", err return "", err
} }
return ingAnnotations(ing.GetAnnotations()).parseString(name) return ingAnnotations(ing.GetAnnotations()).parseString(v)
} }
// GetIntAnnotation extracts an int from an Ingress annotation // GetIntAnnotation extracts an int from an Ingress annotation
func GetIntAnnotation(name string, ing *extensions.Ingress) (int, error) { func GetIntAnnotation(name string, ing *extensions.Ingress, r resolver.Resolver) (int, error) {
err := checkAnnotation(name, ing) v := r.GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return ingAnnotations(ing.GetAnnotations()).parseInt(name) return ingAnnotations(ing.GetAnnotations()).parseInt(v)
} }

View file

@ -17,11 +17,13 @@ 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 {
@ -35,9 +37,11 @@ func buildIngress() *extensions.Ingress {
} }
func TestGetBoolAnnotation(t *testing.T) { func TestGetBoolAnnotation(t *testing.T) {
r := &resolver.Mock{}
ing := buildIngress() ing := buildIngress()
_, err := GetBoolAnnotation("", nil) _, err := GetBoolAnnotation("", nil, r)
if err == nil { if err == nil {
t.Errorf("expected error but retuned nil") t.Errorf("expected error but retuned nil")
} }
@ -49,8 +53,6 @@ func TestGetBoolAnnotation(t *testing.T) {
exp bool exp bool
expErr bool expErr bool
}{ }{
{"empty - false", "", "false", false, true},
{"empty - true", "", "true", false, true},
{"valid - false", "bool", "false", false, false}, {"valid - false", "bool", "false", false, false},
{"valid - true", "bool", "true", true, false}, {"valid - true", "bool", "true", true, false},
} }
@ -59,9 +61,9 @@ func TestGetBoolAnnotation(t *testing.T) {
ing.SetAnnotations(data) ing.SetAnnotations(data)
for _, test := range tests { for _, test := range tests {
data[test.field] = test.value data[fmt.Sprintf("nginx/%v", test.field)] = test.value
u, err := GetBoolAnnotation(test.field, ing) u, err := GetBoolAnnotation(test.field, ing, r)
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)
@ -77,9 +79,11 @@ 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) _, err := GetStringAnnotation("", nil, r)
if err == nil { if err == nil {
t.Errorf("expected error but retuned nil") t.Errorf("expected error but retuned nil")
} }
@ -91,8 +95,6 @@ func TestGetStringAnnotation(t *testing.T) {
exp string exp string
expErr bool expErr bool
}{ }{
{"empty - A", "", "A", "", true},
{"empty - B", "", "B", "", true},
{"valid - A", "string", "A", "A", false}, {"valid - A", "string", "A", "A", false},
{"valid - B", "string", "B", "B", false}, {"valid - B", "string", "B", "B", false},
} }
@ -101,9 +103,9 @@ func TestGetStringAnnotation(t *testing.T) {
ing.SetAnnotations(data) ing.SetAnnotations(data)
for _, test := range tests { for _, test := range tests {
data[test.field] = test.value data[fmt.Sprintf("nginx/%v", test.field)] = test.value
s, err := GetStringAnnotation(test.field, ing) s, err := GetStringAnnotation(test.field, ing, r)
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)
@ -119,9 +121,11 @@ 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) _, err := GetIntAnnotation("", nil, r)
if err == nil { if err == nil {
t.Errorf("expected error but retuned nil") t.Errorf("expected error but retuned nil")
} }
@ -133,8 +137,6 @@ func TestGetIntAnnotation(t *testing.T) {
exp int exp int
expErr bool expErr bool
}{ }{
{"empty - A", "", "1", 0, true},
{"empty - B", "", "2", 0, true},
{"valid - A", "string", "1", 1, false}, {"valid - A", "string", "1", 1, false},
{"valid - B", "string", "2", 2, false}, {"valid - B", "string", "2", 2, false},
} }
@ -143,9 +145,9 @@ func TestGetIntAnnotation(t *testing.T) {
ing.SetAnnotations(data) ing.SetAnnotations(data)
for _, test := range tests { for _, test := range tests {
data[test.field] = test.value data[fmt.Sprintf("nginx/%v", test.field)] = test.value
s, err := GetIntAnnotation(test.field, ing) s, err := GetIntAnnotation(test.field, ing, r)
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

@ -23,25 +23,21 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
annotation = "ingress.kubernetes.io/use-port-in-redirects"
)
type portInRedirect struct { type portInRedirect struct {
backendResolver resolver.DefaultBackend r resolver.Resolver
} }
// NewParser creates a new port in redirect annotation parser // NewParser creates a new port in redirect annotation parser
func NewParser(db resolver.DefaultBackend) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return portInRedirect{db} return portInRedirect{r}
} }
// 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(annotation, ing) up, err := parser.GetBoolAnnotation("use-port-in-redirects", ing, a.r)
if err != nil { if err != nil {
return a.backendResolver.GetDefaultBackend().UsePortInRedirects, nil return a.r.GetDefaultBackend().UsePortInRedirects, nil
} }
return up, nil return up, nil

View file

@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -64,6 +65,7 @@ func buildIngress() *extensions.Ingress {
} }
type mockBackend struct { type mockBackend struct {
resolver.Mock
usePortInRedirects bool usePortInRedirects bool
} }
@ -90,11 +92,11 @@ func TestPortInRedirect(t *testing.T) {
data := map[string]string{} data := map[string]string{}
if test.usePort != nil { if test.usePort != nil {
data[annotation] = fmt.Sprintf("%v", *test.usePort) data["nginx/use-port-in-redirects"] = fmt.Sprintf("%v", *test.usePort)
} }
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, err := NewParser(mockBackend{test.def}).Parse(ing) i, err := NewParser(mockBackend{usePortInRedirects: test.def}).Parse(ing)
if err != nil { if err != nil {
t.Errorf("unexpected error parsing a valid") t.Errorf("unexpected error parsing a valid")
} }

View file

@ -23,19 +23,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
bodySize = "ingress.kubernetes.io/proxy-body-size"
connect = "ingress.kubernetes.io/proxy-connect-timeout"
send = "ingress.kubernetes.io/proxy-send-timeout"
read = "ingress.kubernetes.io/proxy-read-timeout"
bufferSize = "ingress.kubernetes.io/proxy-buffer-size"
cookiePath = "ingress.kubernetes.io/proxy-cookie-path"
cookieDomain = "ingress.kubernetes.io/proxy-cookie-domain"
nextUpstream = "ingress.kubernetes.io/proxy-next-upstream"
passParams = "ingress.kubernetes.io/proxy-pass-params"
requestBuffering = "ingress.kubernetes.io/proxy-request-buffering"
)
// Config returns the proxy timeout to use in the upstream server/s // Config returns the proxy timeout to use in the upstream server/s
type Config struct { type Config struct {
BodySize string `json:"bodySize"` BodySize string `json:"bodySize"`
@ -94,64 +81,64 @@ func (l1 *Config) Equal(l2 *Config) bool {
} }
type proxy struct { type proxy struct {
backendResolver resolver.DefaultBackend r resolver.Resolver
} }
// NewParser creates a new reverse proxy configuration annotation parser // NewParser creates a new reverse proxy configuration annotation parser
func NewParser(br resolver.DefaultBackend) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return proxy{br} return proxy{r}
} }
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
// 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.backendResolver.GetDefaultBackend() defBackend := a.r.GetDefaultBackend()
ct, err := parser.GetIntAnnotation(connect, ing) ct, err := parser.GetIntAnnotation("proxy-connect-timeout", ing, a.r)
if err != nil { if err != nil {
ct = defBackend.ProxyConnectTimeout ct = defBackend.ProxyConnectTimeout
} }
st, err := parser.GetIntAnnotation(send, ing) st, err := parser.GetIntAnnotation("proxy-send-timeout", ing, a.r)
if err != nil { if err != nil {
st = defBackend.ProxySendTimeout st = defBackend.ProxySendTimeout
} }
rt, err := parser.GetIntAnnotation(read, ing) rt, err := parser.GetIntAnnotation("proxy-read-timeout", ing, a.r)
if err != nil { if err != nil {
rt = defBackend.ProxyReadTimeout rt = defBackend.ProxyReadTimeout
} }
bufs, err := parser.GetStringAnnotation(bufferSize, ing) bufs, err := parser.GetStringAnnotation("proxy-buffer-size", ing, a.r)
if err != nil || bufs == "" { if err != nil || bufs == "" {
bufs = defBackend.ProxyBufferSize bufs = defBackend.ProxyBufferSize
} }
cp, err := parser.GetStringAnnotation(cookiePath, ing) cp, err := parser.GetStringAnnotation("proxy-cookie-path", ing, a.r)
if err != nil || cp == "" { if err != nil || cp == "" {
cp = defBackend.ProxyCookiePath cp = defBackend.ProxyCookiePath
} }
cd, err := parser.GetStringAnnotation(cookieDomain, ing) cd, err := parser.GetStringAnnotation("proxy-cookie-domain", ing, a.r)
if err != nil || cd == "" { if err != nil || cd == "" {
cd = defBackend.ProxyCookieDomain cd = defBackend.ProxyCookieDomain
} }
bs, err := parser.GetStringAnnotation(bodySize, ing) bs, err := parser.GetStringAnnotation("proxy-body-size", ing, a.r)
if err != nil || bs == "" { if err != nil || bs == "" {
bs = defBackend.ProxyBodySize bs = defBackend.ProxyBodySize
} }
nu, err := parser.GetStringAnnotation(nextUpstream, ing) nu, err := parser.GetStringAnnotation("proxy-next-upstream", ing, a.r)
if err != nil || nu == "" { if err != nil || nu == "" {
nu = defBackend.ProxyNextUpstream nu = defBackend.ProxyNextUpstream
} }
pp, err := parser.GetStringAnnotation(passParams, ing) pp, err := parser.GetStringAnnotation("proxy-pass-params", ing, a.r)
if err != nil || pp == "" { if err != nil || pp == "" {
pp = defBackend.ProxyPassParams pp = defBackend.ProxyPassParams
} }
rb, err := parser.GetStringAnnotation(requestBuffering, ing) rb, err := parser.GetStringAnnotation("proxy-request-buffering", ing, a.r)
if err != nil || rb == "" { if err != nil || rb == "" {
rb = defBackend.ProxyRequestBuffering rb = defBackend.ProxyRequestBuffering
} }

View file

@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -63,6 +64,7 @@ func buildIngress() *extensions.Ingress {
} }
type mockBackend struct { type mockBackend struct {
resolver.Mock
} }
func (m mockBackend) GetDefaultBackend() defaults.Backend { func (m mockBackend) GetDefaultBackend() defaults.Backend {
@ -83,14 +85,14 @@ func TestProxy(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[connect] = "1" data["nginx/proxy-connect-timeout"] = "1"
data[send] = "2" data["nginx/proxy-send-timeout"] = "2"
data[read] = "3" data["nginx/proxy-read-timeout"] = "3"
data[bufferSize] = "1k" data["nginx/proxy-buffer-size"] = "1k"
data[bodySize] = "2k" data["nginx/proxy-body-size"] = "2k"
data[nextUpstream] = "off" data["nginx/proxy-next-upstream"] = "off"
data[passParams] = "smax=5 max=10" data["nginx/proxy-pass-params"] = "smax=5 max=10"
data[requestBuffering] = "off" data["nginx/proxy-request-buffering"] = "off"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, err := NewParser(mockBackend{}).Parse(ing) i, err := NewParser(mockBackend{}).Parse(ing)

View file

@ -30,13 +30,6 @@ import (
) )
const ( const (
limitIP = "ingress.kubernetes.io/limit-connections"
limitRPS = "ingress.kubernetes.io/limit-rps"
limitRPM = "ingress.kubernetes.io/limit-rpm"
limitRATE = "ingress.kubernetes.io/limit-rate"
limitRATEAFTER = "ingress.kubernetes.io/limit-rate-after"
limitWhitelist = "ingress.kubernetes.io/limit-whitelist"
// allow 5 times the specified limit as burst // allow 5 times the specified limit as burst
defBurst = 5 defBurst = 5
@ -152,32 +145,32 @@ func (z1 *Zone) Equal(z2 *Zone) bool {
} }
type ratelimit struct { type ratelimit struct {
backendResolver resolver.DefaultBackend r resolver.Resolver
} }
// NewParser creates a new ratelimit annotation parser // NewParser creates a new ratelimit annotation parser
func NewParser(br resolver.DefaultBackend) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return ratelimit{br} return ratelimit{r}
} }
// 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 ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) { func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
defBackend := a.backendResolver.GetDefaultBackend() defBackend := a.r.GetDefaultBackend()
lr, err := parser.GetIntAnnotation(limitRATE, ing) lr, err := parser.GetIntAnnotation("limit-rate", ing, a.r)
if err != nil { if err != nil {
lr = defBackend.LimitRate lr = defBackend.LimitRate
} }
lra, err := parser.GetIntAnnotation(limitRATEAFTER, ing) lra, err := parser.GetIntAnnotation("limit-rate-after", ing, a.r)
if err != nil { if err != nil {
lra = defBackend.LimitRateAfter lra = defBackend.LimitRateAfter
} }
rpm, _ := parser.GetIntAnnotation(limitRPM, ing) rpm, _ := parser.GetIntAnnotation("limit-rpm", ing, a.r)
rps, _ := parser.GetIntAnnotation(limitRPS, ing) rps, _ := parser.GetIntAnnotation("limit-rps", ing, a.r)
conn, _ := parser.GetIntAnnotation(limitIP, ing) conn, _ := parser.GetIntAnnotation("limit-connections", ing, a.r)
val, _ := parser.GetStringAnnotation(limitWhitelist, ing) val, _ := parser.GetStringAnnotation("limit-whitelist", ing, a.r)
cidrs, err := parseCIDRs(val) cidrs, err := parseCIDRs(val)
if err != nil { if err != nil {

View file

@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -63,6 +64,7 @@ func buildIngress() *extensions.Ingress {
} }
type mockBackend struct { type mockBackend struct {
resolver.Mock
} }
func (m mockBackend) GetDefaultBackend() defaults.Backend { func (m mockBackend) GetDefaultBackend() defaults.Backend {
@ -84,9 +86,9 @@ func TestBadRateLimiting(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[limitIP] = "0" data["nginx/limit-connections"] = "0"
data[limitRPS] = "0" data["nginx/limit-rps"] = "0"
data[limitRPM] = "0" data["nginx/limit-rpm"] = "0"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, err := NewParser(mockBackend{}).Parse(ing) _, err := NewParser(mockBackend{}).Parse(ing)
@ -95,11 +97,11 @@ func TestBadRateLimiting(t *testing.T) {
} }
data = map[string]string{} data = map[string]string{}
data[limitIP] = "5" data["nginx/limit-connections"] = "5"
data[limitRPS] = "100" data["nginx/limit-rps"] = "100"
data[limitRPM] = "10" data["nginx/limit-rpm"] = "10"
data[limitRATEAFTER] = "100" data["nginx/limit-rate-after"] = "100"
data[limitRATE] = "10" data["nginx/limit-rate"] = "10"
ing.SetAnnotations(data) ing.SetAnnotations(data)

View file

@ -25,12 +25,7 @@ import (
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/errors"
) "k8s.io/ingress-nginx/internal/ingress/resolver"
const (
permanent = "ingress.kubernetes.io/permanent-redirect"
temporal = "ingress.kubernetes.io/temporal-redirect"
www = "ingress.kubernetes.io/from-to-www-redirect"
) )
// Config returns the redirect configuration for an Ingress rule // Config returns the redirect configuration for an Ingress rule
@ -40,11 +35,13 @@ type Config struct {
FromToWWW bool `json:"fromToWWW"` FromToWWW bool `json:"fromToWWW"`
} }
type redirect struct{} type redirect struct {
r resolver.Resolver
}
// NewParser creates a new redirect annotation parser // NewParser creates a new redirect annotation parser
func NewParser() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return redirect{} return redirect{r}
} }
// Parse parses the annotations contained in the ingress // Parse parses the annotations contained in the ingress
@ -52,9 +49,9 @@ func NewParser() 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(www, ing) r3w, _ := parser.GetBoolAnnotation("from-to-www-redirect", ing, a.r)
tr, err := parser.GetStringAnnotation(temporal, ing) tr, err := parser.GetStringAnnotation("temporal-redirect", ing, a.r)
if err != nil && !errors.IsMissingAnnotations(err) { if err != nil && !errors.IsMissingAnnotations(err) {
return nil, err return nil, err
} }
@ -71,7 +68,7 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) {
}, nil }, nil
} }
pr, err := parser.GetStringAnnotation(permanent, ing) pr, err := parser.GetStringAnnotation("permanent-redirect", ing, a.r)
if err != nil && !errors.IsMissingAnnotations(err) { if err != nil && !errors.IsMissingAnnotations(err) {
return nil, err return nil, err
} }

View file

@ -23,15 +23,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
rewriteTo = "ingress.kubernetes.io/rewrite-target"
addBaseURL = "ingress.kubernetes.io/add-base-url"
baseURLScheme = "ingress.kubernetes.io/base-url-scheme"
sslRedirect = "ingress.kubernetes.io/ssl-redirect"
forceSSLRedirect = "ingress.kubernetes.io/force-ssl-redirect"
appRoot = "ingress.kubernetes.io/app-root"
)
// Config describes the per location redirect config // Config describes the per location redirect config
type Config struct { type Config struct {
// Target URI where the traffic must be redirected // Target URI where the traffic must be redirected
@ -80,29 +71,30 @@ func (r1 *Config) Equal(r2 *Config) bool {
} }
type rewrite struct { type rewrite struct {
backendResolver resolver.DefaultBackend r resolver.Resolver
} }
// NewParser creates a new reqrite annotation parser // NewParser creates a new reqrite annotation parser
func NewParser(br resolver.DefaultBackend) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return rewrite{br} return rewrite{r}
} }
// 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(rewriteTo, ing) rt, _ := parser.GetStringAnnotation("rewrite-target", ing, a.r)
sslRe, err := parser.GetBoolAnnotation(sslRedirect, ing) sslRe, err := parser.GetBoolAnnotation("ssl-redirect", ing, a.r)
if err != nil { if err != nil {
sslRe = a.backendResolver.GetDefaultBackend().SSLRedirect sslRe = a.r.GetDefaultBackend().SSLRedirect
} }
fSslRe, err := parser.GetBoolAnnotation(forceSSLRedirect, ing) fSslRe, err := parser.GetBoolAnnotation("force-ssl-redirect", ing, a.r)
if err != nil { if err != nil {
fSslRe = a.backendResolver.GetDefaultBackend().ForceSSLRedirect fSslRe = a.r.GetDefaultBackend().ForceSSLRedirect
} }
abu, _ := parser.GetBoolAnnotation(addBaseURL, ing) abu, _ := parser.GetBoolAnnotation("add-base-url", ing, a.r)
bus, _ := parser.GetStringAnnotation(baseURLScheme, ing) bus, _ := parser.GetStringAnnotation("base-url-scheme", ing, a.r)
ar, _ := parser.GetStringAnnotation(appRoot, ing) ar, _ := parser.GetStringAnnotation("app-root", ing, a.r)
return &Config{ return &Config{
Target: rt, Target: rt,
AddBaseURL: abu, AddBaseURL: abu,

View file

@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const ( const (
@ -67,6 +68,7 @@ func buildIngress() *extensions.Ingress {
} }
type mockBackend struct { type mockBackend struct {
resolver.Mock
redirect bool redirect bool
} }
@ -86,7 +88,7 @@ func TestRedirect(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[rewriteTo] = defRoute data["nginx/rewrite-target"] = defRoute
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, err := NewParser(mockBackend{}).Parse(ing) i, err := NewParser(mockBackend{}).Parse(ing)
@ -106,10 +108,10 @@ func TestSSLRedirect(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[rewriteTo] = defRoute data["nginx/rewrite-target"] = defRoute
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{true}).Parse(ing) i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
redirect, ok := i.(*Config) redirect, ok := i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -118,10 +120,10 @@ func TestSSLRedirect(t *testing.T) {
t.Errorf("Expected true but returned false") t.Errorf("Expected true but returned false")
} }
data[sslRedirect] = "false" data["nginx/ssl-redirect"] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{false}).Parse(ing) i, _ = NewParser(mockBackend{redirect: false}).Parse(ing)
redirect, ok = i.(*Config) redirect, ok = i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -135,10 +137,10 @@ func TestForceSSLRedirect(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[rewriteTo] = defRoute data["nginx/rewrite-target"] = defRoute
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{true}).Parse(ing) i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
redirect, ok := i.(*Config) redirect, ok := i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -147,10 +149,10 @@ func TestForceSSLRedirect(t *testing.T) {
t.Errorf("Expected false but returned true") t.Errorf("Expected false but returned true")
} }
data[forceSSLRedirect] = "true" data["nginx/force-ssl-redirect"] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{false}).Parse(ing) i, _ = NewParser(mockBackend{redirect: false}).Parse(ing)
redirect, ok = i.(*Config) redirect, ok = i.(*Config)
if !ok { if !ok {
t.Errorf("expected a Redirect type") t.Errorf("expected a Redirect type")
@ -163,10 +165,10 @@ func TestAppRoot(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[appRoot] = "/app1" data["nginx/app-root"] = "/app1"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{true}).Parse(ing) i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
redirect, ok := i.(*Config) redirect, ok := i.(*Config)
if !ok { if !ok {
t.Errorf("expected a App Context") t.Errorf("expected a App Context")

View file

@ -26,11 +26,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
) )
const (
secureUpstream = "ingress.kubernetes.io/secure-backends"
secureVerifyCASecret = "ingress.kubernetes.io/secure-verify-ca-secret"
)
// Config describes SSL backend configuration // Config describes SSL backend configuration
type Config struct { type Config struct {
Secure bool `json:"secure"` Secure bool `json:"secure"`
@ -38,21 +33,19 @@ type Config struct {
} }
type su struct { type su struct {
certResolver resolver.AuthCertificate r resolver.Resolver
} }
// NewParser creates a new secure upstream annotation parser // NewParser creates a new secure upstream annotation parser
func NewParser(resolver resolver.AuthCertificate) parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return su{ return su{r}
certResolver: resolver,
}
} }
// 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(secureUpstream, ing) s, _ := parser.GetBoolAnnotation("secure-backends", ing, a.r)
ca, _ := parser.GetStringAnnotation(secureVerifyCASecret, ing) ca, _ := parser.GetStringAnnotation("secure-verify-ca-secret", ing, a.r)
secure := &Config{ secure := &Config{
Secure: s, Secure: s,
CACert: resolver.AuthSSLCert{}, CACert: resolver.AuthSSLCert{},
@ -64,7 +57,7 @@ func (a su) Parse(ing *extensions.Ingress) (interface{}, error) {
if ca == "" { if ca == "" {
return secure, nil return secure, nil
} }
caCert, err := a.certResolver.GetAuthCertificate(fmt.Sprintf("%v/%v", ing.Namespace, ca)) caCert, err := a.r.GetAuthCertificate(fmt.Sprintf("%v/%v", ing.Namespace, ca))
if err != nil { if err != nil {
return secure, errors.Wrap(err, "error obtaining certificate") return secure, errors.Wrap(err, "error obtaining certificate")
} }

View file

@ -64,6 +64,7 @@ func buildIngress() *extensions.Ingress {
} }
type mockCfg struct { type mockCfg struct {
resolver.Mock
certs map[string]resolver.AuthSSLCert certs map[string]resolver.AuthSSLCert
} }
@ -77,8 +78,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[secureUpstream] = "true" data["nginx/secure-backends"] = "true"
data[secureVerifyCASecret] = "secure-verify-ca" data["nginx/secure-verify-ca-secret"] = "secure-verify-ca"
ing.SetAnnotations(data) ing.SetAnnotations(data)
_, err := NewParser(mockCfg{ _, err := NewParser(mockCfg{
@ -94,8 +95,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[secureUpstream] = "true" data["nginx/secure-backends"] = "true"
data[secureVerifyCASecret] = "secure-verify-ca" data["nginx/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 {
@ -106,8 +107,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[secureUpstream] = "false" data["nginx/secure-backends"] = "false"
data[secureVerifyCASecret] = "secure-verify-ca" data["nginx/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,23 +20,21 @@ 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 (
annotation = "ingress.kubernetes.io/server-snippet"
) )
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() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return serverSnippet{} return serverSnippet{r}
} }
// 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(annotation, ing) return parser.GetStringAnnotation("server-snippet", ing, a.r)
} }

View file

@ -22,10 +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"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
ap := NewParser() annotation := "nginx/server-snippet"
ap := NewParser(&resolver.Mock{})
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

@ -18,21 +18,20 @@ package serviceupstream
import ( import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
)
const ( "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
annotationServiceUpstream = "ingress.kubernetes.io/service-upstream" "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() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return serviceUpstream{} return serviceUpstream{r}
} }
func (s serviceUpstream) Parse(ing *extensions.Ingress) (interface{}, error) { func (s serviceUpstream) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetBoolAnnotation(annotationServiceUpstream, ing) return parser.GetBoolAnnotation("service-upstream", ing, s.r)
} }

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/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -64,10 +65,10 @@ func TestIngressAnnotationServiceUpstreamEnabled(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[annotationServiceUpstream] = "true" data["nginx/service-upstream"] = "true"
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser().Parse(ing) val, _ := NewParser(&resolver.Mock{}).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")
@ -83,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[annotationServiceUpstream] = "false" data["nginx/service-upstream"] = "false"
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ := NewParser().Parse(ing) val, _ := NewParser(&resolver.Mock{}).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")
@ -100,7 +101,7 @@ func TestIngressAnnotationServiceUpstreamSetFalse(t *testing.T) {
data = map[string]string{} data = map[string]string{}
ing.SetAnnotations(data) ing.SetAnnotations(data)
val, _ = NewParser().Parse(ing) val, _ = NewParser(&resolver.Mock{}).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

@ -24,17 +24,20 @@ 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 (
annotationAffinityType = "ingress.kubernetes.io/affinity" annotationAffinityType = "affinity"
// If a cookie with this name exists, // If a cookie with this name exists,
// its value is used as an index into the list of available backends. // its value is used as an index into the list of available backends.
annotationAffinityCookieName = "ingress.kubernetes.io/session-cookie-name" annotationAffinityCookieName = "session-cookie-name"
defaultAffinityCookieName = "INGRESSCOOKIE" defaultAffinityCookieName = "INGRESSCOOKIE"
// This is the algorithm used by nginx to generate a value for the session cookie, if // This is the algorithm used by nginx to generate a value for the session cookie, if
// one isn't supplied and affinity is set to "cookie". // one isn't supplied and affinity is set to "cookie".
annotationAffinityCookieHash = "ingress.kubernetes.io/session-cookie-hash" annotationAffinityCookieHash = "session-cookie-hash"
defaultAffinityCookieHash = "md5" defaultAffinityCookieHash = "md5"
) )
@ -59,16 +62,15 @@ 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 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) sh, err := parser.GetStringAnnotation(annotationAffinityCookieHash, ing, a.r)
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)
@ -82,11 +84,12 @@ func cookieAffinityParse(ing *extensions.Ingress) *Cookie {
} }
// NewParser creates a new Affinity annotation parser // NewParser creates a new Affinity annotation parser
func NewParser() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return affinity{} return affinity{r}
} }
type affinity struct { type affinity struct {
r resolver.Resolver
} }
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
@ -94,14 +97,14 @@ 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) at, err := parser.GetStringAnnotation(annotationAffinityType, ing, a.r)
if err != nil { if err != nil {
at = "" at = ""
} }
switch at { switch at {
case "cookie": case "cookie":
cookie = cookieAffinityParse(ing) cookie = a.cookieAffinityParse(ing)
default: default:
glog.V(3).Infof("No default affinity was found for Ingress %v", ing.Name) glog.V(3).Infof("No default affinity was found for Ingress %v", ing.Name)

View file

@ -17,12 +17,14 @@ 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/resolver"
) )
func buildIngress() *extensions.Ingress { func buildIngress() *extensions.Ingress {
@ -64,12 +66,12 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}
data[annotationAffinityType] = "cookie" data[fmt.Sprintf("nginx/%v", annotationAffinityType)] = "cookie"
data[annotationAffinityCookieHash] = "sha123" data[fmt.Sprintf("nginx/%v", annotationAffinityCookieHash)] = "sha123"
data[annotationAffinityCookieName] = "INGRESSCOOKIE" data[fmt.Sprintf("nginx/%v", annotationAffinityCookieName)] = "INGRESSCOOKIE"
ing.SetAnnotations(data) ing.SetAnnotations(data)
affin, _ := NewParser().Parse(ing) affin, _ := NewParser(&resolver.Mock{}).Parse(ing)
nginxAffinity, ok := affin.(*Config) nginxAffinity, ok := affin.(*Config)
if !ok { if !ok {
t.Errorf("expected a Config type") t.Errorf("expected a Config type")

View file

@ -20,23 +20,21 @@ 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 (
annotation = "ingress.kubernetes.io/configuration-snippet"
) )
type snippet struct { type snippet struct {
r resolver.Resolver
} }
// NewParser creates a new CORS annotation parser // NewParser creates a new CORS annotation parser
func NewParser() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return snippet{} return snippet{r}
} }
// 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(annotation, ing) return parser.GetStringAnnotation("configuration-snippet", ing, a.r)
} }

View file

@ -22,10 +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"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
ap := NewParser() annotation := "nginx/configuration-snippet"
ap := NewParser(&resolver.Mock{})
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,18 +21,16 @@ 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"
const (
passthrough = "ingress.kubernetes.io/ssl-passthrough"
) )
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() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return sslpt{} return sslpt{r}
} }
// ParseAnnotations parses the annotations contained in the ingress // ParseAnnotations parses the annotations contained in the ingress
@ -42,5 +40,5 @@ func (a sslpt) Parse(ing *extensions.Ingress) (interface{}, error) {
return false, ing_errors.ErrMissingAnnotations return false, ing_errors.ErrMissingAnnotations
} }
return parser.GetBoolAnnotation(passthrough, ing) return parser.GetBoolAnnotation("ssl-passthrough", ing, a.r)
} }

View file

@ -22,6 +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/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
) )
@ -44,16 +45,16 @@ func buildIngress() *extensions.Ingress {
func TestParseAnnotations(t *testing.T) { func TestParseAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
_, err := NewParser().Parse(ing) _, err := NewParser(&resolver.Mock{}).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[passthrough] = "true" data["nginx/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().Parse(ing) _, err = NewParser(&resolver.Mock{}).Parse(ing)
if err != nil { if err != nil {
t.Errorf("unexpected error parsing ingress with sslpassthrough") t.Errorf("unexpected error parsing ingress with sslpassthrough")
} }
@ -64,7 +65,7 @@ func TestParseAnnotations(t *testing.T) {
Hosts: []string{"foo.bar.com"}, Hosts: []string{"foo.bar.com"},
}, },
} }
i, err := NewParser().Parse(ing) i, err := NewParser(&resolver.Mock{}).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,23 +20,21 @@ 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 (
annotation = "ingress.kubernetes.io/upstream-hash-by"
) )
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() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return upstreamhashby{} return upstreamhashby{r}
} }
// 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(annotation, ing) return parser.GetStringAnnotation("upstream-hash-by", ing, a.r)
} }

View file

@ -22,10 +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"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
ap := NewParser() annotation := "nginx/upstream-hash-by"
ap := NewParser(&resolver.Mock{})
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,23 +20,21 @@ 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 (
annotation = "ingress.kubernetes.io/upstream-vhost"
) )
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() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return upstreamVhost{} return upstreamVhost{r}
} }
// 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(annotation, ing) return parser.GetStringAnnotation("upstream-vhost", ing, a.r)
} }

View file

@ -20,23 +20,21 @@ 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 (
annotation = "ingress.kubernetes.io/vts-filter-key"
) )
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() parser.IngressAnnotation { func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return vtsFilterKey{} return vtsFilterKey{r}
} }
// 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 vtsFilterKey) Parse(ing *extensions.Ingress) (interface{}, error) { func (a vtsFilterKey) Parse(ing *extensions.Ingress) (interface{}, error) {
return parser.GetStringAnnotation(annotation, ing) return parser.GetStringAnnotation("vts-filter-key", ing, a.r)
} }

View file

@ -92,7 +92,7 @@ func (ic *NGINXController) getPemCertificate(secretName string) (*ingress.SSLCer
} }
// If 'ca.crt' is also present, it will allow this secret to be used in the // If 'ca.crt' is also present, it will allow this secret to be used in the
// 'ingress.kubernetes.io/auth-tls-secret' annotation // 'nginx.ingress.kubernetes.io/auth-tls-secret' annotation
s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca) s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca)
if err != nil { if err != nil {
return nil, fmt.Errorf("unexpected error creating pem file: %v", err) return nil, fmt.Errorf("unexpected error creating pem file: %v", err)
@ -145,7 +145,7 @@ func (ic *NGINXController) checkMissingSecrets() {
} }
} }
key, _ := parser.GetStringAnnotation("ingress.kubernetes.io/auth-tls-secret", ing) key, _ := parser.GetStringAnnotation("auth-tls-secret", ing, ic)
if key == "" { if key == "" {
continue continue
} }

View file

@ -66,6 +66,8 @@ 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
@ -133,6 +135,11 @@ func (n NGINXController) GetService(name string) (*apiv1.Service, error) {
return n.listers.Service.GetByName(name) return n.listers.Service.GetByName(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
// then sends the content to the backend (OnUpdate) receiving the populated // then sends the content to the backend (OnUpdate) receiving the populated
// template as response reloading the backend if is required. // template as response reloading the backend if is required.
@ -1156,7 +1163,7 @@ func (n *NGINXController) readSecrets(ing *extensions.Ingress) {
n.syncSecret(key) n.syncSecret(key)
} }
key, _ := parser.GetStringAnnotation("ingress.kubernetes.io/auth-tls-secret", ing) key, _ := parser.GetStringAnnotation("auth-tls-secret", ing, n)
if key == "" { if key == "" {
return return
} }

View file

@ -66,7 +66,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) (*ingress.StoreLis
AddFunc: func(obj interface{}) { AddFunc: func(obj interface{}) {
addIng := obj.(*extensions.Ingress) addIng := obj.(*extensions.Ingress)
if !class.IsValid(addIng, n.cfg.IngressClass, defIngressClass) { if !class.IsValid(addIng, n.cfg.IngressClass, defIngressClass) {
a, _ := parser.GetStringAnnotation(class.IngressKey, addIng) 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) glog.Infof("ignoring add for ingress %v based on annotation %v with value %v", addIng.Name, class.IngressKey, a)
return return
} }

View file

@ -246,7 +246,7 @@ func (n *NGINXController) Start() {
ing := obj.(*extensions.Ingress) ing := obj.(*extensions.Ingress)
if !class.IsValid(ing, n.cfg.IngressClass, n.cfg.DefaultIngressClass) { if !class.IsValid(ing, n.cfg.IngressClass, n.cfg.DefaultIngressClass) {
a, _ := parser.GetStringAnnotation(class.IngressKey, ing) 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) glog.Infof("ignoring add for ingress %v based on annotation %v with value %v", ing.Name, class.IngressKey, a)
continue continue
} }

View file

@ -193,7 +193,7 @@ func buildResolvers(input interface{}) string {
} }
// buildLocation produces the location string, if the ingress has redirects // buildLocation produces the location string, if the ingress has redirects
// (specified through the ingress.kubernetes.io/rewrite-to annotation) // (specified through the nginx.ingress.kubernetes.io/rewrite-to annotation)
func buildLocation(input interface{}) string { func buildLocation(input interface{}) string {
location, ok := input.(*ingress.Location) location, ok := input.(*ingress.Location)
if !ok { if !ok {
@ -268,8 +268,8 @@ func buildLogFormatUpstream(input interface{}) string {
} }
// buildProxyPass produces the proxy pass string, if the ingress has redirects // buildProxyPass produces the proxy pass string, if the ingress has redirects
// (specified through the ingress.kubernetes.io/rewrite-to annotation) // (specified through the nginx.ingress.kubernetes.io/rewrite-to annotation)
// If the annotation ingress.kubernetes.io/add-base-url:"true" is specified it will // If the annotation nginx.ingress.kubernetes.io/add-base-url:"true" is specified it will
// add a base tag in the head of the response from the service // add a base tag in the head of the response from the service
func buildProxyPass(host string, b interface{}, loc interface{}) string { func buildProxyPass(host string, b interface{}, loc interface{}) string {
backends, ok := b.([]*ingress.Backend) backends, ok := b.([]*ingress.Backend)

View file

@ -22,29 +22,24 @@ import (
"k8s.io/ingress-nginx/internal/ingress/defaults" "k8s.io/ingress-nginx/internal/ingress/defaults"
) )
// DefaultBackend has a method that returns the backend // Resolver is an interface that knows how to extract information from a controller
// that must be used as default type Resolver interface {
type DefaultBackend interface { // GetDefaultBackend returns the backend that must be used as default
GetDefaultBackend() defaults.Backend GetDefaultBackend() defaults.Backend
}
// Secret has a method that searches for secrets contenating // GetSecret searches for secrets contenating the namespace and name using a the character /
// the namespace and name using a the character /
type Secret interface {
GetSecret(string) (*apiv1.Secret, error) GetSecret(string) (*apiv1.Secret, error)
}
// AuthCertificate resolves a given secret name into an SSL certificate. // GetAuthCertificate resolves a given secret name into an SSL certificate.
// The secret must contain 3 keys named: // The secret must contain 3 keys named:
// ca.crt: contains the certificate chain used for authentication // ca.crt: contains the certificate chain used for authentication
type AuthCertificate interface {
GetAuthCertificate(string) (*AuthSSLCert, error) GetAuthCertificate(string) (*AuthSSLCert, error)
}
// Service has a method that searches for services contenating // GetService searches for services contenating the namespace and name using a the character /
// the namespace and name using a the character /
type Service interface {
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

@ -0,0 +1,56 @@
/*
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 resolver
import (
"fmt"
apiv1 "k8s.io/api/core/v1"
"k8s.io/ingress-nginx/internal/ingress/defaults"
)
// Mock implements the Resolver interface
type Mock struct {
}
// GetDefaultBackend returns the backend that must be used as default
func (m Mock) GetDefaultBackend() defaults.Backend {
return defaults.Backend{}
}
// GetSecret searches for secrets contenating the namespace and name using a the character /
func (m Mock) GetSecret(string) (*apiv1.Secret, error) {
return nil, nil
}
// 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
func (m Mock) GetAuthCertificate(string) (*AuthSSLCert, error) {
return nil, nil
}
// GetService searches for services contenating the namespace and name using a the character /
func (m Mock) GetService(string) (*apiv1.Service, error) {
return nil, nil
}
// GetAnnotationWithPrefix returns the prefix of the Ingress annotations
func (m Mock) GetAnnotationWithPrefix(name string) string {
return fmt.Sprintf("nginx/%v", name)
}