support watch namespaces matched namespace selector (#7472)
skip caching namespaces at cluster scope if only watching single namespace add --watch-namespace-selector in user guide add e2e test
This commit is contained in:
parent
67e13bf692
commit
7203a0b8bd
26 changed files with 461 additions and 19 deletions
|
@ -18,6 +18,9 @@
|
||||||
{{- if .Values.controller.scope.enabled }}
|
{{- if .Values.controller.scope.enabled }}
|
||||||
- --watch-namespace={{ default "$(POD_NAMESPACE)" .Values.controller.scope.namespace }}
|
- --watch-namespace={{ default "$(POD_NAMESPACE)" .Values.controller.scope.namespace }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if and (not .Values.controller.scope.enabled) .Values.controller.scope.namespaceSelector }}
|
||||||
|
- --watch-namespace-selector={{ default "" .Values.controller.scope.namespaceSelector }}
|
||||||
|
{{- end }}
|
||||||
{{- if and .Values.controller.reportNodeInternalIp .Values.controller.hostNetwork }}
|
{{- if and .Values.controller.reportNodeInternalIp .Values.controller.hostNetwork }}
|
||||||
- --report-node-internal-ip-address={{ .Values.controller.reportNodeInternalIp }}
|
- --report-node-internal-ip-address={{ .Values.controller.reportNodeInternalIp }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
@ -20,6 +20,9 @@ rules:
|
||||||
- nodes
|
- nodes
|
||||||
- pods
|
- pods
|
||||||
- secrets
|
- secrets
|
||||||
|
{{- if not .Values.controller.scope.enabled }}
|
||||||
|
- namespaces
|
||||||
|
{{- end}}
|
||||||
verbs:
|
verbs:
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
|
|
@ -137,6 +137,9 @@ controller:
|
||||||
scope:
|
scope:
|
||||||
enabled: false
|
enabled: false
|
||||||
namespace: "" # defaults to $(POD_NAMESPACE)
|
namespace: "" # defaults to $(POD_NAMESPACE)
|
||||||
|
# When scope.enabled == false, instead of watching all namespaces, we watching namespaces whose labels
|
||||||
|
# only match with namespaceSelector. Format like foo=bar. Defaults to empty, means watching all namespaces.
|
||||||
|
namespaceSelector: ""
|
||||||
|
|
||||||
## Allows customization of the configmap / nginx-configmap namespace
|
## Allows customization of the configmap / nginx-configmap namespace
|
||||||
##
|
##
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||||
"k8s.io/ingress-nginx/internal/ingress/controller"
|
"k8s.io/ingress-nginx/internal/ingress/controller"
|
||||||
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||||
|
@ -100,6 +101,9 @@ either be a port name or number.`)
|
||||||
This includes Ingresses, Services and all configuration resources. All
|
This includes Ingresses, Services and all configuration resources. All
|
||||||
namespaces are watched if this parameter is left empty.`)
|
namespaces are watched if this parameter is left empty.`)
|
||||||
|
|
||||||
|
watchNamespaceSelector = flags.String("watch-namespace-selector", "",
|
||||||
|
`Selector selects namespaces the controller watches for updates to Kubernetes objects.`)
|
||||||
|
|
||||||
profiling = flags.Bool("profiling", true,
|
profiling = flags.Bool("profiling", true,
|
||||||
`Enable profiling via web interface host:port/debug/pprof/`)
|
`Enable profiling via web interface host:port/debug/pprof/`)
|
||||||
|
|
||||||
|
@ -266,6 +270,19 @@ https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-g
|
||||||
nginx.HealthCheckTimeout = time.Duration(*defHealthCheckTimeout) * time.Second
|
nginx.HealthCheckTimeout = time.Duration(*defHealthCheckTimeout) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(*watchNamespace) != 0 && len(*watchNamespaceSelector) != 0 {
|
||||||
|
return false, nil, fmt.Errorf("flags --watch-namespace and --watch-namespace-selector are mutually exclusive")
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespaceSelector labels.Selector
|
||||||
|
if len(*watchNamespaceSelector) != 0 {
|
||||||
|
var err error
|
||||||
|
namespaceSelector, err = labels.Parse(*watchNamespaceSelector)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, fmt.Errorf("failed to parse --watch-namespace-selector=%s, error: %v", *watchNamespaceSelector, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngx_config.EnableSSLChainCompletion = *enableSSLChainCompletion
|
ngx_config.EnableSSLChainCompletion = *enableSSLChainCompletion
|
||||||
|
|
||||||
config := &controller.Configuration{
|
config := &controller.Configuration{
|
||||||
|
@ -282,6 +299,7 @@ https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-g
|
||||||
ResyncPeriod: *resyncPeriod,
|
ResyncPeriod: *resyncPeriod,
|
||||||
DefaultService: *defaultSvc,
|
DefaultService: *defaultSvc,
|
||||||
Namespace: *watchNamespace,
|
Namespace: *watchNamespace,
|
||||||
|
WatchNamespaceSelector: namespaceSelector,
|
||||||
ConfigMapName: *configMap,
|
ConfigMapName: *configMap,
|
||||||
TCPConfigMapName: *tcpConfigMapName,
|
TCPConfigMapName: *tcpConfigMapName,
|
||||||
UDPConfigMapName: *udpConfigMapName,
|
UDPConfigMapName: *udpConfigMapName,
|
||||||
|
|
|
@ -59,6 +59,7 @@ rules:
|
||||||
- nodes
|
- nodes
|
||||||
- pods
|
- pods
|
||||||
- secrets
|
- secrets
|
||||||
|
- namespaces
|
||||||
verbs:
|
verbs:
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
|
|
@ -59,6 +59,7 @@ rules:
|
||||||
- nodes
|
- nodes
|
||||||
- pods
|
- pods
|
||||||
- secrets
|
- secrets
|
||||||
|
- namespaces
|
||||||
verbs:
|
verbs:
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
|
|
@ -59,6 +59,7 @@ rules:
|
||||||
- nodes
|
- nodes
|
||||||
- pods
|
- pods
|
||||||
- secrets
|
- secrets
|
||||||
|
- namespaces
|
||||||
verbs:
|
verbs:
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
|
|
@ -60,6 +60,7 @@ rules:
|
||||||
- nodes
|
- nodes
|
||||||
- pods
|
- pods
|
||||||
- secrets
|
- secrets
|
||||||
|
- namespaces
|
||||||
verbs:
|
verbs:
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
|
|
@ -59,6 +59,7 @@ rules:
|
||||||
- nodes
|
- nodes
|
||||||
- pods
|
- pods
|
||||||
- secrets
|
- secrets
|
||||||
|
- namespaces
|
||||||
verbs:
|
verbs:
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
|
|
@ -59,6 +59,7 @@ rules:
|
||||||
- nodes
|
- nodes
|
||||||
- pods
|
- pods
|
||||||
- secrets
|
- secrets
|
||||||
|
- namespaces
|
||||||
verbs:
|
verbs:
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
|
|
@ -60,6 +60,7 @@ rules:
|
||||||
- nodes
|
- nodes
|
||||||
- pods
|
- pods
|
||||||
- secrets
|
- secrets
|
||||||
|
- namespaces
|
||||||
verbs:
|
verbs:
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
|
|
@ -65,3 +65,4 @@ They are set in the container spec of the `nginx-ingress-controller` Deployment
|
||||||
| `--version` | Show release information about the NGINX Ingress controller and exit. |
|
| `--version` | Show release information about the NGINX Ingress controller and exit. |
|
||||||
| `--vmodule` | comma-separated list of pattern=N settings for file-filtered logging |
|
| `--vmodule` | comma-separated list of pattern=N settings for file-filtered logging |
|
||||||
| `--watch-namespace` | Namespace the controller watches for updates to Kubernetes objects. This includes Ingresses, Services and all configuration resources. All namespaces are watched if this parameter is left empty. |
|
| `--watch-namespace` | Namespace the controller watches for updates to Kubernetes objects. This includes Ingresses, Services and all configuration resources. All namespaces are watched if this parameter is left empty. |
|
||||||
|
| `--watch-namespace-selector` | The controller will watch namespaces whose labels match the given selector. This flag only takes effective when `--watch-namespace` is empty. |
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -63,6 +63,7 @@ require (
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||||
|
github.com/client9/misspell v0.3.4 // indirect
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2 // indirect
|
github.com/cyphar/filepath-securejoin v0.2.2 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
|
1
go.sum
1
go.sum
|
@ -120,6 +120,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||||
|
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
networking "k8s.io/api/networking/v1"
|
networking "k8s.io/api/networking/v1"
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
@ -67,6 +68,8 @@ type Configuration struct {
|
||||||
|
|
||||||
Namespace string
|
Namespace string
|
||||||
|
|
||||||
|
WatchNamespaceSelector labels.Selector
|
||||||
|
|
||||||
// +optional
|
// +optional
|
||||||
TCPConfigMapName string
|
TCPConfigMapName string
|
||||||
// +optional
|
// +optional
|
||||||
|
|
|
@ -36,6 +36,7 @@ import (
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
networking "k8s.io/api/networking/v1"
|
networking "k8s.io/api/networking/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
|
|
||||||
"k8s.io/ingress-nginx/internal/file"
|
"k8s.io/ingress-nginx/internal/file"
|
||||||
|
@ -2378,6 +2379,7 @@ func newNGINXController(t *testing.T) *NGINXController {
|
||||||
|
|
||||||
storer := store.New(
|
storer := store.New(
|
||||||
ns,
|
ns,
|
||||||
|
labels.Nothing(),
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -2441,6 +2443,7 @@ func newDynamicNginxController(t *testing.T, setConfigMap func(string) *v1.Confi
|
||||||
|
|
||||||
storer := store.New(
|
storer := store.New(
|
||||||
ns,
|
ns,
|
||||||
|
labels.Nothing(),
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
|
|
@ -122,6 +122,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro
|
||||||
|
|
||||||
n.store = store.New(
|
n.store = store.New(
|
||||||
config.Namespace,
|
config.Namespace,
|
||||||
|
config.WatchNamespaceSelector,
|
||||||
config.ConfigMapName,
|
config.ConfigMapName,
|
||||||
config.TCPConfigMapName,
|
config.TCPConfigMapName,
|
||||||
config.UDPConfigMapName,
|
config.UDPConfigMapName,
|
||||||
|
|
39
internal/ingress/controller/store/namespace.go
Normal file
39
internal/ingress/controller/store/namespace.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NamespaceLister makes a Store that lists Namespaces.
|
||||||
|
type NamespaceLister struct {
|
||||||
|
cache.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByKey returns the Namespace matching key in the local Namespace Store.
|
||||||
|
func (cml *NamespaceLister) ByKey(key string) (*apiv1.Namespace, error) {
|
||||||
|
s, exists, err := cml.GetByKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return nil, NotExistsError(key)
|
||||||
|
}
|
||||||
|
return s.(*apiv1.Namespace), nil
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ import (
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
@ -127,6 +128,7 @@ type Informer struct {
|
||||||
Service cache.SharedIndexInformer
|
Service cache.SharedIndexInformer
|
||||||
Secret cache.SharedIndexInformer
|
Secret cache.SharedIndexInformer
|
||||||
ConfigMap cache.SharedIndexInformer
|
ConfigMap cache.SharedIndexInformer
|
||||||
|
Namespace cache.SharedIndexInformer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lister contains object listers (stores).
|
// Lister contains object listers (stores).
|
||||||
|
@ -137,6 +139,7 @@ type Lister struct {
|
||||||
Endpoint EndpointLister
|
Endpoint EndpointLister
|
||||||
Secret SecretLister
|
Secret SecretLister
|
||||||
ConfigMap ConfigMapLister
|
ConfigMap ConfigMapLister
|
||||||
|
Namespace NamespaceLister
|
||||||
IngressWithAnnotation IngressWithAnnotationsLister
|
IngressWithAnnotation IngressWithAnnotationsLister
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +175,15 @@ func (i *Informer) Run(stopCh chan struct{}) {
|
||||||
runtime.HandleError(fmt.Errorf("timed out waiting for ingress classcaches to sync"))
|
runtime.HandleError(fmt.Errorf("timed out waiting for ingress classcaches to sync"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when limit controller scope to one namespace, skip sync namespaces at cluster scope
|
||||||
|
if i.Namespace != nil {
|
||||||
|
go i.Namespace.Run(stopCh)
|
||||||
|
|
||||||
|
if !cache.WaitForCacheSync(stopCh, i.Namespace.HasSynced) {
|
||||||
|
runtime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// in big clusters, deltas can keep arriving even after HasSynced
|
// in big clusters, deltas can keep arriving even after HasSynced
|
||||||
// functions have returned 'true'
|
// functions have returned 'true'
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
@ -225,7 +237,9 @@ type k8sStore struct {
|
||||||
|
|
||||||
// New creates a new object store to be used in the ingress controller
|
// New creates a new object store to be used in the ingress controller
|
||||||
func New(
|
func New(
|
||||||
namespace, configmap, tcp, udp, defaultSSLCertificate string,
|
namespace string,
|
||||||
|
namespaceSelector labels.Selector,
|
||||||
|
configmap, tcp, udp, defaultSSLCertificate string,
|
||||||
resyncPeriod time.Duration,
|
resyncPeriod time.Duration,
|
||||||
client clientset.Interface,
|
client clientset.Interface,
|
||||||
updateCh *channels.RingChannel,
|
updateCh *channels.RingChannel,
|
||||||
|
@ -322,6 +336,35 @@ func New(
|
||||||
store.informers.Service = infFactory.Core().V1().Services().Informer()
|
store.informers.Service = infFactory.Core().V1().Services().Informer()
|
||||||
store.listers.Service.Store = store.informers.Service.GetStore()
|
store.listers.Service.Store = store.informers.Service.GetStore()
|
||||||
|
|
||||||
|
// avoid caching namespaces at cluster scope when watching single namespace
|
||||||
|
if namespaceSelector != nil && !namespaceSelector.Empty() {
|
||||||
|
// cache informers factory for namespaces
|
||||||
|
infFactoryNamespaces := informers.NewSharedInformerFactoryWithOptions(client, resyncPeriod,
|
||||||
|
informers.WithTweakListOptions(labelsTweakListOptionsFunc),
|
||||||
|
)
|
||||||
|
|
||||||
|
store.informers.Namespace = infFactoryNamespaces.Core().V1().Namespaces().Informer()
|
||||||
|
store.listers.Namespace.Store = store.informers.Namespace.GetStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
watchedNamespace := func(namespace string) bool {
|
||||||
|
if namespaceSelector == nil || namespaceSelector.Empty() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
item, ok, err := store.listers.Namespace.GetByKey(namespace)
|
||||||
|
if !ok {
|
||||||
|
klog.Errorf("Namespace %s not existed: %v.", namespace, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ns, ok := item.(*corev1.Namespace)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return namespaceSelector.Matches(labels.Set(ns.Labels))
|
||||||
|
}
|
||||||
|
|
||||||
ingDeleteHandler := func(obj interface{}) {
|
ingDeleteHandler := func(obj interface{}) {
|
||||||
ing, ok := toIngress(obj)
|
ing, ok := toIngress(obj)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -338,6 +381,10 @@ func New(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !watchedNamespace(ing.Namespace) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_, err := store.GetIngressClass(ing, icConfig)
|
_, err := store.GetIngressClass(ing, icConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.InfoS("Ignoring ingress because of error while validating ingress class", "ingress", klog.KObj(ing), "error", err)
|
klog.InfoS("Ignoring ingress because of error while validating ingress class", "ingress", klog.KObj(ing), "error", err)
|
||||||
|
@ -363,6 +410,11 @@ func New(
|
||||||
ingEventHandler := cache.ResourceEventHandlerFuncs{
|
ingEventHandler := cache.ResourceEventHandlerFuncs{
|
||||||
AddFunc: func(obj interface{}) {
|
AddFunc: func(obj interface{}) {
|
||||||
ing, _ := toIngress(obj)
|
ing, _ := toIngress(obj)
|
||||||
|
|
||||||
|
if !watchedNamespace(ing.Namespace) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ic, err := store.GetIngressClass(ing, icConfig)
|
ic, err := store.GetIngressClass(ing, icConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.InfoS("Ignoring ingress because of error while validating ingress class", "ingress", klog.KObj(ing), "error", err)
|
klog.InfoS("Ignoring ingress because of error while validating ingress class", "ingress", klog.KObj(ing), "error", err)
|
||||||
|
@ -392,6 +444,10 @@ func New(
|
||||||
oldIng, _ := toIngress(old)
|
oldIng, _ := toIngress(old)
|
||||||
curIng, _ := toIngress(cur)
|
curIng, _ := toIngress(cur)
|
||||||
|
|
||||||
|
if !watchedNamespace(oldIng.Namespace) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var errOld, errCur error
|
var errOld, errCur error
|
||||||
var classCur string
|
var classCur string
|
||||||
if !icConfig.IgnoreIngressClass {
|
if !icConfig.IgnoreIngressClass {
|
||||||
|
@ -528,6 +584,10 @@ func New(
|
||||||
sec := cur.(*corev1.Secret)
|
sec := cur.(*corev1.Secret)
|
||||||
key := k8s.MetaNamespaceKey(sec)
|
key := k8s.MetaNamespaceKey(sec)
|
||||||
|
|
||||||
|
if !watchedNamespace(sec.Namespace) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if store.defaultSSLCertificate == key {
|
if store.defaultSSLCertificate == key {
|
||||||
store.syncSecret(store.defaultSSLCertificate)
|
store.syncSecret(store.defaultSSLCertificate)
|
||||||
}
|
}
|
||||||
|
@ -566,6 +626,10 @@ func New(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !watchedNamespace(sec.Namespace) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
store.sslStore.Delete(k8s.MetaNamespaceKey(sec))
|
store.sslStore.Delete(k8s.MetaNamespaceKey(sec))
|
||||||
|
|
||||||
key := k8s.MetaNamespaceKey(sec)
|
key := k8s.MetaNamespaceKey(sec)
|
||||||
|
|
|
@ -31,6 +31,7 @@ import (
|
||||||
networking "k8s.io/api/networking/v1"
|
networking "k8s.io/api/networking/v1"
|
||||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||||
|
@ -89,6 +90,8 @@ func TestStore(t *testing.T) {
|
||||||
t.Fatalf("error: %v", err)
|
t.Fatalf("error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emptySelector, _ := labels.Parse("")
|
||||||
|
|
||||||
defer te.Stop()
|
defer te.Stop()
|
||||||
|
|
||||||
clientSet, err := kubernetes.NewForConfig(cfg)
|
clientSet, err := kubernetes.NewForConfig(cfg)
|
||||||
|
@ -112,6 +115,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -191,6 +195,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -293,6 +298,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -407,6 +413,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -535,6 +542,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -633,6 +641,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -725,6 +734,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -809,6 +819,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -903,6 +914,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -1025,6 +1037,7 @@ func TestStore(t *testing.T) {
|
||||||
|
|
||||||
storer := New(
|
storer := New(
|
||||||
ns,
|
ns,
|
||||||
|
emptySelector,
|
||||||
fmt.Sprintf("%v/config", ns),
|
fmt.Sprintf("%v/config", ns),
|
||||||
fmt.Sprintf("%v/tcp", ns),
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
fmt.Sprintf("%v/udp", ns),
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
@ -1107,6 +1120,102 @@ func TestStore(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("should not receive events whose namespace doesn't match watch namespace selector", func(t *testing.T) {
|
||||||
|
ns := createNamespace(clientSet, t)
|
||||||
|
defer deleteNamespace(ns, clientSet, t)
|
||||||
|
createConfigMap(clientSet, ns, t)
|
||||||
|
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
updateCh := channels.NewRingChannel(1024)
|
||||||
|
|
||||||
|
var add uint64
|
||||||
|
var upd uint64
|
||||||
|
var del uint64
|
||||||
|
|
||||||
|
go func(ch *channels.RingChannel) {
|
||||||
|
for {
|
||||||
|
evt, ok := <-ch.Out()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e := evt.(Event)
|
||||||
|
if e.Obj == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch e.Type {
|
||||||
|
case CreateEvent:
|
||||||
|
atomic.AddUint64(&add, 1)
|
||||||
|
case UpdateEvent:
|
||||||
|
atomic.AddUint64(&upd, 1)
|
||||||
|
case DeleteEvent:
|
||||||
|
atomic.AddUint64(&del, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(updateCh)
|
||||||
|
|
||||||
|
namesapceSelector, _ := labels.Parse("foo=bar")
|
||||||
|
storer := New(
|
||||||
|
ns,
|
||||||
|
namesapceSelector,
|
||||||
|
fmt.Sprintf("%v/config", ns),
|
||||||
|
fmt.Sprintf("%v/tcp", ns),
|
||||||
|
fmt.Sprintf("%v/udp", ns),
|
||||||
|
"",
|
||||||
|
10*time.Minute,
|
||||||
|
clientSet,
|
||||||
|
updateCh,
|
||||||
|
false,
|
||||||
|
DefaultClassConfig)
|
||||||
|
|
||||||
|
storer.Run(stopCh)
|
||||||
|
|
||||||
|
ing := ensureIngress(&networking.Ingress{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "dummy",
|
||||||
|
Namespace: ns,
|
||||||
|
},
|
||||||
|
Spec: networking.IngressSpec{
|
||||||
|
Rules: []networking.IngressRule{
|
||||||
|
{
|
||||||
|
Host: "dummy",
|
||||||
|
IngressRuleValue: networking.IngressRuleValue{
|
||||||
|
HTTP: &networking.HTTPIngressRuleValue{
|
||||||
|
Paths: []networking.HTTPIngressPath{
|
||||||
|
{
|
||||||
|
Path: "/",
|
||||||
|
PathType: &pathPrefix,
|
||||||
|
Backend: networking.IngressBackend{
|
||||||
|
Service: &networking.IngressServiceBackend{
|
||||||
|
Name: "http-svc",
|
||||||
|
Port: networking.ServiceBackendPort{
|
||||||
|
Number: 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, clientSet, t)
|
||||||
|
defer deleteIngress(ing, clientSet, t)
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
if atomic.LoadUint64(&add) != 0 {
|
||||||
|
t.Errorf("expected 0 events of type Create but %v occurred", add)
|
||||||
|
}
|
||||||
|
if atomic.LoadUint64(&upd) != 0 {
|
||||||
|
t.Errorf("expected 0 events of type Update but %v occurred", upd)
|
||||||
|
}
|
||||||
|
if atomic.LoadUint64(&del) != 0 {
|
||||||
|
t.Errorf("expected 0 events of type Delete but %v occurred", del)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
// test add ingress with secret it doesn't exists and then add secret
|
// test add ingress with secret it doesn't exists and then add secret
|
||||||
// check secret is generated on fs
|
// check secret is generated on fs
|
||||||
// check ocsp
|
// check ocsp
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# TODO: remove the need to use fullnameOverride
|
||||||
|
fullnameOverride: nginx-ingress
|
||||||
|
controller:
|
||||||
|
image:
|
||||||
|
repository: ingress-controller/controller
|
||||||
|
tag: 1.0.0-dev
|
||||||
|
digest:
|
||||||
|
containerPort:
|
||||||
|
http: "1080"
|
||||||
|
https: "1443"
|
||||||
|
|
||||||
|
extraArgs:
|
||||||
|
http-port: "1080"
|
||||||
|
https-port: "1443"
|
||||||
|
# e2e tests do not require information about ingress status
|
||||||
|
update-status: "false"
|
||||||
|
ingressClassResource:
|
||||||
|
# We will create and remove each IC/ClusterRole/ClusterRoleBinding per test so there's no conflict
|
||||||
|
enabled: false
|
||||||
|
scope:
|
||||||
|
enabled: false
|
||||||
|
namespaceSelector: "foo=bar"
|
||||||
|
|
||||||
|
config:
|
||||||
|
worker-processes: "1"
|
||||||
|
service:
|
||||||
|
type: NodePort
|
||||||
|
admissionWebhooks:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
defaultBackend:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
rbac:
|
||||||
|
create: true
|
||||||
|
scope: false
|
|
@ -55,7 +55,15 @@ func (f *Framework) NewEchoDeploymentWithReplicas(replicas int) {
|
||||||
// replicas is configurable and
|
// replicas is configurable and
|
||||||
// name is configurable
|
// name is configurable
|
||||||
func (f *Framework) NewEchoDeploymentWithNameAndReplicas(name string, replicas int) {
|
func (f *Framework) NewEchoDeploymentWithNameAndReplicas(name string, replicas int) {
|
||||||
deployment := newDeployment(name, f.Namespace, "k8s.gcr.io/ingress-nginx/e2e-test-echo@sha256:131ece0637b29231470cfaa04690c2966a2e0b147d3c9df080a0857b78982410", 80, int32(replicas),
|
f.newEchoDeployment(f.Namespace, name, replicas)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framework) NewEchoDeploymentWithNamespaceAndReplicas(namespace string, replicas int) {
|
||||||
|
f.newEchoDeployment(namespace, EchoService, replicas)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framework) newEchoDeployment(namespace, name string, replicas int) {
|
||||||
|
deployment := newDeployment(name, namespace, "k8s.gcr.io/ingress-nginx/e2e-test-echo@sha256:131ece0637b29231470cfaa04690c2966a2e0b147d3c9df080a0857b78982410", 80, int32(replicas),
|
||||||
nil,
|
nil,
|
||||||
[]corev1.VolumeMount{},
|
[]corev1.VolumeMount{},
|
||||||
[]corev1.Volume{},
|
[]corev1.Volume{},
|
||||||
|
@ -66,7 +74,7 @@ func (f *Framework) NewEchoDeploymentWithNameAndReplicas(name string, replicas i
|
||||||
service := &corev1.Service{
|
service := &corev1.Service{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: f.Namespace,
|
Namespace: namespace,
|
||||||
},
|
},
|
||||||
Spec: corev1.ServiceSpec{
|
Spec: corev1.ServiceSpec{
|
||||||
Ports: []corev1.ServicePort{
|
Ports: []corev1.ServicePort{
|
||||||
|
@ -85,7 +93,7 @@ func (f *Framework) NewEchoDeploymentWithNameAndReplicas(name string, replicas i
|
||||||
|
|
||||||
f.EnsureService(service)
|
f.EnsureService(service)
|
||||||
|
|
||||||
err := WaitForEndpoints(f.KubeClientSet, DefaultTimeout, name, f.Namespace, replicas)
|
err := WaitForEndpoints(f.KubeClientSet, DefaultTimeout, name, namespace, replicas)
|
||||||
assert.Nil(ginkgo.GinkgoT(), err, "waiting for endpoints to become ready")
|
assert.Nil(ginkgo.GinkgoT(), err, "waiting for endpoints to become ready")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ func (f *Framework) AfterEach() {
|
||||||
defer func(kubeClient kubernetes.Interface, ns string) {
|
defer func(kubeClient kubernetes.Interface, ns string) {
|
||||||
go func() {
|
go func() {
|
||||||
defer ginkgo.GinkgoRecover()
|
defer ginkgo.GinkgoRecover()
|
||||||
err := deleteKubeNamespace(kubeClient, ns)
|
err := DeleteKubeNamespace(kubeClient, ns)
|
||||||
assert.Nil(ginkgo.GinkgoT(), err, "deleting namespace %v", f.Namespace)
|
assert.Nil(ginkgo.GinkgoT(), err, "deleting namespace %v", f.Namespace)
|
||||||
}()
|
}()
|
||||||
}(f.KubeClientSet, f.Namespace)
|
}(f.KubeClientSet, f.Namespace)
|
||||||
|
@ -588,6 +588,12 @@ func NewSingleIngress(name, path, host, ns, service string, port int, annotation
|
||||||
return newSingleIngressWithRules(name, path, host, ns, service, port, annotations, nil)
|
return newSingleIngressWithRules(name, path, host, ns, service, port, annotations, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSingleIngressWithIngressClass(name, path, host, ns, service, ingressClass string, port int, annotations map[string]string) *networking.Ingress {
|
||||||
|
ing := newSingleIngressWithRules(name, path, host, ns, service, port, annotations, nil)
|
||||||
|
ing.Spec.IngressClassName = &ingressClass
|
||||||
|
return ing
|
||||||
|
}
|
||||||
|
|
||||||
// NewSingleIngressWithMultiplePaths creates a simple ingress rule with multiple paths
|
// NewSingleIngressWithMultiplePaths creates a simple ingress rule with multiple paths
|
||||||
func NewSingleIngressWithMultiplePaths(name string, paths []string, host, ns, service string, port int, annotations map[string]string) *networking.Ingress {
|
func NewSingleIngressWithMultiplePaths(name string, paths []string, host, ns, service string, port int, annotations map[string]string) *networking.Ingress {
|
||||||
pathtype := networking.PathTypePrefix
|
pathtype := networking.PathTypePrefix
|
||||||
|
|
|
@ -38,7 +38,7 @@ import (
|
||||||
|
|
||||||
// EnsureSecret creates a Secret object or returns it if it already exists.
|
// EnsureSecret creates a Secret object or returns it if it already exists.
|
||||||
func (f *Framework) EnsureSecret(secret *api.Secret) *api.Secret {
|
func (f *Framework) EnsureSecret(secret *api.Secret) *api.Secret {
|
||||||
err := createSecretWithRetries(f.KubeClientSet, f.Namespace, secret)
|
err := createSecretWithRetries(f.KubeClientSet, secret.Namespace, secret)
|
||||||
assert.Nil(ginkgo.GinkgoT(), err, "creating secret")
|
assert.Nil(ginkgo.GinkgoT(), err, "creating secret")
|
||||||
|
|
||||||
s, err := f.KubeClientSet.CoreV1().Secrets(secret.Namespace).Get(context.TODO(), secret.Name, metav1.GetOptions{})
|
s, err := f.KubeClientSet.CoreV1().Secrets(secret.Namespace).Get(context.TODO(), secret.Name, metav1.GetOptions{})
|
||||||
|
@ -50,10 +50,10 @@ func (f *Framework) EnsureSecret(secret *api.Secret) *api.Secret {
|
||||||
|
|
||||||
// EnsureConfigMap creates a ConfigMap object or returns it if it already exists.
|
// EnsureConfigMap creates a ConfigMap object or returns it if it already exists.
|
||||||
func (f *Framework) EnsureConfigMap(configMap *api.ConfigMap) (*api.ConfigMap, error) {
|
func (f *Framework) EnsureConfigMap(configMap *api.ConfigMap) (*api.ConfigMap, error) {
|
||||||
cm, err := f.KubeClientSet.CoreV1().ConfigMaps(f.Namespace).Create(context.TODO(), configMap, metav1.CreateOptions{})
|
cm, err := f.KubeClientSet.CoreV1().ConfigMaps(configMap.Namespace).Create(context.TODO(), configMap, metav1.CreateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if k8sErrors.IsAlreadyExists(err) {
|
if k8sErrors.IsAlreadyExists(err) {
|
||||||
return f.KubeClientSet.CoreV1().ConfigMaps(f.Namespace).Update(context.TODO(), configMap, metav1.UpdateOptions{})
|
return f.KubeClientSet.CoreV1().ConfigMaps(configMap.Namespace).Update(context.TODO(), configMap, metav1.UpdateOptions{})
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -72,13 +72,13 @@ func (f *Framework) GetIngress(namespace string, name string) *networking.Ingres
|
||||||
// EnsureIngress creates an Ingress object and returns it, throws error if it already exists.
|
// EnsureIngress creates an Ingress object and returns it, throws error if it already exists.
|
||||||
func (f *Framework) EnsureIngress(ingress *networking.Ingress) *networking.Ingress {
|
func (f *Framework) EnsureIngress(ingress *networking.Ingress) *networking.Ingress {
|
||||||
fn := func() {
|
fn := func() {
|
||||||
err := createIngressWithRetries(f.KubeClientSet, f.Namespace, ingress)
|
err := createIngressWithRetries(f.KubeClientSet, ingress.Namespace, ingress)
|
||||||
assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
|
assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
|
||||||
}
|
}
|
||||||
|
|
||||||
f.WaitForReload(fn)
|
f.WaitForReload(fn)
|
||||||
|
|
||||||
ing := f.GetIngress(f.Namespace, ingress.Name)
|
ing := f.GetIngress(ingress.Namespace, ingress.Name)
|
||||||
if ing.Annotations == nil {
|
if ing.Annotations == nil {
|
||||||
ing.Annotations = make(map[string]string)
|
ing.Annotations = make(map[string]string)
|
||||||
}
|
}
|
||||||
|
@ -88,10 +88,10 @@ func (f *Framework) EnsureIngress(ingress *networking.Ingress) *networking.Ingre
|
||||||
|
|
||||||
// UpdateIngress updates an Ingress object and returns the updated object.
|
// UpdateIngress updates an Ingress object and returns the updated object.
|
||||||
func (f *Framework) UpdateIngress(ingress *networking.Ingress) *networking.Ingress {
|
func (f *Framework) UpdateIngress(ingress *networking.Ingress) *networking.Ingress {
|
||||||
err := updateIngressWithRetries(f.KubeClientSet, f.Namespace, ingress)
|
err := updateIngressWithRetries(f.KubeClientSet, ingress.Namespace, ingress)
|
||||||
assert.Nil(ginkgo.GinkgoT(), err, "updating ingress")
|
assert.Nil(ginkgo.GinkgoT(), err, "updating ingress")
|
||||||
|
|
||||||
ing := f.GetIngress(f.Namespace, ingress.Name)
|
ing := f.GetIngress(ingress.Namespace, ingress.Name)
|
||||||
if ing.Annotations == nil {
|
if ing.Annotations == nil {
|
||||||
ing.Annotations = make(map[string]string)
|
ing.Annotations = make(map[string]string)
|
||||||
}
|
}
|
||||||
|
@ -113,15 +113,15 @@ func (f *Framework) GetService(namespace string, name string) *core.Service {
|
||||||
|
|
||||||
// EnsureService creates a Service object and returns it, throws error if it already exists.
|
// EnsureService creates a Service object and returns it, throws error if it already exists.
|
||||||
func (f *Framework) EnsureService(service *core.Service) *core.Service {
|
func (f *Framework) EnsureService(service *core.Service) *core.Service {
|
||||||
err := createServiceWithRetries(f.KubeClientSet, f.Namespace, service)
|
err := createServiceWithRetries(f.KubeClientSet, service.Namespace, service)
|
||||||
assert.Nil(ginkgo.GinkgoT(), err, "creating service")
|
assert.Nil(ginkgo.GinkgoT(), err, "creating service")
|
||||||
|
|
||||||
return f.GetService(f.Namespace, service.Name)
|
return f.GetService(service.Namespace, service.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureDeployment creates a Deployment object and returns it, throws error if it already exists.
|
// EnsureDeployment creates a Deployment object and returns it, throws error if it already exists.
|
||||||
func (f *Framework) EnsureDeployment(deployment *appsv1.Deployment) *appsv1.Deployment {
|
func (f *Framework) EnsureDeployment(deployment *appsv1.Deployment) *appsv1.Deployment {
|
||||||
err := createDeploymentWithRetries(f.KubeClientSet, f.Namespace, deployment)
|
err := createDeploymentWithRetries(f.KubeClientSet, deployment.Namespace, deployment)
|
||||||
assert.Nil(ginkgo.GinkgoT(), err, "creating deployment")
|
assert.Nil(ginkgo.GinkgoT(), err, "creating deployment")
|
||||||
|
|
||||||
d, err := f.KubeClientSet.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{})
|
d, err := f.KubeClientSet.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{})
|
||||||
|
|
|
@ -85,14 +85,15 @@ func RestclientConfig(config, context string) (*api.Config, error) {
|
||||||
// RunID unique identifier of the e2e run
|
// RunID unique identifier of the e2e run
|
||||||
var RunID = uuid.NewUUID()
|
var RunID = uuid.NewUUID()
|
||||||
|
|
||||||
// CreateKubeNamespace creates a new namespace in the cluster
|
func createNamespace(baseName string, labels map[string]string, c kubernetes.Interface) (string, error) {
|
||||||
func CreateKubeNamespace(baseName string, c kubernetes.Interface) (string, error) {
|
|
||||||
ts := time.Now().UnixNano()
|
ts := time.Now().UnixNano()
|
||||||
ns := &corev1.Namespace{
|
ns := &corev1.Namespace{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
GenerateName: fmt.Sprintf("e2e-tests-%v-%v-", baseName, ts),
|
GenerateName: fmt.Sprintf("e2e-tests-%v-%v-", baseName, ts),
|
||||||
|
Labels: labels,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Be robust about making the namespace creation call.
|
// Be robust about making the namespace creation call.
|
||||||
var got *corev1.Namespace
|
var got *corev1.Namespace
|
||||||
var err error
|
var err error
|
||||||
|
@ -111,8 +112,20 @@ func CreateKubeNamespace(baseName string, c kubernetes.Interface) (string, error
|
||||||
return got.Name, nil
|
return got.Name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteKubeNamespace deletes a namespace and all the objects inside
|
// CreateKubeNamespace creates a new namespace in the cluster
|
||||||
func deleteKubeNamespace(c kubernetes.Interface, namespace string) error {
|
func CreateKubeNamespace(baseName string, c kubernetes.Interface) (string, error) {
|
||||||
|
|
||||||
|
return createNamespace(baseName, nil, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateKubeNamespaceWithLabel creates a new namespace with given labels in the cluster
|
||||||
|
func CreateKubeNamespaceWithLabel(baseName string, labels map[string]string, c kubernetes.Interface) (string, error) {
|
||||||
|
|
||||||
|
return createNamespace(baseName, labels, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteKubeNamespace deletes a namespace and all the objects inside
|
||||||
|
func DeleteKubeNamespace(c kubernetes.Interface, namespace string) error {
|
||||||
grace := int64(0)
|
grace := int64(0)
|
||||||
pb := metav1.DeletePropagationBackground
|
pb := metav1.DeletePropagationBackground
|
||||||
return c.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{
|
return c.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{
|
||||||
|
|
123
test/e2e/settings/namespace_selector.go
Normal file
123
test/e2e/settings/namespace_selector.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 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 settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/onsi/ginkgo"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/ingress-nginx/test/e2e/framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = framework.IngressNginxDescribe("[Flag] watch namespace selector", func() {
|
||||||
|
f := framework.NewDefaultFramework("namespace-selector")
|
||||||
|
notMatchedHost, matchedHost := "bar", "foo"
|
||||||
|
var notMatchedNs string
|
||||||
|
var matchedNs string
|
||||||
|
|
||||||
|
// create a test namespace, under which create an ingress and backend deployment
|
||||||
|
prepareTestIngress := func(baseName string, host string, labels map[string]string) string {
|
||||||
|
ns, err := framework.CreateKubeNamespaceWithLabel(f.BaseName, labels, f.KubeClientSet)
|
||||||
|
assert.Nil(ginkgo.GinkgoT(), err, "creating test namespace")
|
||||||
|
f.NewEchoDeploymentWithNamespaceAndReplicas(ns, 1)
|
||||||
|
ing := framework.NewSingleIngressWithIngressClass(host, "/", host, ns, framework.EchoService, f.IngressClass, 80, nil)
|
||||||
|
f.EnsureIngress(ing)
|
||||||
|
return ns
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupNamespace := func(ns string) {
|
||||||
|
err := framework.DeleteKubeNamespace(f.KubeClientSet, ns)
|
||||||
|
assert.Nil(ginkgo.GinkgoT(), err, "deleting temporarily crated namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
notMatchedNs = prepareTestIngress(notMatchedHost, notMatchedHost, nil) // create namespace without label "foo=bar"
|
||||||
|
matchedNs = prepareTestIngress(matchedHost, matchedHost, map[string]string{"foo": "bar"})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.AfterEach(func() {
|
||||||
|
cleanupNamespace(notMatchedNs)
|
||||||
|
cleanupNamespace(matchedNs)
|
||||||
|
|
||||||
|
// cleanup clusterrole/clusterrolebinding created by installing chart with controller.scope.enabled=false
|
||||||
|
err := f.KubeClientSet.RbacV1().ClusterRoles().Delete(context.TODO(), "nginx-ingress", metav1.DeleteOptions{})
|
||||||
|
assert.Nil(ginkgo.GinkgoT(), err, "deleting clusterrole nginx-ingress")
|
||||||
|
|
||||||
|
err = f.KubeClientSet.RbacV1().ClusterRoleBindings().Delete(context.TODO(), "nginx-ingress", metav1.DeleteOptions{})
|
||||||
|
assert.Nil(ginkgo.GinkgoT(), err, "deleting clusterrolebinging nginx-ingress")
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.Context("With specific watch-namespace-selector flags", func() {
|
||||||
|
|
||||||
|
ginkgo.It("should ingore Ingress of namespace without label foo=bar and accept those of namespace with label foo=bar", func() {
|
||||||
|
|
||||||
|
f.WaitForNginxConfiguration(func(cfg string) bool {
|
||||||
|
return !strings.Contains(cfg, "server_name bar") &&
|
||||||
|
strings.Contains(cfg, "server_name foo")
|
||||||
|
})
|
||||||
|
|
||||||
|
f.HTTPTestClient().
|
||||||
|
GET("/").
|
||||||
|
WithHeader("Host", matchedHost).
|
||||||
|
Expect().
|
||||||
|
Status(http.StatusOK)
|
||||||
|
|
||||||
|
f.HTTPTestClient().
|
||||||
|
GET("/").
|
||||||
|
WithHeader("Host", notMatchedHost).
|
||||||
|
Expect().
|
||||||
|
Status(http.StatusNotFound)
|
||||||
|
|
||||||
|
// should accept Ingress when namespace labeled with foo=bar
|
||||||
|
ns, err := f.KubeClientSet.CoreV1().Namespaces().Get(context.TODO(), notMatchedNs, metav1.GetOptions{})
|
||||||
|
assert.Nil(ginkgo.GinkgoT(), err)
|
||||||
|
|
||||||
|
if ns.Labels == nil {
|
||||||
|
ns.Labels = make(map[string]string)
|
||||||
|
}
|
||||||
|
ns.Labels["foo"] = "bar"
|
||||||
|
|
||||||
|
_, err = f.KubeClientSet.CoreV1().Namespaces().Update(context.TODO(), ns, metav1.UpdateOptions{})
|
||||||
|
assert.Nil(ginkgo.GinkgoT(), err, "labeling not matched namespace")
|
||||||
|
|
||||||
|
// update ingress to trigger reconcilation
|
||||||
|
ing, err := f.KubeClientSet.NetworkingV1().Ingresses(notMatchedNs).Get(context.TODO(), notMatchedHost, metav1.GetOptions{})
|
||||||
|
assert.Nil(ginkgo.GinkgoT(), err, "retrieve test ingress")
|
||||||
|
if ing.Labels == nil {
|
||||||
|
ing.Labels = make(map[string]string)
|
||||||
|
}
|
||||||
|
ing.Labels["foo"] = "bar"
|
||||||
|
|
||||||
|
_, err = f.KubeClientSet.NetworkingV1().Ingresses(notMatchedNs).Update(context.TODO(), ing, metav1.UpdateOptions{})
|
||||||
|
assert.Nil(ginkgo.GinkgoT(), err, "updating ingress")
|
||||||
|
|
||||||
|
f.WaitForNginxConfiguration(func(cfg string) bool {
|
||||||
|
return strings.Contains(cfg, "server_name bar")
|
||||||
|
})
|
||||||
|
|
||||||
|
f.HTTPTestClient().
|
||||||
|
GET("/").
|
||||||
|
WithHeader("Host", notMatchedHost).
|
||||||
|
Expect().
|
||||||
|
Status(http.StatusOK)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue