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
|
|
@ -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
|
||||
// name is configurable
|
||||
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,
|
||||
[]corev1.VolumeMount{},
|
||||
[]corev1.Volume{},
|
||||
|
|
@ -66,7 +74,7 @@ func (f *Framework) NewEchoDeploymentWithNameAndReplicas(name string, replicas i
|
|||
service := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: f.Namespace,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
|
|
@ -85,7 +93,7 @@ func (f *Framework) NewEchoDeploymentWithNameAndReplicas(name string, replicas i
|
|||
|
||||
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")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ func (f *Framework) AfterEach() {
|
|||
defer func(kubeClient kubernetes.Interface, ns string) {
|
||||
go func() {
|
||||
defer ginkgo.GinkgoRecover()
|
||||
err := deleteKubeNamespace(kubeClient, ns)
|
||||
err := DeleteKubeNamespace(kubeClient, ns)
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "deleting namespace %v", 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)
|
||||
}
|
||||
|
||||
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
|
||||
func NewSingleIngressWithMultiplePaths(name string, paths []string, host, ns, service string, port int, annotations map[string]string) *networking.Ingress {
|
||||
pathtype := networking.PathTypePrefix
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import (
|
|||
|
||||
// EnsureSecret creates a Secret object or returns it if it already exists.
|
||||
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")
|
||||
|
||||
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.
|
||||
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 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
|
||||
}
|
||||
|
|
@ -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.
|
||||
func (f *Framework) EnsureIngress(ingress *networking.Ingress) *networking.Ingress {
|
||||
fn := func() {
|
||||
err := createIngressWithRetries(f.KubeClientSet, f.Namespace, ingress)
|
||||
err := createIngressWithRetries(f.KubeClientSet, ingress.Namespace, ingress)
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
|
||||
}
|
||||
|
||||
f.WaitForReload(fn)
|
||||
|
||||
ing := f.GetIngress(f.Namespace, ingress.Name)
|
||||
ing := f.GetIngress(ingress.Namespace, ingress.Name)
|
||||
if ing.Annotations == nil {
|
||||
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.
|
||||
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")
|
||||
|
||||
ing := f.GetIngress(f.Namespace, ingress.Name)
|
||||
ing := f.GetIngress(ingress.Namespace, ingress.Name)
|
||||
if ing.Annotations == nil {
|
||||
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.
|
||||
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")
|
||||
|
||||
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.
|
||||
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")
|
||||
|
||||
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
|
||||
var RunID = uuid.NewUUID()
|
||||
|
||||
// CreateKubeNamespace creates a new namespace in the cluster
|
||||
func CreateKubeNamespace(baseName string, c kubernetes.Interface) (string, error) {
|
||||
func createNamespace(baseName string, labels map[string]string, c kubernetes.Interface) (string, error) {
|
||||
ts := time.Now().UnixNano()
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: fmt.Sprintf("e2e-tests-%v-%v-", baseName, ts),
|
||||
Labels: labels,
|
||||
},
|
||||
}
|
||||
|
||||
// Be robust about making the namespace creation call.
|
||||
var got *corev1.Namespace
|
||||
var err error
|
||||
|
|
@ -111,8 +112,20 @@ func CreateKubeNamespace(baseName string, c kubernetes.Interface) (string, error
|
|||
return got.Name, nil
|
||||
}
|
||||
|
||||
// deleteKubeNamespace deletes a namespace and all the objects inside
|
||||
func deleteKubeNamespace(c kubernetes.Interface, namespace string) error {
|
||||
// CreateKubeNamespace creates a new namespace in the cluster
|
||||
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)
|
||||
pb := metav1.DeletePropagationBackground
|
||||
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…
Add table
Add a link
Reference in a new issue