Merge pull request #1662 from aledbf/refactor-annotations
Refactor annotations
This commit is contained in:
commit
39c30853ae
119 changed files with 1630 additions and 1201 deletions
|
@ -27,9 +27,9 @@ import (
|
||||||
|
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/controller"
|
"k8s.io/ingress-nginx/internal/ingress/controller"
|
||||||
ngx_config "k8s.io/ingress-nginx/pkg/ingress/controller/config"
|
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||||
ing_net "k8s.io/ingress-nginx/pkg/net"
|
ing_net "k8s.io/ingress-nginx/internal/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -39,10 +39,10 @@ import (
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/controller"
|
"k8s.io/ingress-nginx/internal/ingress/controller"
|
||||||
"k8s.io/ingress-nginx/pkg/k8s"
|
"k8s.io/ingress-nginx/internal/k8s"
|
||||||
"k8s.io/ingress-nginx/pkg/net/ssl"
|
"k8s.io/ingress-nginx/internal/net/ssl"
|
||||||
"k8s.io/ingress-nginx/version"
|
"k8s.io/ingress-nginx/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -19,23 +19,21 @@ package alias
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -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")
|
||||||
}
|
}
|
162
internal/ingress/annotations/annotations.go
Normal file
162
internal/ingress/annotations/annotations.go
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
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 annotations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"github.com/imdario/mergo"
|
||||||
|
|
||||||
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/alias"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/auth"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/authreq"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/authtls"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/clientbodybuffersize"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/cors"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/defaultbackend"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/healthcheck"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/portinredirect"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/proxy"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/redirect"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/rewrite"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/secureupstream"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/serversnippet"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/serviceupstream"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/sessionaffinity"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/snippet"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/sslpassthrough"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/upstreamhashby"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/upstreamvhost"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/vtsfilterkey"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/errors"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeniedKeyName name of the key that contains the reason to deny a location
|
||||||
|
const DeniedKeyName = "Denied"
|
||||||
|
|
||||||
|
// Ingress defines the valid annotations present in one NGINX Ingress rule
|
||||||
|
type Ingress struct {
|
||||||
|
metav1.ObjectMeta
|
||||||
|
Alias string
|
||||||
|
BasicDigestAuth auth.Config
|
||||||
|
CertificateAuth authtls.Config
|
||||||
|
ClientBodyBufferSize string
|
||||||
|
ConfigurationSnippet string
|
||||||
|
CorsConfig cors.Config
|
||||||
|
DefaultBackend string
|
||||||
|
Denied error
|
||||||
|
ExternalAuth authreq.Config
|
||||||
|
HealthCheck healthcheck.Config
|
||||||
|
Proxy proxy.Config
|
||||||
|
RateLimit ratelimit.Config
|
||||||
|
Redirect redirect.Config
|
||||||
|
Rewrite rewrite.Config
|
||||||
|
SecureUpstream secureupstream.Config
|
||||||
|
ServerSnippet string
|
||||||
|
ServiceUpstream bool
|
||||||
|
SessionAffinity sessionaffinity.Config
|
||||||
|
SSLPassthrough bool
|
||||||
|
UsePortInRedirects bool
|
||||||
|
UpstreamHashBy string
|
||||||
|
UpstreamVhost string
|
||||||
|
VtsFilterKey string
|
||||||
|
Whitelist ipwhitelist.SourceRange
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extractor defines the annotation parsers to be used in the extraction of annotations
|
||||||
|
type Extractor struct {
|
||||||
|
annotations map[string]parser.IngressAnnotation
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAnnotationExtractor creates a new annotations extractor
|
||||||
|
func NewAnnotationExtractor(cfg resolver.Resolver) Extractor {
|
||||||
|
return Extractor{
|
||||||
|
map[string]parser.IngressAnnotation{
|
||||||
|
"Alias": alias.NewParser(cfg),
|
||||||
|
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
|
||||||
|
"CertificateAuth": authtls.NewParser(cfg),
|
||||||
|
"ClientBodyBufferSize": clientbodybuffersize.NewParser(cfg),
|
||||||
|
"ConfigurationSnippet": snippet.NewParser(cfg),
|
||||||
|
"CorsConfig": cors.NewParser(cfg),
|
||||||
|
"DefaultBackend": defaultbackend.NewParser(cfg),
|
||||||
|
"ExternalAuth": authreq.NewParser(cfg),
|
||||||
|
"HealthCheck": healthcheck.NewParser(cfg),
|
||||||
|
"Proxy": proxy.NewParser(cfg),
|
||||||
|
"RateLimit": ratelimit.NewParser(cfg),
|
||||||
|
"Redirect": redirect.NewParser(cfg),
|
||||||
|
"Rewrite": rewrite.NewParser(cfg),
|
||||||
|
"SecureUpstream": secureupstream.NewParser(cfg),
|
||||||
|
"ServerSnippet": serversnippet.NewParser(cfg),
|
||||||
|
"ServiceUpstream": serviceupstream.NewParser(cfg),
|
||||||
|
"SessionAffinity": sessionaffinity.NewParser(cfg),
|
||||||
|
"SSLPassthrough": sslpassthrough.NewParser(cfg),
|
||||||
|
"UsePortInRedirects": portinredirect.NewParser(cfg),
|
||||||
|
"UpstreamHashBy": upstreamhashby.NewParser(cfg),
|
||||||
|
"UpstreamVhost": upstreamvhost.NewParser(cfg),
|
||||||
|
"VtsFilterKey": vtsfilterkey.NewParser(cfg),
|
||||||
|
"Whitelist": ipwhitelist.NewParser(cfg),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract extracts the annotations from an Ingress
|
||||||
|
func (e Extractor) Extract(ing *extensions.Ingress) *Ingress {
|
||||||
|
pia := &Ingress{
|
||||||
|
ObjectMeta: ing.ObjectMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make(map[string]interface{})
|
||||||
|
for name, annotationParser := range e.annotations {
|
||||||
|
val, err := annotationParser.Parse(ing)
|
||||||
|
glog.V(5).Infof("annotation %v in Ingress %v/%v: %v", name, ing.GetNamespace(), ing.GetName(), val)
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsMissingAnnotations(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.IsLocationDenied(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, alreadyDenied := data[DeniedKeyName]
|
||||||
|
if !alreadyDenied {
|
||||||
|
data[DeniedKeyName] = err
|
||||||
|
glog.Errorf("error reading %v annotation in Ingress %v/%v: %v", name, ing.GetNamespace(), ing.GetName(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(5).Infof("error reading %v annotation in Ingress %v/%v: %v", name, ing.GetNamespace(), ing.GetName(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val != nil {
|
||||||
|
data[name] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mergo.Map(pia, data)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unexpected error merging extracted annotations: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pia
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package controller
|
package annotations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -24,30 +24,31 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/defaults"
|
"k8s.io/ingress-nginx/internal/ingress/defaults"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -75,20 +76,6 @@ func (m mockCfg) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAnnotationExtractor(t *testing.T) {
|
|
||||||
ec := newAnnotationExtractor(mockCfg{})
|
|
||||||
ing := buildIngress()
|
|
||||||
|
|
||||||
m := ec.Extract(ing)
|
|
||||||
// the map at least should contains HealthCheck and Proxy information (defaults)
|
|
||||||
if _, ok := m["HealthCheck"]; !ok {
|
|
||||||
t.Error("expected HealthCheck annotation")
|
|
||||||
}
|
|
||||||
if _, ok := m["Proxy"]; !ok {
|
|
||||||
t.Error("expected Proxy annotation")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildIngress() *extensions.Ingress {
|
func buildIngress() *extensions.Ingress {
|
||||||
defaultBackend := extensions.IngressBackend{
|
defaultBackend := extensions.IngressBackend{
|
||||||
ServiceName: "default-backend",
|
ServiceName: "default-backend",
|
||||||
|
@ -125,7 +112,7 @@ func buildIngress() *extensions.Ingress {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSecureUpstream(t *testing.T) {
|
func TestSecureUpstream(t *testing.T) {
|
||||||
ec := newAnnotationExtractor(mockCfg{})
|
ec := NewAnnotationExtractor(mockCfg{})
|
||||||
ing := buildIngress()
|
ing := buildIngress()
|
||||||
|
|
||||||
fooAnns := []struct {
|
fooAnns := []struct {
|
||||||
|
@ -141,7 +128,7 @@ func TestSecureUpstream(t *testing.T) {
|
||||||
|
|
||||||
for _, foo := range fooAnns {
|
for _, foo := range fooAnns {
|
||||||
ing.SetAnnotations(foo.annotations)
|
ing.SetAnnotations(foo.annotations)
|
||||||
r := ec.SecureUpstream(ing)
|
r := ec.Extract(ing).SecureUpstream
|
||||||
if r.Secure != foo.er {
|
if r.Secure != foo.er {
|
||||||
t.Errorf("Returned %v but expected %v", r, foo.er)
|
t.Errorf("Returned %v but expected %v", r, foo.er)
|
||||||
}
|
}
|
||||||
|
@ -149,7 +136,7 @@ func TestSecureUpstream(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSecureVerifyCACert(t *testing.T) {
|
func TestSecureVerifyCACert(t *testing.T) {
|
||||||
ec := newAnnotationExtractor(mockCfg{
|
ec := NewAnnotationExtractor(mockCfg{
|
||||||
MockSecrets: map[string]*apiv1.Secret{
|
MockSecrets: map[string]*apiv1.Secret{
|
||||||
"default/secure-verify-ca": {
|
"default/secure-verify-ca": {
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
@ -176,15 +163,15 @@ func TestSecureVerifyCACert(t *testing.T) {
|
||||||
for _, ann := range anns {
|
for _, ann := range anns {
|
||||||
ing := buildIngress()
|
ing := buildIngress()
|
||||||
ing.SetAnnotations(ann.annotations)
|
ing.SetAnnotations(ann.annotations)
|
||||||
res := ec.SecureUpstream(ing)
|
su := ec.Extract(ing).SecureUpstream
|
||||||
if (res.CACert.CAFileName != "") != ann.exists {
|
if (su.CACert.CAFileName != "") != ann.exists {
|
||||||
t.Errorf("Expected exists was %v on iteration %v", ann.exists, ann.it)
|
t.Errorf("Expected exists was %v on iteration %v", ann.exists, ann.it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHealthCheck(t *testing.T) {
|
func TestHealthCheck(t *testing.T) {
|
||||||
ec := newAnnotationExtractor(mockCfg{})
|
ec := NewAnnotationExtractor(mockCfg{})
|
||||||
ing := buildIngress()
|
ing := buildIngress()
|
||||||
|
|
||||||
fooAnns := []struct {
|
fooAnns := []struct {
|
||||||
|
@ -201,11 +188,7 @@ func TestHealthCheck(t *testing.T) {
|
||||||
|
|
||||||
for _, foo := range fooAnns {
|
for _, foo := range fooAnns {
|
||||||
ing.SetAnnotations(foo.annotations)
|
ing.SetAnnotations(foo.annotations)
|
||||||
r := ec.HealthCheck(ing)
|
r := ec.Extract(ing).HealthCheck
|
||||||
if r == nil {
|
|
||||||
t.Errorf("Returned nil but expected a healthcheck.Upstream")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.FailTimeout != foo.euft {
|
if r.FailTimeout != foo.euft {
|
||||||
t.Errorf("Returned %d but expected %d for FailTimeout", r.FailTimeout, foo.euft)
|
t.Errorf("Returned %d but expected %d for FailTimeout", r.FailTimeout, foo.euft)
|
||||||
|
@ -218,7 +201,7 @@ func TestHealthCheck(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSSLPassthrough(t *testing.T) {
|
func TestSSLPassthrough(t *testing.T) {
|
||||||
ec := newAnnotationExtractor(mockCfg{})
|
ec := NewAnnotationExtractor(mockCfg{})
|
||||||
ing := buildIngress()
|
ing := buildIngress()
|
||||||
|
|
||||||
fooAnns := []struct {
|
fooAnns := []struct {
|
||||||
|
@ -234,7 +217,7 @@ func TestSSLPassthrough(t *testing.T) {
|
||||||
|
|
||||||
for _, foo := range fooAnns {
|
for _, foo := range fooAnns {
|
||||||
ing.SetAnnotations(foo.annotations)
|
ing.SetAnnotations(foo.annotations)
|
||||||
r := ec.SSLPassthrough(ing)
|
r := ec.Extract(ing).SSLPassthrough
|
||||||
if r != foo.er {
|
if r != foo.er {
|
||||||
t.Errorf("Returned %v but expected %v", r, foo.er)
|
t.Errorf("Returned %v but expected %v", r, foo.er)
|
||||||
}
|
}
|
||||||
|
@ -242,7 +225,7 @@ func TestSSLPassthrough(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpstreamHashBy(t *testing.T) {
|
func TestUpstreamHashBy(t *testing.T) {
|
||||||
ec := newAnnotationExtractor(mockCfg{})
|
ec := NewAnnotationExtractor(mockCfg{})
|
||||||
ing := buildIngress()
|
ing := buildIngress()
|
||||||
|
|
||||||
fooAnns := []struct {
|
fooAnns := []struct {
|
||||||
|
@ -258,7 +241,7 @@ func TestUpstreamHashBy(t *testing.T) {
|
||||||
|
|
||||||
for _, foo := range fooAnns {
|
for _, foo := range fooAnns {
|
||||||
ing.SetAnnotations(foo.annotations)
|
ing.SetAnnotations(foo.annotations)
|
||||||
r := ec.UpstreamHashBy(ing)
|
r := ec.Extract(ing).UpstreamHashBy
|
||||||
if r != foo.er {
|
if r != foo.er {
|
||||||
t.Errorf("Returned %v but expected %v", r, foo.er)
|
t.Errorf("Returned %v but expected %v", r, foo.er)
|
||||||
}
|
}
|
||||||
|
@ -266,7 +249,7 @@ func TestUpstreamHashBy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAffinitySession(t *testing.T) {
|
func TestAffinitySession(t *testing.T) {
|
||||||
ec := newAnnotationExtractor(mockCfg{})
|
ec := NewAnnotationExtractor(mockCfg{})
|
||||||
ing := buildIngress()
|
ing := buildIngress()
|
||||||
|
|
||||||
fooAnns := []struct {
|
fooAnns := []struct {
|
||||||
|
@ -284,25 +267,21 @@ func TestAffinitySession(t *testing.T) {
|
||||||
|
|
||||||
for _, foo := range fooAnns {
|
for _, foo := range fooAnns {
|
||||||
ing.SetAnnotations(foo.annotations)
|
ing.SetAnnotations(foo.annotations)
|
||||||
r := ec.SessionAffinity(ing)
|
r := ec.Extract(ing).SessionAffinity
|
||||||
t.Logf("Testing pass %v %v %v", foo.affinitytype, foo.hash, foo.name)
|
t.Logf("Testing pass %v %v %v", foo.affinitytype, foo.hash, foo.name)
|
||||||
if r == nil {
|
|
||||||
t.Errorf("Returned nil but expected a SessionAffinity.AffinityConfig")
|
if r.Cookie.Hash != foo.hash {
|
||||||
continue
|
t.Errorf("Returned %v but expected %v for Hash", r.Cookie.Hash, foo.hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.CookieConfig.Hash != foo.hash {
|
if r.Cookie.Name != foo.name {
|
||||||
t.Errorf("Returned %v but expected %v for Hash", r.CookieConfig.Hash, foo.hash)
|
t.Errorf("Returned %v but expected %v for Name", r.Cookie.Name, foo.name)
|
||||||
}
|
|
||||||
|
|
||||||
if r.CookieConfig.Name != foo.name {
|
|
||||||
t.Errorf("Returned %v but expected %v for Name", r.CookieConfig.Name, foo.name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCors(t *testing.T) {
|
func TestCors(t *testing.T) {
|
||||||
ec := newAnnotationExtractor(mockCfg{})
|
ec := NewAnnotationExtractor(mockCfg{})
|
||||||
ing := buildIngress()
|
ing := buildIngress()
|
||||||
|
|
||||||
fooAnns := []struct {
|
fooAnns := []struct {
|
||||||
|
@ -322,12 +301,8 @@ func TestCors(t *testing.T) {
|
||||||
|
|
||||||
for _, foo := range fooAnns {
|
for _, foo := range fooAnns {
|
||||||
ing.SetAnnotations(foo.annotations)
|
ing.SetAnnotations(foo.annotations)
|
||||||
r := ec.Cors(ing)
|
r := ec.Extract(ing).CorsConfig
|
||||||
t.Logf("Testing pass %v %v %v %v %v", foo.corsenabled, foo.methods, foo.headers, foo.origin, foo.credentials)
|
t.Logf("Testing pass %v %v %v %v %v", foo.corsenabled, foo.methods, foo.headers, foo.origin, foo.credentials)
|
||||||
if r == nil {
|
|
||||||
t.Errorf("Returned nil but expected a Cors.CorsConfig")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.CorsEnabled != foo.corsenabled {
|
if r.CorsEnabled != foo.corsenabled {
|
||||||
t.Errorf("Returned %v but expected %v for Cors Enabled", r.CorsEnabled, foo.corsenabled)
|
t.Errorf("Returned %v but expected %v for Cors Enabled", r.CorsEnabled, foo.corsenabled)
|
||||||
|
@ -351,3 +326,48 @@ func TestCors(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestMergeLocationAnnotations(t *testing.T) {
|
||||||
|
// initial parameters
|
||||||
|
keys := []string{"BasicDigestAuth", "CorsConfig", "ExternalAuth", "RateLimit", "Redirect", "Rewrite", "Whitelist", "Proxy", "UsePortInRedirects"}
|
||||||
|
|
||||||
|
loc := ingress.Location{}
|
||||||
|
annotations := &Ingress{
|
||||||
|
BasicDigestAuth: &auth.Config{},
|
||||||
|
CorsConfig: &cors.Config{},
|
||||||
|
ExternalAuth: &authreq.Config{},
|
||||||
|
RateLimit: &ratelimit.Config{},
|
||||||
|
Redirect: &redirect.Config{},
|
||||||
|
Rewrite: &rewrite.Config{},
|
||||||
|
Whitelist: &ipwhitelist.SourceRange{},
|
||||||
|
Proxy: &proxy.Config{},
|
||||||
|
UsePortInRedirects: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// create test table
|
||||||
|
type fooMergeLocationAnnotationsStruct struct {
|
||||||
|
fName string
|
||||||
|
er interface{}
|
||||||
|
}
|
||||||
|
fooTests := []fooMergeLocationAnnotationsStruct{}
|
||||||
|
for name, value := range keys {
|
||||||
|
fva := fooMergeLocationAnnotationsStruct{name, value}
|
||||||
|
fooTests = append(fooTests, fva)
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute test
|
||||||
|
MergeWithLocation(&loc, annotations)
|
||||||
|
|
||||||
|
// check result
|
||||||
|
for _, foo := range fooTests {
|
||||||
|
fv := reflect.ValueOf(loc).FieldByName(foo.fName).Interface()
|
||||||
|
if !reflect.DeepEqual(fv, foo.er) {
|
||||||
|
t.Errorf("Returned %v but expected %v for the field %s", fv, foo.er, foo.fName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := annotations[DeniedKeyName]; ok {
|
||||||
|
t.Errorf("%s should be removed after mergeLocationAnnotations", DeniedKeyName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
|
@ -27,16 +27,10 @@ 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"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/file"
|
"k8s.io/ingress-nginx/internal/file"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
ing_errors "k8s.io/ingress-nginx/pkg/ingress/errors"
|
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
|
||||||
"k8s.io/ingress-nginx/pkg/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 (
|
||||||
|
@ -46,8 +40,8 @@ var (
|
||||||
AuthDirectory = "/etc/ingress-controller/auth"
|
AuthDirectory = "/etc/ingress-controller/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BasicDigest returns authentication configuration for an Ingress rule
|
// Config returns authentication configuration for an Ingress rule
|
||||||
type BasicDigest struct {
|
type Config struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Realm string `json:"realm"`
|
Realm string `json:"realm"`
|
||||||
File string `json:"file"`
|
File string `json:"file"`
|
||||||
|
@ -55,8 +49,8 @@ type BasicDigest struct {
|
||||||
FileSHA string `json:"fileSha"`
|
FileSHA string `json:"fileSha"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests for equality between two BasicDigest types
|
// Equal tests for equality between two Config types
|
||||||
func (bd1 *BasicDigest) Equal(bd2 *BasicDigest) bool {
|
func (bd1 *Config) Equal(bd2 *Config) bool {
|
||||||
if bd1 == bd2 {
|
if bd1 == bd2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -83,12 +77,12 @@ func (bd1 *BasicDigest) Equal(bd2 *BasicDigest) 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)
|
||||||
|
@ -140,7 +134,7 @@ func (a auth) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &BasicDigest{
|
return &Config{
|
||||||
Type: at,
|
Type: at,
|
||||||
Realm: realm,
|
Realm: realm,
|
||||||
File: passFile,
|
File: passFile,
|
|
@ -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,19 +99,19 @@ 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)
|
||||||
}
|
}
|
||||||
auth, ok := i.(*BasicDigest)
|
auth, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a BasicDigest type")
|
t.Errorf("expected a BasicDigest type")
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
@ -23,20 +23,13 @@ import (
|
||||||
|
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
ing_errors "k8s.io/ingress-nginx/pkg/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
|
type Config struct {
|
||||||
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 External 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
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
|
@ -45,8 +38,8 @@ type External struct {
|
||||||
ResponseHeaders []string `json:"responseHeaders,omitEmpty"`
|
ResponseHeaders []string `json:"responseHeaders,omitEmpty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests for equality between two External types
|
// Equal tests for equality between two Config types
|
||||||
func (e1 *External) Equal(e2 *External) bool {
|
func (e1 *Config) Equal(e2 *Config) bool {
|
||||||
if e1 == e2 {
|
if e1 == e2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -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 external URL as source for authentication
|
// rule used to use an Config URL as source for authentication
|
||||||
func (a authReq) Parse(ing *extensions.Ingress) (interface{}, error) {
|
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, ",")
|
||||||
|
@ -165,7 +159,7 @@ func (a authReq) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &External{
|
return &Config{
|
||||||
URL: str,
|
URL: str,
|
||||||
Host: ur.Hostname(),
|
Host: ur.Hostname(),
|
||||||
SigninURL: signin,
|
SigninURL: signin,
|
|
@ -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,18 +87,18 @@ 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)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
u, ok := i.(*External)
|
u, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("%v: expected an External type", test.title)
|
t.Errorf("%v: expected an External type", 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())
|
||||||
|
@ -149,7 +150,7 @@ func TestHeaderAnnotations(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log(i)
|
t.Log(i)
|
||||||
u, ok := i.(*External)
|
u, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("%v: expected an External type", test.title)
|
t.Errorf("%v: expected an External type", test.title)
|
||||||
continue
|
continue
|
|
@ -20,38 +20,34 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
|
||||||
ing_errors "k8s.io/ingress-nginx/pkg/ingress/errors"
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
|
||||||
"k8s.io/ingress-nginx/pkg/k8s"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
|
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
|
"k8s.io/ingress-nginx/internal/k8s"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// name of the secret
|
defaultAuthTLSDepth = 1
|
||||||
annotationAuthTLSSecret = "ingress.kubernetes.io/auth-tls-secret"
|
defaultAuthVerifyClient = "on"
|
||||||
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
|
|
||||||
defaultAuthVerifyClient = "on"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
authVerifyClientRegex = regexp.MustCompile(`on|off|optional|optional_no_ca`)
|
authVerifyClientRegex = regexp.MustCompile(`on|off|optional|optional_no_ca`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthSSLConfig contains the AuthSSLCert used for muthual autentication
|
// Config contains the AuthSSLCert used for muthual autentication
|
||||||
// and the configured ValidationDepth
|
// and the configured ValidationDepth
|
||||||
type AuthSSLConfig struct {
|
type Config struct {
|
||||||
resolver.AuthSSLCert
|
resolver.AuthSSLCert
|
||||||
VerifyClient string `json:"verify_client"`
|
VerifyClient string `json:"verify_client"`
|
||||||
ValidationDepth int `json:"validationDepth"`
|
ValidationDepth int `json:"validationDepth"`
|
||||||
ErrorPage string `json:"errorPage"`
|
ErrorPage string `json:"errorPage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests for equality between two AuthSSLConfig types
|
// Equal tests for equality between two Config types
|
||||||
func (assl1 *AuthSSLConfig) Equal(assl2 *AuthSSLConfig) bool {
|
func (assl1 *Config) Equal(assl2 *Config) bool {
|
||||||
if assl1 == assl2 {
|
if assl1 == assl2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -74,55 +70,55 @@ func (assl1 *AuthSSLConfig) Equal(assl2 *AuthSSLConfig) 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 &AuthSSLConfig{}, err
|
return &Config{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if tlsauthsecret == "" {
|
if tlsauthsecret == "" {
|
||||||
return &AuthSSLConfig{}, ing_errors.NewLocationDenied("an empty string is not a valid secret name")
|
return &Config{}, ing_errors.NewLocationDenied("an empty string is not a valid secret name")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err = k8s.ParseNameNS(tlsauthsecret)
|
_, _, err = k8s.ParseNameNS(tlsauthsecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &AuthSSLConfig{}, 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 &AuthSSLConfig{}, 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 = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AuthSSLConfig{
|
return &Config{
|
||||||
AuthSSLCert: *authCert,
|
AuthSSLCert: *authCert,
|
||||||
VerifyClient: tlsVerifyClient,
|
VerifyClient: tlsVerifyClient,
|
||||||
ValidationDepth: tlsdepth,
|
ValidationDepth: tlsdepth,
|
|
@ -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/pkg/ingress/annotations/parser"
|
|
||||||
"k8s.io/ingress-nginx/pkg/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
|
|
@ -19,23 +19,21 @@ package clientbodybuffersize
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -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")
|
||||||
}
|
}
|
|
@ -21,15 +21,11 @@ import (
|
||||||
|
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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,10 +45,11 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type cors struct {
|
type cors struct {
|
||||||
|
r resolver.Resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// CorsConfig contains the Cors configuration to be used in the Ingress
|
// Config contains the Cors configuration to be used in the Ingress
|
||||||
type CorsConfig struct {
|
type Config struct {
|
||||||
CorsEnabled bool `json:"corsEnabled"`
|
CorsEnabled bool `json:"corsEnabled"`
|
||||||
CorsAllowOrigin string `json:"corsAllowOrigin"`
|
CorsAllowOrigin string `json:"corsAllowOrigin"`
|
||||||
CorsAllowMethods string `json:"corsAllowMethods"`
|
CorsAllowMethods string `json:"corsAllowMethods"`
|
||||||
|
@ -61,12 +58,12 @@ type CorsConfig 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
|
||||||
func (c1 *CorsConfig) Equal(c2 *CorsConfig) bool {
|
func (c1 *Config) Equal(c2 *Config) bool {
|
||||||
if c1 == c2 {
|
if c1 == c2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -94,33 +91,33 @@ func (c1 *CorsConfig) Equal(c2 *CorsConfig) 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
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CorsConfig{
|
return &Config{
|
||||||
CorsEnabled: corsenabled,
|
CorsEnabled: corsenabled,
|
||||||
CorsAllowOrigin: corsalloworigin,
|
CorsAllowOrigin: corsalloworigin,
|
||||||
CorsAllowHeaders: corsallowheaders,
|
CorsAllowHeaders: corsallowheaders,
|
|
@ -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,15 +65,15 @@ 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.(*CorsConfig)
|
nginxCors, ok := corst.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a Config type")
|
t.Errorf("expected a Config type")
|
||||||
}
|
}
|
|
@ -22,33 +22,29 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -19,48 +19,43 @@ package healthcheck
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Config returns the URL and method to use check the status of
|
||||||
upsMaxFails = "ingress.kubernetes.io/upstream-max-fails"
|
|
||||||
upsFailTimeout = "ingress.kubernetes.io/upstream-fail-timeout"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Upstream returns the URL and method to use check the status of
|
|
||||||
// the upstream server/s
|
// the upstream server/s
|
||||||
type Upstream struct {
|
type Config struct {
|
||||||
MaxFails int `json:"maxFails"`
|
MaxFails int `json:"maxFails"`
|
||||||
FailTimeout int `json:"failTimeout"`
|
FailTimeout int `json:"failTimeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
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 &Upstream{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
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Upstream{mf, ft}, nil
|
return &Config{mf, ft}, nil
|
||||||
}
|
}
|
|
@ -24,7 +24,8 @@ import (
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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,11 +75,11 @@ 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)
|
||||||
nginxHz, ok := hzi.(*Upstream)
|
nginxHz, ok := hzi.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a Upstream type")
|
t.Errorf("expected a Upstream type")
|
||||||
}
|
}
|
|
@ -23,15 +23,11 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
"k8s.io/ingress-nginx/pkg/net"
|
"k8s.io/ingress-nginx/internal/net"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
ing_errors "k8s.io/ingress-nginx/pkg/ingress/errors"
|
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
|
||||||
"k8s.io/ingress-nginx/pkg/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
|
||||||
|
@ -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
|
|
@ -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/pkg/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)
|
|
@ -21,7 +21,8 @@ import (
|
||||||
|
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -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)
|
|
@ -19,29 +19,25 @@ package portinredirect
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/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
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package portinredirect
|
package portinredirect
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
api "k8s.io/api/core/v1"
|
api "k8s.io/api/core/v1"
|
||||||
|
@ -24,9 +25,8 @@ import (
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
|
||||||
"fmt"
|
"k8s.io/ingress-nginx/internal/ingress/defaults"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/defaults"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func buildIngress() *extensions.Ingress {
|
func buildIngress() *extensions.Ingress {
|
||||||
|
@ -65,6 +65,7 @@ func buildIngress() *extensions.Ingress {
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockBackend struct {
|
type mockBackend struct {
|
||||||
|
resolver.Mock
|
||||||
usePortInRedirects bool
|
usePortInRedirects bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,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")
|
||||||
}
|
}
|
|
@ -19,25 +19,12 @@ package proxy
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Config returns the proxy timeout to use in the upstream server/s
|
||||||
bodySize = "ingress.kubernetes.io/proxy-body-size"
|
type Config struct {
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Configuration returns the proxy timeout to use in the upstream server/s
|
|
||||||
type Configuration struct {
|
|
||||||
BodySize string `json:"bodySize"`
|
BodySize string `json:"bodySize"`
|
||||||
ConnectTimeout int `json:"connectTimeout"`
|
ConnectTimeout int `json:"connectTimeout"`
|
||||||
SendTimeout int `json:"sendTimeout"`
|
SendTimeout int `json:"sendTimeout"`
|
||||||
|
@ -51,7 +38,7 @@ type Configuration struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests for equality between two Configuration types
|
// Equal tests for equality between two Configuration types
|
||||||
func (l1 *Configuration) Equal(l2 *Configuration) bool {
|
func (l1 *Config) Equal(l2 *Config) bool {
|
||||||
if l1 == l2 {
|
if l1 == l2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -94,67 +81,67 @@ func (l1 *Configuration) Equal(l2 *Configuration) 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
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Configuration{bs, ct, st, rt, bufs, cd, cp, nu, pp, rb}, nil
|
return &Config{bs, ct, st, rt, bufs, cd, cp, nu, pp, rb}, nil
|
||||||
}
|
}
|
|
@ -24,7 +24,8 @@ import (
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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,23 +85,23 @@ 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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error parsing a valid")
|
t.Fatalf("unexpected error parsing a valid")
|
||||||
}
|
}
|
||||||
p, ok := i.(*Configuration)
|
p, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("expected a Configuration type")
|
t.Fatalf("expected a Config type")
|
||||||
}
|
}
|
||||||
if p.ConnectTimeout != 1 {
|
if p.ConnectTimeout != 1 {
|
||||||
t.Errorf("expected 1 as connect-timeout but returned %v", p.ConnectTimeout)
|
t.Errorf("expected 1 as connect-timeout but returned %v", p.ConnectTimeout)
|
||||||
|
@ -137,9 +139,9 @@ func TestProxyWithNoAnnotation(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error parsing a valid")
|
t.Fatalf("unexpected error parsing a valid")
|
||||||
}
|
}
|
||||||
p, ok := i.(*Configuration)
|
p, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("expected a Configuration type")
|
t.Fatalf("expected a Config type")
|
||||||
}
|
}
|
||||||
if p.ConnectTimeout != 10 {
|
if p.ConnectTimeout != 10 {
|
||||||
t.Errorf("expected 10 as connect-timeout but returned %v", p.ConnectTimeout)
|
t.Errorf("expected 10 as connect-timeout but returned %v", p.ConnectTimeout)
|
|
@ -24,19 +24,12 @@ import (
|
||||||
|
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
"k8s.io/ingress-nginx/pkg/net"
|
"k8s.io/ingress-nginx/internal/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -45,11 +38,11 @@ const (
|
||||||
defSharedSize = 5
|
defSharedSize = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// RateLimit returns rate limit configuration for an Ingress rule limiting the
|
// Config returns rate limit configuration for an Ingress rule limiting the
|
||||||
// number of connections per IP address and/or connections per second.
|
// number of connections per IP address and/or connections per second.
|
||||||
// If you both annotations are specified in a single Ingress rule, RPS limits
|
// If you both annotations are specified in a single Ingress rule, RPS limits
|
||||||
// takes precedence
|
// takes precedence
|
||||||
type RateLimit struct {
|
type Config struct {
|
||||||
// Connections indicates a limit with the number of connections per IP address
|
// Connections indicates a limit with the number of connections per IP address
|
||||||
Connections Zone `json:"connections"`
|
Connections Zone `json:"connections"`
|
||||||
// RPS indicates a limit with the number of connections per second
|
// RPS indicates a limit with the number of connections per second
|
||||||
|
@ -69,7 +62,7 @@ type RateLimit struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests for equality between two RateLimit types
|
// Equal tests for equality between two RateLimit types
|
||||||
func (rt1 *RateLimit) Equal(rt2 *RateLimit) bool {
|
func (rt1 *Config) Equal(rt2 *Config) bool {
|
||||||
if rt1 == rt2 {
|
if rt1 == rt2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -185,7 +178,7 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if rpm == 0 && rps == 0 && conn == 0 {
|
if rpm == 0 && rps == 0 && conn == 0 {
|
||||||
return &RateLimit{
|
return &Config{
|
||||||
Connections: Zone{},
|
Connections: Zone{},
|
||||||
RPS: Zone{},
|
RPS: Zone{},
|
||||||
RPM: Zone{},
|
RPM: Zone{},
|
||||||
|
@ -196,7 +189,7 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
|
|
||||||
zoneName := fmt.Sprintf("%v_%v", ing.GetNamespace(), ing.GetName())
|
zoneName := fmt.Sprintf("%v_%v", ing.GetNamespace(), ing.GetName())
|
||||||
|
|
||||||
return &RateLimit{
|
return &Config{
|
||||||
Connections: Zone{
|
Connections: Zone{
|
||||||
Name: fmt.Sprintf("%v_conn", zoneName),
|
Name: fmt.Sprintf("%v_conn", zoneName),
|
||||||
Limit: conn,
|
Limit: conn,
|
|
@ -24,7 +24,8 @@ import (
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
|
|
||||||
|
@ -107,7 +109,7 @@ func TestBadRateLimiting(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
rateLimit, ok := i.(*RateLimit)
|
rateLimit, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a RateLimit type")
|
t.Errorf("expected a RateLimit type")
|
||||||
}
|
}
|
|
@ -23,28 +23,25 @@ import (
|
||||||
|
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/errors"
|
"k8s.io/ingress-nginx/internal/ingress/errors"
|
||||||
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Config returns the redirect configuration for an Ingress rule
|
||||||
permanent = "ingress.kubernetes.io/permanent-redirect"
|
type Config struct {
|
||||||
temporal = "ingress.kubernetes.io/temporal-redirect"
|
|
||||||
www = "ingress.kubernetes.io/from-to-www-redirect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Redirect returns the redirect configuration for an Ingress rule
|
|
||||||
type Redirect struct {
|
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -64,14 +61,14 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Redirect{
|
return &Config{
|
||||||
URL: tr,
|
URL: tr,
|
||||||
Code: http.StatusFound,
|
Code: http.StatusFound,
|
||||||
FromToWWW: r3w,
|
FromToWWW: r3w,
|
||||||
}, 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
|
||||||
}
|
}
|
||||||
|
@ -81,7 +78,7 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Redirect{
|
return &Config{
|
||||||
URL: pr,
|
URL: pr,
|
||||||
Code: http.StatusMovedPermanently,
|
Code: http.StatusMovedPermanently,
|
||||||
FromToWWW: r3w,
|
FromToWWW: r3w,
|
||||||
|
@ -89,7 +86,7 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if r3w {
|
if r3w {
|
||||||
return &Redirect{
|
return &Config{
|
||||||
FromToWWW: r3w,
|
FromToWWW: r3w,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -98,7 +95,7 @@ func (a redirect) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests for equality between two Redirect types
|
// Equal tests for equality between two Redirect types
|
||||||
func (r1 *Redirect) Equal(r2 *Redirect) bool {
|
func (r1 *Config) Equal(r2 *Config) bool {
|
||||||
if r1 == r2 {
|
if r1 == r2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
|
@ -19,21 +19,12 @@ package rewrite
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Config describes the per location redirect config
|
||||||
rewriteTo = "ingress.kubernetes.io/rewrite-target"
|
type Config struct {
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Redirect describes the per location redirect config
|
|
||||||
type Redirect struct {
|
|
||||||
// Target URI where the traffic must be redirected
|
// Target URI where the traffic must be redirected
|
||||||
Target string `json:"target"`
|
Target string `json:"target"`
|
||||||
// AddBaseURL indicates if is required to add a base tag in the head
|
// AddBaseURL indicates if is required to add a base tag in the head
|
||||||
|
@ -50,7 +41,7 @@ type Redirect struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests for equality between two Redirect types
|
// Equal tests for equality between two Redirect types
|
||||||
func (r1 *Redirect) Equal(r2 *Redirect) bool {
|
func (r1 *Config) Equal(r2 *Config) bool {
|
||||||
if r1 == r2 {
|
if r1 == r2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -80,30 +71,31 @@ func (r1 *Redirect) Equal(r2 *Redirect) 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 &Redirect{
|
|
||||||
|
return &Config{
|
||||||
Target: rt,
|
Target: rt,
|
||||||
AddBaseURL: abu,
|
AddBaseURL: abu,
|
||||||
BaseURLScheme: bus,
|
BaseURLScheme: bus,
|
|
@ -24,7 +24,8 @@ import (
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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,14 +88,14 @@ 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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error with ingress: %v", err)
|
t.Errorf("Unexpected error with ingress: %v", err)
|
||||||
}
|
}
|
||||||
redirect, ok := i.(*Redirect)
|
redirect, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a Redirect type")
|
t.Errorf("expected a Redirect type")
|
||||||
}
|
}
|
||||||
|
@ -106,11 +108,11 @@ 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.(*Redirect)
|
redirect, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a Redirect type")
|
t.Errorf("expected a Redirect type")
|
||||||
}
|
}
|
||||||
|
@ -118,11 +120,11 @@ 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.(*Redirect)
|
redirect, ok = i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a Redirect type")
|
t.Errorf("expected a Redirect type")
|
||||||
}
|
}
|
||||||
|
@ -135,11 +137,11 @@ 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.(*Redirect)
|
redirect, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a Redirect type")
|
t.Errorf("expected a Redirect type")
|
||||||
}
|
}
|
||||||
|
@ -147,11 +149,11 @@ 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.(*Redirect)
|
redirect, ok = i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a Redirect type")
|
t.Errorf("expected a Redirect type")
|
||||||
}
|
}
|
||||||
|
@ -163,16 +165,15 @@ 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.(*Redirect)
|
redirect, ok := i.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a App Context")
|
t.Errorf("expected a App Context")
|
||||||
}
|
}
|
||||||
if redirect.AppRoot != "/app1" {
|
if redirect.AppRoot != "/app1" {
|
||||||
t.Errorf("Unexpected value got in AppRoot")
|
t.Errorf("Unexpected value got in AppRoot")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -22,38 +22,31 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Config describes SSL backend configuration
|
||||||
secureUpstream = "ingress.kubernetes.io/secure-backends"
|
type Config struct {
|
||||||
secureVerifyCASecret = "ingress.kubernetes.io/secure-verify-ca-secret"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Secure describes SSL backend configuration
|
|
||||||
type Secure struct {
|
|
||||||
Secure bool `json:"secure"`
|
Secure bool `json:"secure"`
|
||||||
CACert resolver.AuthSSLCert `json:"caCert"`
|
CACert resolver.AuthSSLCert `json:"caCert"`
|
||||||
}
|
}
|
||||||
|
|
||||||
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 := &Secure{
|
secure := &Config{
|
||||||
Secure: s,
|
Secure: s,
|
||||||
CACert: resolver.AuthSSLCert{},
|
CACert: resolver.AuthSSLCert{},
|
||||||
}
|
}
|
||||||
|
@ -64,14 +57,14 @@ 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")
|
||||||
}
|
}
|
||||||
if caCert == nil {
|
if caCert == nil {
|
||||||
return secure, nil
|
return secure, nil
|
||||||
}
|
}
|
||||||
return &Secure{
|
return &Config{
|
||||||
Secure: s,
|
Secure: s,
|
||||||
CACert: *caCert,
|
CACert: *caCert,
|
||||||
}, nil
|
}, nil
|
|
@ -17,16 +17,15 @@ limitations under the License.
|
||||||
package secureupstream
|
package secureupstream
|
||||||
|
|
||||||
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"
|
||||||
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func buildIngress() *extensions.Ingress {
|
func buildIngress() *extensions.Ingress {
|
||||||
|
@ -65,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,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{
|
||||||
|
@ -95,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 {
|
||||||
|
@ -107,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{
|
|
@ -19,24 +19,22 @@ package serversnippet
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -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")
|
||||||
}
|
}
|
|
@ -18,21 +18,20 @@ package serviceupstream
|
||||||
|
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -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")
|
|
@ -23,18 +23,21 @@ import (
|
||||||
|
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,74 +45,73 @@ var (
|
||||||
affinityCookieHashRegex = regexp.MustCompile(`^(index|md5|sha1)$`)
|
affinityCookieHashRegex = regexp.MustCompile(`^(index|md5|sha1)$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// AffinityConfig describes the per ingress session affinity config
|
// Config describes the per ingress session affinity config
|
||||||
type AffinityConfig struct {
|
type Config struct {
|
||||||
// The type of affinity that will be used
|
// The type of affinity that will be used
|
||||||
AffinityType string `json:"type"`
|
Type string `json:"type"`
|
||||||
CookieConfig
|
Cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
// CookieConfig describes the Config of cookie type affinity
|
// Cookie describes the Config of cookie type affinity
|
||||||
type CookieConfig struct {
|
type Cookie struct {
|
||||||
// The name of the cookie that will be used in case of cookie affinity type.
|
// The name of the cookie that will be used in case of cookie affinity type.
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
// The hash that will be used to encode the cookie in case of cookie affinity type
|
// The hash that will be used to encode the cookie in case of cookie affinity type
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) *CookieConfig {
|
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)
|
||||||
sh = defaultAffinityCookieHash
|
sh = defaultAffinityCookieHash
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CookieConfig{
|
return &Cookie{
|
||||||
Name: sn,
|
Name: sn,
|
||||||
Hash: sh,
|
Hash: sh,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
// rule used to configure the affinity directives
|
// rule used to configure the affinity directives
|
||||||
func (a affinity) Parse(ing *extensions.Ingress) (interface{}, error) {
|
func (a affinity) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
cookieAffinityConfig := &CookieConfig{}
|
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":
|
||||||
cookieAffinityConfig = 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)
|
||||||
|
|
||||||
}
|
}
|
||||||
return &AffinityConfig{
|
|
||||||
AffinityType: at,
|
|
||||||
CookieConfig: *cookieAffinityConfig,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
|
return &Config{
|
||||||
|
Type: at,
|
||||||
|
Cookie: *cookie,
|
||||||
|
}, nil
|
||||||
}
|
}
|
|
@ -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,26 +66,26 @@ 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.(*AffinityConfig)
|
nginxAffinity, ok := affin.(*Config)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expected a Config type")
|
t.Errorf("expected a Config type")
|
||||||
}
|
}
|
||||||
|
|
||||||
if nginxAffinity.AffinityType != "cookie" {
|
if nginxAffinity.Type != "cookie" {
|
||||||
t.Errorf("expected cookie as sticky-type but returned %v", nginxAffinity.AffinityType)
|
t.Errorf("expected cookie as sticky-type but returned %v", nginxAffinity.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if nginxAffinity.CookieConfig.Hash != "md5" {
|
if nginxAffinity.Cookie.Hash != "md5" {
|
||||||
t.Errorf("expected md5 as sticky-hash but returned %v", nginxAffinity.CookieConfig.Hash)
|
t.Errorf("expected md5 as sticky-hash but returned %v", nginxAffinity.Cookie.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if nginxAffinity.CookieConfig.Name != "INGRESSCOOKIE" {
|
if nginxAffinity.Cookie.Name != "INGRESSCOOKIE" {
|
||||||
t.Errorf("expected route as sticky-name but returned %v", nginxAffinity.CookieConfig.Name)
|
t.Errorf("expected route as sticky-name but returned %v", nginxAffinity.Cookie.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -19,24 +19,22 @@ package snippet
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -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")
|
||||||
}
|
}
|
|
@ -19,20 +19,18 @@ package sslpassthrough
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
ing_errors "k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -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")
|
||||||
}
|
}
|
|
@ -19,24 +19,22 @@ package upstreamhashby
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -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")
|
||||||
}
|
}
|
|
@ -19,24 +19,22 @@ package upstreamvhost
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -19,24 +19,22 @@ package vtsfilterkey
|
||||||
import (
|
import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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)
|
||||||
}
|
}
|
|
@ -25,12 +25,11 @@ import (
|
||||||
|
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
"k8s.io/client-go/tools/cache"
|
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/net/ssl"
|
"k8s.io/ingress-nginx/internal/net/ssl"
|
||||||
)
|
)
|
||||||
|
|
||||||
// syncSecret keeps in sync Secrets used by Ingress rules with the files on
|
// syncSecret keeps in sync Secrets used by Ingress rules with the files on
|
||||||
|
@ -93,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)
|
||||||
|
@ -146,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
|
||||||
}
|
}
|
||||||
|
@ -156,18 +155,3 @@ func (ic *NGINXController) checkMissingSecrets() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sslCertTracker holds a store of referenced Secrets in Ingress rules
|
|
||||||
type sslCertTracker struct {
|
|
||||||
cache.ThreadSafeStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSSLCertTracker() *sslCertTracker {
|
|
||||||
return &sslCertTracker{
|
|
||||||
cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sslCertTracker) DeleteAll(key string) {
|
|
||||||
s.Delete(key)
|
|
||||||
}
|
|
|
@ -28,9 +28,9 @@ import (
|
||||||
cache_client "k8s.io/client-go/tools/cache"
|
cache_client "k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/client-go/util/flowcontrol"
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/store"
|
"k8s.io/ingress-nginx/internal/ingress/store"
|
||||||
"k8s.io/ingress-nginx/pkg/task"
|
"k8s.io/ingress-nginx/internal/task"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ func buildGenericControllerForBackendSSL() *NGINXController {
|
||||||
Client: buildSimpleClientSetForBackendSSL(),
|
Client: buildSimpleClientSetForBackendSSL(),
|
||||||
},
|
},
|
||||||
listers: buildListers(),
|
listers: buildListers(),
|
||||||
sslCertTracker: newSSLCertTracker(),
|
sslCertTracker: store.NewSSLCertTracker(),
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.syncQueue = task.NewTaskQueue(gc.syncIngress)
|
gc.syncQueue = task.NewTaskQueue(gc.syncIngress)
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"k8s.io/apiserver/pkg/server/healthz"
|
"k8s.io/apiserver/pkg/server/healthz"
|
||||||
"k8s.io/kubernetes/pkg/util/filesystem"
|
"k8s.io/kubernetes/pkg/util/filesystem"
|
||||||
|
|
||||||
ngx_config "k8s.io/ingress-nginx/pkg/ingress/controller/config"
|
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNginxCheck(t *testing.T) {
|
func TestNginxCheck(t *testing.T) {
|
|
@ -25,8 +25,8 @@ import (
|
||||||
|
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/defaults"
|
"k8s.io/ingress-nginx/internal/ingress/defaults"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
|
@ -36,16 +36,17 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
"k8s.io/ingress-nginx/internal/ingress/annotations"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/healthcheck"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/healthcheck"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/proxy"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
ngx_config "k8s.io/ingress-nginx/pkg/ingress/controller/config"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/proxy"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/defaults"
|
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
"k8s.io/ingress-nginx/internal/ingress/defaults"
|
||||||
"k8s.io/ingress-nginx/pkg/k8s"
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
"k8s.io/ingress-nginx/pkg/task"
|
"k8s.io/ingress-nginx/internal/k8s"
|
||||||
|
"k8s.io/ingress-nginx/internal/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -65,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
|
||||||
|
@ -132,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.
|
||||||
|
@ -316,7 +324,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
|
||||||
for _, sp := range svc.Spec.Ports {
|
for _, sp := range svc.Spec.Ports {
|
||||||
if sp.Name == svcPort {
|
if sp.Name == svcPort {
|
||||||
if sp.Protocol == proto {
|
if sp.Protocol == proto {
|
||||||
endps = n.getEndpoints(svc, &sp, proto, &healthcheck.Upstream{})
|
endps = n.getEndpoints(svc, &sp, proto, &healthcheck.Config{})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,7 +335,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
|
||||||
for _, sp := range svc.Spec.Ports {
|
for _, sp := range svc.Spec.Ports {
|
||||||
if sp.Port == int32(targetPort) {
|
if sp.Port == int32(targetPort) {
|
||||||
if sp.Protocol == proto {
|
if sp.Protocol == proto {
|
||||||
endps = n.getEndpoints(svc, &sp, proto, &healthcheck.Upstream{})
|
endps = n.getEndpoints(svc, &sp, proto, &healthcheck.Config{})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,7 +387,7 @@ func (n *NGINXController) getDefaultUpstream() *ingress.Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
svc := svcObj.(*apiv1.Service)
|
svc := svcObj.(*apiv1.Service)
|
||||||
endps := n.getEndpoints(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, &healthcheck.Upstream{})
|
endps := n.getEndpoints(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, &healthcheck.Config{})
|
||||||
if len(endps) == 0 {
|
if len(endps) == 0 {
|
||||||
glog.Warningf("service %v does not have any active endpoints", svcKey)
|
glog.Warningf("service %v does not have any active endpoints", svcKey)
|
||||||
endps = []ingress.Endpoint{n.DefaultEndpoint()}
|
endps = []ingress.Endpoint{n.DefaultEndpoint()}
|
||||||
|
@ -398,8 +406,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
servers := n.createServers(ingresses, upstreams, du)
|
servers := n.createServers(ingresses, upstreams, du)
|
||||||
|
|
||||||
for _, ing := range ingresses {
|
for _, ing := range ingresses {
|
||||||
affinity := n.annotations.SessionAffinity(ing)
|
anns := n.getIngressAnnotations(ing)
|
||||||
anns := n.annotations.Extract(ing)
|
|
||||||
|
|
||||||
for _, rule := range ing.Spec.Rules {
|
for _, rule := range ing.Spec.Rules {
|
||||||
host := rule.Host
|
host := rule.Host
|
||||||
|
@ -418,13 +425,11 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
}
|
}
|
||||||
|
|
||||||
if server.CertificateAuth.CAFileName == "" {
|
if server.CertificateAuth.CAFileName == "" {
|
||||||
ca := n.annotations.CertificateAuth(ing)
|
server.CertificateAuth = anns.CertificateAuth
|
||||||
if ca != nil {
|
// It is possible that no CAFileName is found in the secret
|
||||||
server.CertificateAuth = *ca
|
if server.CertificateAuth.CAFileName == "" {
|
||||||
// It is possible that no CAFileName is found in the secret
|
glog.V(3).Infof("secret %v does not contain 'ca.crt', mutual authentication not enabled - ingress rule %v/%v.", server.CertificateAuth.Secret, ing.Namespace, ing.Name)
|
||||||
if server.CertificateAuth.CAFileName == "" {
|
|
||||||
glog.V(3).Infof("secret %v does not contain 'ca.crt', mutual authentication not enabled - ingress rule %v/%v.", server.CertificateAuth.Secret, ing.Namespace, ing.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
glog.V(3).Infof("server %v already contains a mutual authentication configuration - ingress rule %v/%v", server.Hostname, ing.Namespace, ing.Name)
|
glog.V(3).Infof("server %v already contains a mutual authentication configuration - ingress rule %v/%v", server.Hostname, ing.Namespace, ing.Name)
|
||||||
|
@ -461,7 +466,20 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
loc.Port = ups.Port
|
loc.Port = ups.Port
|
||||||
loc.Service = ups.Service
|
loc.Service = ups.Service
|
||||||
loc.Ingress = ing
|
loc.Ingress = ing
|
||||||
mergeLocationAnnotations(loc, anns)
|
loc.BasicDigestAuth = anns.BasicDigestAuth
|
||||||
|
loc.ClientBodyBufferSize = anns.ClientBodyBufferSize
|
||||||
|
loc.ConfigurationSnippet = anns.ConfigurationSnippet
|
||||||
|
loc.CorsConfig = anns.CorsConfig
|
||||||
|
loc.ExternalAuth = anns.ExternalAuth
|
||||||
|
loc.Proxy = anns.Proxy
|
||||||
|
loc.RateLimit = anns.RateLimit
|
||||||
|
loc.Redirect = anns.Redirect
|
||||||
|
loc.Rewrite = anns.Rewrite
|
||||||
|
loc.UpstreamVhost = anns.UpstreamVhost
|
||||||
|
loc.VtsFilterKey = anns.VtsFilterKey
|
||||||
|
loc.Whitelist = anns.Whitelist
|
||||||
|
loc.Denied = anns.Denied
|
||||||
|
|
||||||
if loc.Redirect.FromToWWW {
|
if loc.Redirect.FromToWWW {
|
||||||
server.RedirectFromToWWW = true
|
server.RedirectFromToWWW = true
|
||||||
}
|
}
|
||||||
|
@ -472,14 +490,27 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
if addLoc {
|
if addLoc {
|
||||||
glog.V(3).Infof("adding location %v in ingress rule %v/%v upstream %v", nginxPath, ing.Namespace, ing.Name, ups.Name)
|
glog.V(3).Infof("adding location %v in ingress rule %v/%v upstream %v", nginxPath, ing.Namespace, ing.Name, ups.Name)
|
||||||
loc := &ingress.Location{
|
loc := &ingress.Location{
|
||||||
Path: nginxPath,
|
Path: nginxPath,
|
||||||
Backend: ups.Name,
|
Backend: ups.Name,
|
||||||
IsDefBackend: false,
|
IsDefBackend: false,
|
||||||
Service: ups.Service,
|
Service: ups.Service,
|
||||||
Port: ups.Port,
|
Port: ups.Port,
|
||||||
Ingress: ing,
|
Ingress: ing,
|
||||||
|
BasicDigestAuth: anns.BasicDigestAuth,
|
||||||
|
ClientBodyBufferSize: anns.ClientBodyBufferSize,
|
||||||
|
ConfigurationSnippet: anns.ConfigurationSnippet,
|
||||||
|
CorsConfig: anns.CorsConfig,
|
||||||
|
ExternalAuth: anns.ExternalAuth,
|
||||||
|
Proxy: anns.Proxy,
|
||||||
|
RateLimit: anns.RateLimit,
|
||||||
|
Redirect: anns.Redirect,
|
||||||
|
Rewrite: anns.Rewrite,
|
||||||
|
UpstreamVhost: anns.UpstreamVhost,
|
||||||
|
VtsFilterKey: anns.VtsFilterKey,
|
||||||
|
Whitelist: anns.Whitelist,
|
||||||
|
Denied: anns.Denied,
|
||||||
}
|
}
|
||||||
mergeLocationAnnotations(loc, anns)
|
|
||||||
if loc.Redirect.FromToWWW {
|
if loc.Redirect.FromToWWW {
|
||||||
server.RedirectFromToWWW = true
|
server.RedirectFromToWWW = true
|
||||||
}
|
}
|
||||||
|
@ -487,12 +518,12 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
}
|
}
|
||||||
|
|
||||||
if ups.SessionAffinity.AffinityType == "" {
|
if ups.SessionAffinity.AffinityType == "" {
|
||||||
ups.SessionAffinity.AffinityType = affinity.AffinityType
|
ups.SessionAffinity.AffinityType = anns.SessionAffinity.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
if affinity.AffinityType == "cookie" {
|
if anns.SessionAffinity.Type == "cookie" {
|
||||||
ups.SessionAffinity.CookieSessionAffinity.Name = affinity.CookieConfig.Name
|
ups.SessionAffinity.CookieSessionAffinity.Name = anns.SessionAffinity.Cookie.Name
|
||||||
ups.SessionAffinity.CookieSessionAffinity.Hash = affinity.CookieConfig.Hash
|
ups.SessionAffinity.CookieSessionAffinity.Hash = anns.SessionAffinity.Cookie.Hash
|
||||||
|
|
||||||
locs := ups.SessionAffinity.CookieSessionAffinity.Locations
|
locs := ups.SessionAffinity.CookieSessionAffinity.Locations
|
||||||
if _, ok := locs[host]; !ok {
|
if _, ok := locs[host]; !ok {
|
||||||
|
@ -519,7 +550,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
|
||||||
// check if the location contains endpoints and a custom default backend
|
// check if the location contains endpoints and a custom default backend
|
||||||
if location.DefaultBackend != nil {
|
if location.DefaultBackend != nil {
|
||||||
sp := location.DefaultBackend.Spec.Ports[0]
|
sp := location.DefaultBackend.Spec.Ports[0]
|
||||||
endps := n.getEndpoints(location.DefaultBackend, &sp, apiv1.ProtocolTCP, &healthcheck.Upstream{})
|
endps := n.getEndpoints(location.DefaultBackend, &sp, apiv1.ProtocolTCP, &healthcheck.Config{})
|
||||||
if len(endps) > 0 {
|
if len(endps) > 0 {
|
||||||
glog.V(3).Infof("using custom default backend in server %v location %v (service %v/%v)",
|
glog.V(3).Infof("using custom default backend in server %v location %v (service %v/%v)",
|
||||||
server.Hostname, location.Path, location.DefaultBackend.Namespace, location.DefaultBackend.Name)
|
server.Hostname, location.Path, location.DefaultBackend.Namespace, location.DefaultBackend.Name)
|
||||||
|
@ -617,10 +648,7 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
||||||
upstreams[defUpstreamName] = du
|
upstreams[defUpstreamName] = du
|
||||||
|
|
||||||
for _, ing := range data {
|
for _, ing := range data {
|
||||||
secUpstream := n.annotations.SecureUpstream(ing)
|
anns := n.getIngressAnnotations(ing)
|
||||||
hz := n.annotations.HealthCheck(ing)
|
|
||||||
serviceUpstream := n.annotations.ServiceUpstream(ing)
|
|
||||||
upstreamHashBy := n.annotations.UpstreamHashBy(ing)
|
|
||||||
|
|
||||||
var defBackend string
|
var defBackend string
|
||||||
if ing.Spec.Backend != nil {
|
if ing.Spec.Backend != nil {
|
||||||
|
@ -635,7 +663,7 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
||||||
|
|
||||||
// Add the service cluster endpoint as the upstream instead of individual endpoints
|
// Add the service cluster endpoint as the upstream instead of individual endpoints
|
||||||
// if the serviceUpstream annotation is enabled
|
// if the serviceUpstream annotation is enabled
|
||||||
if serviceUpstream {
|
if anns.ServiceUpstream {
|
||||||
endpoint, err := n.getServiceClusterEndpoint(svcKey, ing.Spec.Backend)
|
endpoint, err := n.getServiceClusterEndpoint(svcKey, ing.Spec.Backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Failed to get service cluster endpoint for service %s: %v", svcKey, err)
|
glog.Errorf("Failed to get service cluster endpoint for service %s: %v", svcKey, err)
|
||||||
|
@ -645,7 +673,7 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(upstreams[defBackend].Endpoints) == 0 {
|
if len(upstreams[defBackend].Endpoints) == 0 {
|
||||||
endps, err := n.serviceEndpoints(svcKey, ing.Spec.Backend.ServicePort.String(), hz)
|
endps, err := n.serviceEndpoints(svcKey, ing.Spec.Backend.ServicePort.String(), &anns.HealthCheck)
|
||||||
upstreams[defBackend].Endpoints = append(upstreams[defBackend].Endpoints, endps...)
|
upstreams[defBackend].Endpoints = append(upstreams[defBackend].Endpoints, endps...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Warningf("error creating upstream %v: %v", defBackend, err)
|
glog.Warningf("error creating upstream %v: %v", defBackend, err)
|
||||||
|
@ -674,22 +702,22 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
||||||
upstreams[name].Port = path.Backend.ServicePort
|
upstreams[name].Port = path.Backend.ServicePort
|
||||||
|
|
||||||
if !upstreams[name].Secure {
|
if !upstreams[name].Secure {
|
||||||
upstreams[name].Secure = secUpstream.Secure
|
upstreams[name].Secure = anns.SecureUpstream.Secure
|
||||||
}
|
}
|
||||||
|
|
||||||
if upstreams[name].SecureCACert.Secret == "" {
|
if upstreams[name].SecureCACert.Secret == "" {
|
||||||
upstreams[name].SecureCACert = secUpstream.CACert
|
upstreams[name].SecureCACert = anns.SecureUpstream.CACert
|
||||||
}
|
}
|
||||||
|
|
||||||
if upstreams[name].UpstreamHashBy == "" {
|
if upstreams[name].UpstreamHashBy == "" {
|
||||||
upstreams[name].UpstreamHashBy = upstreamHashBy
|
upstreams[name].UpstreamHashBy = anns.UpstreamHashBy
|
||||||
}
|
}
|
||||||
|
|
||||||
svcKey := fmt.Sprintf("%v/%v", ing.GetNamespace(), path.Backend.ServiceName)
|
svcKey := fmt.Sprintf("%v/%v", ing.GetNamespace(), path.Backend.ServiceName)
|
||||||
|
|
||||||
// Add the service cluster endpoint as the upstream instead of individual endpoints
|
// Add the service cluster endpoint as the upstream instead of individual endpoints
|
||||||
// if the serviceUpstream annotation is enabled
|
// if the serviceUpstream annotation is enabled
|
||||||
if serviceUpstream {
|
if anns.ServiceUpstream {
|
||||||
endpoint, err := n.getServiceClusterEndpoint(svcKey, &path.Backend)
|
endpoint, err := n.getServiceClusterEndpoint(svcKey, &path.Backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("failed to get service cluster endpoint for service %s: %v", svcKey, err)
|
glog.Errorf("failed to get service cluster endpoint for service %s: %v", svcKey, err)
|
||||||
|
@ -699,7 +727,7 @@ func (n *NGINXController) createUpstreams(data []*extensions.Ingress, du *ingres
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(upstreams[name].Endpoints) == 0 {
|
if len(upstreams[name].Endpoints) == 0 {
|
||||||
endp, err := n.serviceEndpoints(svcKey, path.Backend.ServicePort.String(), hz)
|
endp, err := n.serviceEndpoints(svcKey, path.Backend.ServicePort.String(), &anns.HealthCheck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Warningf("error obtaining service endpoints: %v", err)
|
glog.Warningf("error obtaining service endpoints: %v", err)
|
||||||
continue
|
continue
|
||||||
|
@ -759,7 +787,7 @@ func (n *NGINXController) getServiceClusterEndpoint(svcKey string, backend *exte
|
||||||
// serviceEndpoints returns the upstream servers (endpoints) associated
|
// serviceEndpoints returns the upstream servers (endpoints) associated
|
||||||
// to a service.
|
// to a service.
|
||||||
func (n *NGINXController) serviceEndpoints(svcKey, backendPort string,
|
func (n *NGINXController) serviceEndpoints(svcKey, backendPort string,
|
||||||
hz *healthcheck.Upstream) ([]ingress.Endpoint, error) {
|
hz *healthcheck.Config) ([]ingress.Endpoint, error) {
|
||||||
svc, err := n.listers.Service.GetByName(svcKey)
|
svc, err := n.listers.Service.GetByName(svcKey)
|
||||||
|
|
||||||
var upstreams []ingress.Endpoint
|
var upstreams []ingress.Endpoint
|
||||||
|
@ -843,7 +871,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
||||||
aliases := make(map[string]string, len(data))
|
aliases := make(map[string]string, len(data))
|
||||||
|
|
||||||
bdef := n.GetDefaultBackend()
|
bdef := n.GetDefaultBackend()
|
||||||
ngxProxy := proxy.Configuration{
|
ngxProxy := proxy.Config{
|
||||||
BodySize: bdef.ProxyBodySize,
|
BodySize: bdef.ProxyBodySize,
|
||||||
ConnectTimeout: bdef.ProxyConnectTimeout,
|
ConnectTimeout: bdef.ProxyConnectTimeout,
|
||||||
SendTimeout: bdef.ProxySendTimeout,
|
SendTimeout: bdef.ProxySendTimeout,
|
||||||
|
@ -884,9 +912,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
||||||
|
|
||||||
// initialize all the servers
|
// initialize all the servers
|
||||||
for _, ing := range data {
|
for _, ing := range data {
|
||||||
|
anns := n.getIngressAnnotations(ing)
|
||||||
// check if ssl passthrough is configured
|
|
||||||
sslpt := n.annotations.SSLPassthrough(ing)
|
|
||||||
|
|
||||||
// default upstream server
|
// default upstream server
|
||||||
un := du.Name
|
un := du.Name
|
||||||
|
@ -930,16 +956,14 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
||||||
Service: &apiv1.Service{},
|
Service: &apiv1.Service{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SSLPassthrough: sslpt,
|
SSLPassthrough: anns.SSLPassthrough,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure default location, alias, and SSL
|
// configure default location, alias, and SSL
|
||||||
for _, ing := range data {
|
for _, ing := range data {
|
||||||
// setup server-alias based on annotations
|
anns := n.getIngressAnnotations(ing)
|
||||||
aliasAnnotation := n.annotations.Alias(ing)
|
|
||||||
srvsnippet := n.annotations.ServerSnippet(ing)
|
|
||||||
|
|
||||||
for _, rule := range ing.Spec.Rules {
|
for _, rule := range ing.Spec.Rules {
|
||||||
host := rule.Host
|
host := rule.Host
|
||||||
|
@ -948,11 +972,11 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup server aliases
|
// setup server aliases
|
||||||
if aliasAnnotation != "" {
|
if anns.Alias != "" {
|
||||||
if servers[host].Alias == "" {
|
if servers[host].Alias == "" {
|
||||||
servers[host].Alias = aliasAnnotation
|
servers[host].Alias = anns.Alias
|
||||||
if _, ok := aliases[aliasAnnotation]; !ok {
|
if _, ok := aliases["Alias"]; !ok {
|
||||||
aliases[aliasAnnotation] = host
|
aliases["Alias"] = host
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
glog.Warningf("ingress %v/%v for host %v contains an Alias but one has already been configured.",
|
glog.Warningf("ingress %v/%v for host %v contains an Alias but one has already been configured.",
|
||||||
|
@ -961,14 +985,14 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
||||||
}
|
}
|
||||||
|
|
||||||
//notifying the user that it has already been configured.
|
//notifying the user that it has already been configured.
|
||||||
if servers[host].ServerSnippet != "" && srvsnippet != "" {
|
if servers[host].ServerSnippet != "" && anns.ServerSnippet != "" {
|
||||||
glog.Warningf("ingress %v/%v for host %v contains a Server Snippet section that it has already been configured.",
|
glog.Warningf("ingress %v/%v for host %v contains a Server Snippet section that it has already been configured.",
|
||||||
ing.Namespace, ing.Name, host)
|
ing.Namespace, ing.Name, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// only add a server snippet if the server does not have one previously configured
|
// only add a server snippet if the server does not have one previously configured
|
||||||
if servers[host].ServerSnippet == "" && srvsnippet != "" {
|
if servers[host].ServerSnippet == "" && anns.ServerSnippet != "" {
|
||||||
servers[host].ServerSnippet = srvsnippet
|
servers[host].ServerSnippet = anns.ServerSnippet
|
||||||
}
|
}
|
||||||
|
|
||||||
// only add a certificate if the server does not have one previously configured
|
// only add a certificate if the server does not have one previously configured
|
||||||
|
@ -992,8 +1016,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
glog.Warningf("ingress %v/%v for host %v contains a TLS section but none of the host match",
|
// does not contains a TLS section but none of the host match
|
||||||
ing.Namespace, ing.Name, host)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1044,7 +1067,7 @@ func (n *NGINXController) getEndpoints(
|
||||||
s *apiv1.Service,
|
s *apiv1.Service,
|
||||||
servicePort *apiv1.ServicePort,
|
servicePort *apiv1.ServicePort,
|
||||||
proto apiv1.Protocol,
|
proto apiv1.Protocol,
|
||||||
hz *healthcheck.Upstream) []ingress.Endpoint {
|
hz *healthcheck.Config) []ingress.Endpoint {
|
||||||
|
|
||||||
upsServers := []ingress.Endpoint{}
|
upsServers := []ingress.Endpoint{}
|
||||||
|
|
||||||
|
@ -1141,7 +1164,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
|
||||||
}
|
}
|
||||||
|
@ -1152,6 +1175,7 @@ func (n *NGINXController) isForceReload() bool {
|
||||||
return atomic.LoadInt32(&n.forceReload) != 0
|
return atomic.LoadInt32(&n.forceReload) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetForceReload sets if the ingress controller should be reloaded or not
|
||||||
func (n *NGINXController) SetForceReload(shouldReload bool) {
|
func (n *NGINXController) SetForceReload(shouldReload bool) {
|
||||||
if shouldReload {
|
if shouldReload {
|
||||||
atomic.StoreInt32(&n.forceReload, 1)
|
atomic.StoreInt32(&n.forceReload, 1)
|
||||||
|
@ -1160,3 +1184,24 @@ func (n *NGINXController) SetForceReload(shouldReload bool) {
|
||||||
atomic.StoreInt32(&n.forceReload, 0)
|
atomic.StoreInt32(&n.forceReload, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *NGINXController) extractAnnotations(ing *extensions.Ingress) {
|
||||||
|
anns := n.annotations.Extract(ing)
|
||||||
|
glog.V(3).Infof("updating annotations information for ingres %v/%v", anns.Namespace, anns.Name)
|
||||||
|
n.listers.IngressAnnotation.Update(anns)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getByIngress returns the parsed annotations from an Ingress
|
||||||
|
func (n *NGINXController) getIngressAnnotations(ing *extensions.Ingress) *annotations.Ingress {
|
||||||
|
key := fmt.Sprintf("%v/%v", ing.Namespace, ing.Name)
|
||||||
|
item, exists, err := n.listers.IngressAnnotation.GetByKey(key)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unexpected error getting ingress annotation %v: %v", key, err)
|
||||||
|
return &annotations.Ingress{}
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
glog.Errorf("ingress annotation %v was not found", key)
|
||||||
|
return &annotations.Ingress{}
|
||||||
|
}
|
||||||
|
return item.(*annotations.Ingress)
|
||||||
|
}
|
|
@ -27,10 +27,11 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
cache_client "k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cacheController struct {
|
type cacheController struct {
|
||||||
|
@ -60,19 +61,17 @@ func (c *cacheController) Run(stopCh chan struct{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreLister {
|
func (n *NGINXController) createListers(stopCh chan struct{}) (*ingress.StoreLister, *cacheController) {
|
||||||
// from here to the end of the method all the code is just boilerplate
|
|
||||||
// required to watch Ingress, Secrets, ConfigMaps and Endoints.
|
|
||||||
// This is used to detect new content, updates or removals and act accordingly
|
|
||||||
ingEventHandler := cache.ResourceEventHandlerFuncs{
|
ingEventHandler := cache.ResourceEventHandlerFuncs{
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.extractAnnotations(addIng)
|
||||||
n.recorder.Eventf(addIng, apiv1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", addIng.Namespace, addIng.Name))
|
n.recorder.Eventf(addIng, apiv1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", addIng.Namespace, addIng.Name))
|
||||||
n.syncQueue.Enqueue(obj)
|
n.syncQueue.Enqueue(obj)
|
||||||
},
|
},
|
||||||
|
@ -96,6 +95,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n.recorder.Eventf(delIng, apiv1.EventTypeNormal, "DELETE", fmt.Sprintf("Ingress %s/%s", delIng.Namespace, delIng.Name))
|
n.recorder.Eventf(delIng, apiv1.EventTypeNormal, "DELETE", fmt.Sprintf("Ingress %s/%s", delIng.Namespace, delIng.Name))
|
||||||
|
n.listers.IngressAnnotation.Delete(delIng)
|
||||||
n.syncQueue.Enqueue(obj)
|
n.syncQueue.Enqueue(obj)
|
||||||
},
|
},
|
||||||
UpdateFunc: func(old, cur interface{}) {
|
UpdateFunc: func(old, cur interface{}) {
|
||||||
|
@ -113,6 +113,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
||||||
n.recorder.Eventf(curIng, apiv1.EventTypeNormal, "UPDATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
|
n.recorder.Eventf(curIng, apiv1.EventTypeNormal, "UPDATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.extractAnnotations(curIng)
|
||||||
n.syncQueue.Enqueue(cur)
|
n.syncQueue.Enqueue(cur)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -141,7 +142,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key := fmt.Sprintf("%v/%v", sec.Namespace, sec.Name)
|
key := fmt.Sprintf("%v/%v", sec.Namespace, sec.Name)
|
||||||
n.sslCertTracker.DeleteAll(key)
|
n.sslCertTracker.Delete(key)
|
||||||
n.syncQueue.Enqueue(key)
|
n.syncQueue.Enqueue(key)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -196,6 +197,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
||||||
}
|
}
|
||||||
|
|
||||||
lister := &ingress.StoreLister{}
|
lister := &ingress.StoreLister{}
|
||||||
|
lister.IngressAnnotation.Store = cache_client.NewStore(cache_client.DeletionHandlingMetaNamespaceKeyFunc)
|
||||||
|
|
||||||
controller := &cacheController{}
|
controller := &cacheController{}
|
||||||
|
|
||||||
|
@ -219,7 +221,5 @@ func (n *NGINXController) createListers(stopCh chan struct{}) *ingress.StoreList
|
||||||
cache.NewListWatchFromClient(n.cfg.Client.CoreV1().RESTClient(), "services", n.cfg.Namespace, fields.Everything()),
|
cache.NewListWatchFromClient(n.cfg.Client.CoreV1().RESTClient(), "services", n.cfg.Namespace, fields.Everything()),
|
||||||
&apiv1.Service{}, n.cfg.ResyncPeriod, cache.ResourceEventHandlerFuncs{})
|
&apiv1.Service{}, n.cfg.ResyncPeriod, cache.ResourceEventHandlerFuncs{})
|
||||||
|
|
||||||
controller.Run(n.stopCh)
|
return lister, controller
|
||||||
|
|
||||||
return lister
|
|
||||||
}
|
}
|
|
@ -19,7 +19,7 @@ package controller
|
||||||
import (
|
import (
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
|
@ -42,18 +42,20 @@ import (
|
||||||
"k8s.io/client-go/util/flowcontrol"
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
"k8s.io/kubernetes/pkg/util/filesystem"
|
"k8s.io/kubernetes/pkg/util/filesystem"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
"k8s.io/ingress-nginx/internal/ingress/annotations"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
|
||||||
ngx_config "k8s.io/ingress-nginx/pkg/ingress/controller/config"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/controller/process"
|
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||||
ngx_template "k8s.io/ingress-nginx/pkg/ingress/controller/template"
|
"k8s.io/ingress-nginx/internal/ingress/controller/process"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/defaults"
|
ngx_template "k8s.io/ingress-nginx/internal/ingress/controller/template"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/status"
|
"k8s.io/ingress-nginx/internal/ingress/defaults"
|
||||||
ing_net "k8s.io/ingress-nginx/pkg/net"
|
"k8s.io/ingress-nginx/internal/ingress/status"
|
||||||
"k8s.io/ingress-nginx/pkg/net/dns"
|
"k8s.io/ingress-nginx/internal/ingress/store"
|
||||||
"k8s.io/ingress-nginx/pkg/net/ssl"
|
ing_net "k8s.io/ingress-nginx/internal/net"
|
||||||
"k8s.io/ingress-nginx/pkg/task"
|
"k8s.io/ingress-nginx/internal/net/dns"
|
||||||
|
"k8s.io/ingress-nginx/internal/net/ssl"
|
||||||
|
"k8s.io/ingress-nginx/internal/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type statusModule string
|
type statusModule string
|
||||||
|
@ -102,7 +104,7 @@ func NewNGINXController(config *Configuration) *NGINXController {
|
||||||
|
|
||||||
resolver: h,
|
resolver: h,
|
||||||
cfg: config,
|
cfg: config,
|
||||||
sslCertTracker: newSSLCertTracker(),
|
sslCertTracker: store.NewSSLCertTracker(),
|
||||||
syncRateLimiter: flowcontrol.NewTokenBucketRateLimiter(0.3, 1),
|
syncRateLimiter: flowcontrol.NewTokenBucketRateLimiter(0.3, 1),
|
||||||
|
|
||||||
recorder: eventBroadcaster.NewRecorder(scheme.Scheme, apiv1.EventSource{
|
recorder: eventBroadcaster.NewRecorder(scheme.Scheme, apiv1.EventSource{
|
||||||
|
@ -115,11 +117,13 @@ func NewNGINXController(config *Configuration) *NGINXController {
|
||||||
fileSystem: filesystem.DefaultFs{},
|
fileSystem: filesystem.DefaultFs{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.listers, n.controllers = n.createListers(n.stopCh)
|
||||||
|
|
||||||
n.stats = newStatsCollector(config.Namespace, config.IngressClass, n.binary, n.cfg.ListenPorts.Status)
|
n.stats = newStatsCollector(config.Namespace, config.IngressClass, n.binary, n.cfg.ListenPorts.Status)
|
||||||
|
|
||||||
n.syncQueue = task.NewTaskQueue(n.syncIngress)
|
n.syncQueue = task.NewTaskQueue(n.syncIngress)
|
||||||
|
|
||||||
n.listers = n.createListers(n.stopCh)
|
n.annotations = annotations.NewAnnotationExtractor(n)
|
||||||
|
|
||||||
if config.UpdateStatus {
|
if config.UpdateStatus {
|
||||||
n.syncStatus = status.NewStatusSyncer(status.Config{
|
n.syncStatus = status.NewStatusSyncer(status.Config{
|
||||||
|
@ -135,7 +139,6 @@ func NewNGINXController(config *Configuration) *NGINXController {
|
||||||
} else {
|
} else {
|
||||||
glog.Warning("Update of ingress status is disabled (flag --update-status=false was specified)")
|
glog.Warning("Update of ingress status is disabled (flag --update-status=false was specified)")
|
||||||
}
|
}
|
||||||
n.annotations = newAnnotationExtractor(n)
|
|
||||||
|
|
||||||
var onChange func()
|
var onChange func()
|
||||||
onChange = func() {
|
onChange = func() {
|
||||||
|
@ -170,9 +173,10 @@ Error loading new template : %v
|
||||||
type NGINXController struct {
|
type NGINXController struct {
|
||||||
cfg *Configuration
|
cfg *Configuration
|
||||||
|
|
||||||
listers *ingress.StoreLister
|
listers *ingress.StoreLister
|
||||||
|
controllers *cacheController
|
||||||
|
|
||||||
annotations annotationExtractor
|
annotations annotations.Extractor
|
||||||
|
|
||||||
recorder record.EventRecorder
|
recorder record.EventRecorder
|
||||||
|
|
||||||
|
@ -182,7 +186,7 @@ type NGINXController struct {
|
||||||
|
|
||||||
// local store of SSL certificates
|
// local store of SSL certificates
|
||||||
// (only certificates used in ingress)
|
// (only certificates used in ingress)
|
||||||
sslCertTracker *sslCertTracker
|
sslCertTracker *store.SSLCertTracker
|
||||||
|
|
||||||
syncRateLimiter flowcontrol.RateLimiter
|
syncRateLimiter flowcontrol.RateLimiter
|
||||||
|
|
||||||
|
@ -234,13 +238,15 @@ type NGINXController struct {
|
||||||
func (n *NGINXController) Start() {
|
func (n *NGINXController) Start() {
|
||||||
glog.Infof("starting Ingress controller")
|
glog.Infof("starting Ingress controller")
|
||||||
|
|
||||||
|
n.controllers.Run(n.stopCh)
|
||||||
|
|
||||||
// initial sync of secrets to avoid unnecessary reloads
|
// initial sync of secrets to avoid unnecessary reloads
|
||||||
glog.Info("running initial sync of secrets")
|
glog.Info("running initial sync of secrets")
|
||||||
for _, obj := range n.listers.Ingress.List() {
|
for _, obj := range n.listers.Ingress.List() {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -425,12 +431,12 @@ func (n *NGINXController) SetConfig(cmap *apiv1.ConfigMap) {
|
||||||
n.backendDefaults = c.Backend
|
n.backendDefaults = c.Backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnUpdate is called by syncQueue in https://github.com/kubernetes/ingress-nginx/blob/master/pkg/ingress/controller/controller.go#L426
|
// OnUpdate is called periodically by syncQueue to keep the configuration in sync.
|
||||||
// periodically to keep the configuration in sync.
|
//
|
||||||
|
// 1. converts configmap configuration to custom configuration object
|
||||||
|
// 2. write the custom template (the complexity depends on the implementation)
|
||||||
|
// 3. write the configuration file
|
||||||
//
|
//
|
||||||
// convert configmap to custom configuration object (different in each implementation)
|
|
||||||
// write the custom template (the complexity depends on the implementation)
|
|
||||||
// write the configuration file
|
|
||||||
// returning nill implies the backend will be reloaded.
|
// returning nill implies the backend will be reloaded.
|
||||||
// if an error is returned means requeue the update
|
// if an error is returned means requeue the update
|
||||||
func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
|
func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/controller/metric/collector"
|
"k8s.io/ingress-nginx/internal/ingress/controller/metric/collector"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
51
internal/ingress/controller/template/buffer_pool.go
Normal file
51
internal/ingress/controller/template/buffer_pool.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
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 template
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BufferPool defines a Pool of Buffers
|
||||||
|
type BufferPool struct {
|
||||||
|
sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBufferPool creates a new BufferPool with a custom buffer size
|
||||||
|
func NewBufferPool(s int) *BufferPool {
|
||||||
|
return &BufferPool{
|
||||||
|
Pool: sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
b := bytes.NewBuffer(make([]byte, s))
|
||||||
|
b.Reset()
|
||||||
|
return b
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a Buffer from the pool
|
||||||
|
func (bp *BufferPool) Get() *bytes.Buffer {
|
||||||
|
return bp.Pool.Get().(*bytes.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put resets ans returns a Buffer to the pool
|
||||||
|
func (bp *BufferPool) Put(b *bytes.Buffer) {
|
||||||
|
b.Reset()
|
||||||
|
bp.Pool.Put(b)
|
||||||
|
}
|
|
@ -26,8 +26,8 @@ import (
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/controller/config"
|
"k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||||
ing_net "k8s.io/ingress-nginx/pkg/net"
|
ing_net "k8s.io/ingress-nginx/internal/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
|
@ -21,7 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/kylelemons/godebug/pretty"
|
"github.com/kylelemons/godebug/pretty"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/controller/config"
|
"k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFilterErrors(t *testing.T) {
|
func TestFilterErrors(t *testing.T) {
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
package template
|
package template
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -35,11 +34,11 @@ import (
|
||||||
|
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/ratelimit"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/controller/config"
|
"k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||||
ing_net "k8s.io/ingress-nginx/pkg/net"
|
ing_net "k8s.io/ingress-nginx/internal/net"
|
||||||
"k8s.io/ingress-nginx/pkg/watch"
|
"k8s.io/ingress-nginx/internal/watch"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -52,7 +51,7 @@ const (
|
||||||
type Template struct {
|
type Template struct {
|
||||||
tmpl *text_template.Template
|
tmpl *text_template.Template
|
||||||
fw watch.FileWatcher
|
fw watch.FileWatcher
|
||||||
s int
|
bp *BufferPool
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewTemplate returns a new Template instance or an
|
//NewTemplate returns a new Template instance or an
|
||||||
|
@ -70,7 +69,7 @@ func NewTemplate(file string, onChange func()) (*Template, error) {
|
||||||
return &Template{
|
return &Template{
|
||||||
tmpl: tmpl,
|
tmpl: tmpl,
|
||||||
fw: fw,
|
fw: fw,
|
||||||
s: defBufferSize,
|
bp: NewBufferPool(defBufferSize),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,15 +81,11 @@ func (t *Template) Close() {
|
||||||
// Write populates a buffer using a template with NGINX configuration
|
// Write populates a buffer using a template with NGINX configuration
|
||||||
// and the servers and upstreams created by Ingress rules
|
// and the servers and upstreams created by Ingress rules
|
||||||
func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) {
|
func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) {
|
||||||
tmplBuf := bytes.NewBuffer(make([]byte, 0, t.s))
|
tmplBuf := t.bp.Get()
|
||||||
outCmdBuf := bytes.NewBuffer(make([]byte, 0, t.s))
|
defer t.bp.Put(tmplBuf)
|
||||||
|
|
||||||
defer func() {
|
outCmdBuf := t.bp.Get()
|
||||||
if t.s < tmplBuf.Cap() {
|
defer t.bp.Put(outCmdBuf)
|
||||||
glog.V(2).Infof("adjusting template buffer size from %v to %v", t.s, tmplBuf.Cap())
|
|
||||||
t.s = tmplBuf.Cap()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if glog.V(3) {
|
if glog.V(3) {
|
||||||
b, err := json.Marshal(conf)
|
b, err := json.Marshal(conf)
|
||||||
|
@ -198,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 {
|
||||||
|
@ -273,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)
|
||||||
|
@ -354,8 +349,8 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Needs Unit Tests
|
// TODO: Needs Unit Tests
|
||||||
func filterRateLimits(input interface{}) []ratelimit.RateLimit {
|
func filterRateLimits(input interface{}) []ratelimit.Config {
|
||||||
ratelimits := []ratelimit.RateLimit{}
|
ratelimits := []ratelimit.Config{}
|
||||||
found := sets.String{}
|
found := sets.String{}
|
||||||
|
|
||||||
servers, ok := input.([]*ingress.Server)
|
servers, ok := input.([]*ingress.Server)
|
|
@ -26,10 +26,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/authreq"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/authreq"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/rewrite"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/rewrite"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/controller/config"
|
"k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -114,7 +114,7 @@ func TestBuildLocation(t *testing.T) {
|
||||||
for k, tc := range tmplFuncTestcases {
|
for k, tc := range tmplFuncTestcases {
|
||||||
loc := &ingress.Location{
|
loc := &ingress.Location{
|
||||||
Path: tc.Path,
|
Path: tc.Path,
|
||||||
Rewrite: rewrite.Redirect{Target: tc.Target, AddBaseURL: tc.AddBaseURL},
|
Rewrite: rewrite.Config{Target: tc.Target, AddBaseURL: tc.AddBaseURL},
|
||||||
}
|
}
|
||||||
|
|
||||||
newLoc := buildLocation(loc)
|
newLoc := buildLocation(loc)
|
||||||
|
@ -128,7 +128,7 @@ func TestBuildProxyPass(t *testing.T) {
|
||||||
for k, tc := range tmplFuncTestcases {
|
for k, tc := range tmplFuncTestcases {
|
||||||
loc := &ingress.Location{
|
loc := &ingress.Location{
|
||||||
Path: tc.Path,
|
Path: tc.Path,
|
||||||
Rewrite: rewrite.Redirect{Target: tc.Target, AddBaseURL: tc.AddBaseURL, BaseURLScheme: tc.BaseURLScheme},
|
Rewrite: rewrite.Config{Target: tc.Target, AddBaseURL: tc.AddBaseURL, BaseURLScheme: tc.BaseURLScheme},
|
||||||
Backend: "upstream-name",
|
Backend: "upstream-name",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ func TestBuildProxyPass(t *testing.T) {
|
||||||
|
|
||||||
func TestBuildAuthResponseHeaders(t *testing.T) {
|
func TestBuildAuthResponseHeaders(t *testing.T) {
|
||||||
loc := &ingress.Location{
|
loc := &ingress.Location{
|
||||||
ExternalAuth: authreq.External{ResponseHeaders: []string{"h1", "H-With-Caps-And-Dashes"}},
|
ExternalAuth: authreq.Config{ResponseHeaders: []string{"h1", "H-With-Caps-And-Dashes"}},
|
||||||
}
|
}
|
||||||
headers := buildAuthResponseHeaders(loc)
|
headers := buildAuthResponseHeaders(loc)
|
||||||
expected := []string{
|
expected := []string{
|
|
@ -21,17 +21,12 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
|
||||||
|
|
||||||
api "k8s.io/api/core/v1"
|
api "k8s.io/api/core/v1"
|
||||||
"k8s.io/kubernetes/pkg/util/sysctl"
|
"k8s.io/kubernetes/pkg/util/sysctl"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress"
|
"k8s.io/ingress-nginx/internal/ingress"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeniedKeyName name of the key that contains the reason to deny a location
|
|
||||||
const DeniedKeyName = "Denied"
|
|
||||||
|
|
||||||
// newUpstream creates an upstream without servers.
|
// newUpstream creates an upstream without servers.
|
||||||
func newUpstream(name string) *ingress.Backend {
|
func newUpstream(name string) *ingress.Backend {
|
||||||
return &ingress.Backend{
|
return &ingress.Backend{
|
||||||
|
@ -46,17 +41,6 @@ func newUpstream(name string) *ingress.Backend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeLocationAnnotations(loc *ingress.Location, anns map[string]interface{}) {
|
|
||||||
if _, ok := anns[DeniedKeyName]; ok {
|
|
||||||
loc.Denied = anns[DeniedKeyName].(error)
|
|
||||||
}
|
|
||||||
delete(anns, DeniedKeyName)
|
|
||||||
err := mergo.Map(loc, anns)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("unexpected error merging extracted annotations in location type: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sysctlSomaxconn returns the value of net.core.somaxconn, i.e.
|
// sysctlSomaxconn returns the value of net.core.somaxconn, i.e.
|
||||||
// maximum number of connections that can be queued for acceptance
|
// maximum number of connections that can be queued for acceptance
|
||||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
|
// http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
|
61
internal/ingress/controller/util_test.go
Normal file
61
internal/ingress/controller/util_test.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeError struct{}
|
||||||
|
|
||||||
|
func (fe *fakeError) Error() string {
|
||||||
|
return "fakeError"
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntInSlice(t *testing.T) {
|
||||||
|
fooTests := []struct {
|
||||||
|
i int
|
||||||
|
list []int
|
||||||
|
er bool
|
||||||
|
}{
|
||||||
|
{1, []int{1, 2}, true},
|
||||||
|
{3, []int{1, 2}, false},
|
||||||
|
{1, nil, false},
|
||||||
|
{0, nil, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fooTest := range fooTests {
|
||||||
|
r := intInSlice(fooTest.i, fooTest.list)
|
||||||
|
if r != fooTest.er {
|
||||||
|
t.Errorf("returned %t but expected %t for s=%v & list=%v", r, fooTest.er, fooTest.i, fooTest.list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSysctlFSFileMax(t *testing.T) {
|
||||||
|
i := sysctlFSFileMax()
|
||||||
|
if i < 1 {
|
||||||
|
t.Errorf("returned %v but expected > 0", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSysctlSomaxconn(t *testing.T) {
|
||||||
|
i := sysctlSomaxconn()
|
||||||
|
if i < 511 {
|
||||||
|
t.Errorf("returned %v but expected >= 511", i)
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,32 +19,27 @@ package resolver
|
||||||
import (
|
import (
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/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
|
56
internal/ingress/resolver/mock.go
Normal file
56
internal/ingress/resolver/mock.go
Normal 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)
|
||||||
|
}
|
|
@ -40,10 +40,10 @@ import (
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util/sliceutils"
|
"k8s.io/kubernetes/pkg/kubelet/util/sliceutils"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/store"
|
"k8s.io/ingress-nginx/internal/ingress/store"
|
||||||
"k8s.io/ingress-nginx/pkg/k8s"
|
"k8s.io/ingress-nginx/internal/k8s"
|
||||||
"k8s.io/ingress-nginx/pkg/task"
|
"k8s.io/ingress-nginx/internal/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
|
@ -28,10 +28,10 @@ import (
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/store"
|
"k8s.io/ingress-nginx/internal/ingress/store"
|
||||||
"k8s.io/ingress-nginx/pkg/k8s"
|
"k8s.io/ingress-nginx/internal/k8s"
|
||||||
"k8s.io/ingress-nginx/pkg/task"
|
"k8s.io/ingress-nginx/internal/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
func buildLoadBalancerIngressByIP() []apiv1.LoadBalancerIngress {
|
func buildLoadBalancerIngressByIP() []apiv1.LoadBalancerIngress {
|
|
@ -28,6 +28,11 @@ type IngressLister struct {
|
||||||
cache.Store
|
cache.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IngressAnnotationsLister makes a Store that lists annotations in Ingress rules.
|
||||||
|
type IngressAnnotationsLister struct {
|
||||||
|
cache.Store
|
||||||
|
}
|
||||||
|
|
||||||
// SecretLister makes a Store that lists Secrets.
|
// SecretLister makes a Store that lists Secrets.
|
||||||
type SecretLister struct {
|
type SecretLister struct {
|
||||||
cache.Store
|
cache.Store
|
||||||
|
@ -94,3 +99,15 @@ func (s *EndpointLister) GetServiceEndpoints(svc *apiv1.Service) (*apiv1.Endpoin
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("could not find endpoints for service: %v", svc.Name)
|
return nil, fmt.Errorf("could not find endpoints for service: %v", svc.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSLCertTracker holds a store of referenced Secrets in Ingress rules
|
||||||
|
type SSLCertTracker struct {
|
||||||
|
cache.ThreadSafeStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSSLCertTracker creates a new SSLCertTracker store
|
||||||
|
func NewSSLCertTracker() *SSLCertTracker {
|
||||||
|
return &SSLCertTracker{
|
||||||
|
cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,17 +23,17 @@ import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/auth"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/auth"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/authreq"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/authreq"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/authtls"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/authtls"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/cors"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/cors"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/ipwhitelist"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/proxy"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/proxy"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/ratelimit"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/redirect"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/redirect"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/annotations/rewrite"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/rewrite"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/resolver"
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||||
"k8s.io/ingress-nginx/pkg/ingress/store"
|
"k8s.io/ingress-nginx/internal/ingress/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -47,11 +47,12 @@ var (
|
||||||
// StoreLister returns the configured stores for ingresses, services,
|
// StoreLister returns the configured stores for ingresses, services,
|
||||||
// endpoints, secrets and configmaps.
|
// endpoints, secrets and configmaps.
|
||||||
type StoreLister struct {
|
type StoreLister struct {
|
||||||
Ingress store.IngressLister
|
Ingress store.IngressLister
|
||||||
Service store.ServiceLister
|
Service store.ServiceLister
|
||||||
Endpoint store.EndpointLister
|
Endpoint store.EndpointLister
|
||||||
Secret store.SecretLister
|
Secret store.SecretLister
|
||||||
ConfigMap store.ConfigMapLister
|
ConfigMap store.ConfigMapLister
|
||||||
|
IngressAnnotation store.IngressAnnotationsLister
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration holds the definition of all the parts required to describe all
|
// Configuration holds the definition of all the parts required to describe all
|
||||||
|
@ -165,7 +166,7 @@ type Server struct {
|
||||||
RedirectFromToWWW bool `json:"redirectFromToWWW,omitempty"`
|
RedirectFromToWWW bool `json:"redirectFromToWWW,omitempty"`
|
||||||
// CertificateAuth indicates the this server requires mutual authentication
|
// CertificateAuth indicates the this server requires mutual authentication
|
||||||
// +optional
|
// +optional
|
||||||
CertificateAuth authtls.AuthSSLConfig `json:"certificateAuth"`
|
CertificateAuth authtls.Config `json:"certificateAuth"`
|
||||||
|
|
||||||
// ServerSnippet returns the snippet of server
|
// ServerSnippet returns the snippet of server
|
||||||
// +optional
|
// +optional
|
||||||
|
@ -211,28 +212,28 @@ type Location struct {
|
||||||
// BasicDigestAuth returns authentication configuration for
|
// BasicDigestAuth returns authentication configuration for
|
||||||
// an Ingress rule.
|
// an Ingress rule.
|
||||||
// +optional
|
// +optional
|
||||||
BasicDigestAuth auth.BasicDigest `json:"basicDigestAuth,omitempty"`
|
BasicDigestAuth auth.Config `json:"basicDigestAuth,omitempty"`
|
||||||
// Denied returns an error when this location cannot not be allowed
|
// Denied returns an error when this location cannot not be allowed
|
||||||
// Requesting a denied location should return HTTP code 403.
|
// Requesting a denied location should return HTTP code 403.
|
||||||
Denied error `json:"denied,omitempty"`
|
Denied error `json:"denied,omitempty"`
|
||||||
// CorsConfig returns the Cors Configration for the ingress rule
|
// CorsConfig returns the Cors Configration for the ingress rule
|
||||||
// +optional
|
// +optional
|
||||||
CorsConfig cors.CorsConfig `json:"corsConfig,omitempty"`
|
CorsConfig cors.Config `json:"corsConfig,omitempty"`
|
||||||
// ExternalAuth indicates the access to this location requires
|
// ExternalAuth indicates the access to this location requires
|
||||||
// authentication using an external provider
|
// authentication using an external provider
|
||||||
// +optional
|
// +optional
|
||||||
ExternalAuth authreq.External `json:"externalAuth,omitempty"`
|
ExternalAuth authreq.Config `json:"externalAuth,omitempty"`
|
||||||
// RateLimit describes a limit in the number of connections per IP
|
// RateLimit describes a limit in the number of connections per IP
|
||||||
// address or connections per second.
|
// address or connections per second.
|
||||||
// The Redirect annotation precedes RateLimit
|
// The Redirect annotation precedes RateLimit
|
||||||
// +optional
|
// +optional
|
||||||
RateLimit ratelimit.RateLimit `json:"rateLimit,omitempty"`
|
RateLimit ratelimit.Config `json:"rateLimit,omitempty"`
|
||||||
// Redirect describes a temporal o permanent redirection this location.
|
// Redirect describes a temporal o permanent redirection this location.
|
||||||
// +optional
|
// +optional
|
||||||
Redirect redirect.Redirect `json:"redirect,omitempty"`
|
Redirect redirect.Config `json:"redirect,omitempty"`
|
||||||
// Rewrite describes the redirection this location.
|
// Rewrite describes the redirection this location.
|
||||||
// +optional
|
// +optional
|
||||||
Rewrite rewrite.Redirect `json:"rewrite,omitempty"`
|
Rewrite rewrite.Config `json:"rewrite,omitempty"`
|
||||||
// Whitelist indicates only connections from certain client
|
// Whitelist indicates only connections from certain client
|
||||||
// addresses or networks are allowed.
|
// addresses or networks are allowed.
|
||||||
// +optional
|
// +optional
|
||||||
|
@ -240,7 +241,7 @@ type Location struct {
|
||||||
// Proxy contains information about timeouts and buffer sizes
|
// Proxy contains information about timeouts and buffer sizes
|
||||||
// to be used in connections against endpoints
|
// to be used in connections against endpoints
|
||||||
// +optional
|
// +optional
|
||||||
Proxy proxy.Configuration `json:"proxy,omitempty"`
|
Proxy proxy.Config `json:"proxy,omitempty"`
|
||||||
// UsePortInRedirects indicates if redirects must specify the port
|
// UsePortInRedirects indicates if redirects must specify the port
|
||||||
// +optional
|
// +optional
|
||||||
UsePortInRedirects bool `json:"usePortInRedirects"`
|
UsePortInRedirects bool `json:"usePortInRedirects"`
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue