Enable validation of ingress definitions from extensions package
This commit is contained in:
parent
fb6a03ffb4
commit
703c2d6f8e
12 changed files with 225 additions and 79 deletions
|
|
@ -17,13 +17,16 @@ limitations under the License.
|
|||
package admission
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"k8s.io/ingress-nginx/test/e2e/framework"
|
||||
|
|
@ -37,6 +40,11 @@ var _ = framework.IngressNginxDescribe("[Serial] admission controller", func() {
|
|||
f.NewSlowEchoDeployment()
|
||||
})
|
||||
|
||||
ginkgo.AfterEach(func() {
|
||||
err := uninstallChart(f)
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "uninstalling helm chart")
|
||||
})
|
||||
|
||||
ginkgo.It("should not allow overlaps of host and paths without canary annotations", func() {
|
||||
host := "admission-test"
|
||||
|
||||
|
|
@ -52,9 +60,6 @@ var _ = framework.IngressNginxDescribe("[Serial] admission controller", func() {
|
|||
secondIngress := framework.NewSingleIngress("second-ingress", "/", host, f.Namespace, framework.EchoService, 80, nil)
|
||||
_, err = f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace).Create(context.TODO(), secondIngress, metav1.CreateOptions{})
|
||||
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with the same host and path should return an error")
|
||||
|
||||
err = uninstallChart(f)
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "uninstalling helm chart")
|
||||
})
|
||||
|
||||
ginkgo.It("should allow overlaps of host and paths with canary annotation", func() {
|
||||
|
|
@ -76,9 +81,6 @@ var _ = framework.IngressNginxDescribe("[Serial] admission controller", func() {
|
|||
secondIngress := framework.NewSingleIngress("second-ingress", "/", host, f.Namespace, framework.SlowEchoService, 80, canaryAnnotations)
|
||||
_, err = f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace).Create(context.TODO(), secondIngress, metav1.CreateOptions{})
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "creating an ingress with the same host and path should not return an error using a canary annotation")
|
||||
|
||||
err = uninstallChart(f)
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "uninstalling helm chart")
|
||||
})
|
||||
|
||||
ginkgo.It("should return an error if there is an error validating the ingress definition", func() {
|
||||
|
|
@ -90,11 +92,65 @@ var _ = framework.IngressNginxDescribe("[Serial] admission controller", func() {
|
|||
firstIngress := framework.NewSingleIngress("first-ingress", "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
_, err := f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace).Create(context.TODO(), firstIngress, metav1.CreateOptions{})
|
||||
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid configuration should return an error")
|
||||
|
||||
err = uninstallChart(f)
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "uninstalling helm chart")
|
||||
})
|
||||
|
||||
ginkgo.It("should not return an error the ingress definition uses the deprecated extensions package", func() {
|
||||
err := createIngress(f.Namespace, validIngress)
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "creating an ingress using kubectl")
|
||||
|
||||
f.WaitForNginxConfiguration(func(cfg string) bool {
|
||||
return strings.Contains(cfg, "extensions")
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", "extensions").
|
||||
Expect().
|
||||
Status(http.StatusOK)
|
||||
})
|
||||
|
||||
ginkgo.It("should return an error if the ingress definition uses the deprecated extensions package and invalid annotations", func() {
|
||||
err := createIngress(f.Namespace, invalidIngress)
|
||||
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress using kubectl")
|
||||
|
||||
_, err = f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace).Get(context.TODO(), "extensions", metav1.GetOptions{})
|
||||
if !apierrors.IsNotFound(err) {
|
||||
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid configuration should return an error")
|
||||
}
|
||||
})
|
||||
|
||||
ginkgo.It("should not return an error if the Ingress V1 definition is valid", func() {
|
||||
if !f.IsIngressV1Ready {
|
||||
ginkgo.Skip("Test requires Kubernetes v1.19 or higher")
|
||||
}
|
||||
|
||||
err := createIngress(f.Namespace, validV1Ingress)
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "creating an ingress using kubectl")
|
||||
|
||||
f.WaitForNginxConfiguration(func(cfg string) bool {
|
||||
return strings.Contains(cfg, "extensions")
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", "extensions").
|
||||
Expect().
|
||||
Status(http.StatusOK)
|
||||
})
|
||||
|
||||
ginkgo.It("should return an error if the Ingress V1 definition contains invalid annotations", func() {
|
||||
if !f.IsIngressV1Ready {
|
||||
ginkgo.Skip("Test requires Kubernetes v1.19 or higher")
|
||||
}
|
||||
|
||||
err := createIngress(f.Namespace, invalidV1Ingress)
|
||||
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress using kubectl")
|
||||
|
||||
_, err = f.KubeClientSet.NetworkingV1beta1().Ingresses(f.Namespace).Get(context.TODO(), "extensions", metav1.GetOptions{})
|
||||
if !apierrors.IsNotFound(err) {
|
||||
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid configuration should return an error")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
func uninstallChart(f *framework.Framework) error {
|
||||
|
|
@ -106,3 +162,104 @@ func uninstallChart(f *framework.Framework) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
validIngress = `
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: extensions
|
||||
spec:
|
||||
rules:
|
||||
- host: extensions
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: echo
|
||||
servicePort: 80
|
||||
---
|
||||
`
|
||||
|
||||
invalidIngress = `
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: extensions
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
invalid directive
|
||||
spec:
|
||||
rules:
|
||||
- host: extensions
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: echo
|
||||
servicePort: 80
|
||||
---
|
||||
`
|
||||
|
||||
validV1Ingress = `
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: extensions
|
||||
spec:
|
||||
rules:
|
||||
- host: extensions
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: echo
|
||||
port:
|
||||
number: 80
|
||||
|
||||
---
|
||||
`
|
||||
|
||||
invalidV1Ingress = `
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: extensions
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
invalid directive
|
||||
spec:
|
||||
rules:
|
||||
- host: extensions
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: echo
|
||||
port:
|
||||
number: 80
|
||||
---
|
||||
`
|
||||
)
|
||||
|
||||
func createIngress(namespace, ingressDefinition string) error {
|
||||
var (
|
||||
execErr bytes.Buffer
|
||||
)
|
||||
|
||||
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("%v --warnings-as-errors=false apply --namespace %s -f -", framework.KubectlPath, namespace))
|
||||
cmd.Stdin = strings.NewReader(ingressDefinition)
|
||||
cmd.Stderr = &execErr
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
stderr := strings.TrimSpace(execErr.String())
|
||||
return fmt.Errorf("Kubectl error: %v\n%v", err, stderr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/ingress-nginx/internal/k8s"
|
||||
"k8s.io/klog/v2"
|
||||
|
|
@ -63,7 +64,8 @@ var (
|
|||
type Framework struct {
|
||||
BaseName string
|
||||
|
||||
IsIngressV1Ready bool
|
||||
IsIngressV1Ready bool
|
||||
IsIngressV1Beta1Ready bool
|
||||
|
||||
// A Kubernetes and Service Catalog client
|
||||
KubeClientSet kubernetes.Interface
|
||||
|
|
@ -97,11 +99,14 @@ func (f *Framework) BeforeEach() {
|
|||
if f.KubeClientSet == nil {
|
||||
f.KubeConfig, err = kubeframework.LoadConfig()
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "loading a kubernetes client configuration")
|
||||
|
||||
// TODO: remove after k8s v1.22
|
||||
f.KubeConfig.WarningHandler = rest.NoWarnings{}
|
||||
|
||||
f.KubeClientSet, err = kubernetes.NewForConfig(f.KubeConfig)
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "creating a kubernetes client")
|
||||
|
||||
_, isIngressV1Ready := k8s.NetworkingIngressAvailable(f.KubeClientSet)
|
||||
f.IsIngressV1Ready = isIngressV1Ready
|
||||
_, f.IsIngressV1Beta1Ready, f.IsIngressV1Ready = k8s.NetworkingIngressAvailable(f.KubeClientSet)
|
||||
}
|
||||
|
||||
f.Namespace, err = CreateKubeNamespace(f.BaseName, f.KubeClientSet)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() {
|
|||
})
|
||||
|
||||
ginkgo.It("should choose exact location for /exact", func() {
|
||||
if !f.IsIngressV1Ready {
|
||||
if !f.IsIngressV1Beta1Ready {
|
||||
ginkgo.Skip("Test requires Kubernetes v1.18 or higher")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
appsv1 "k8s.io/api/apps/v1"
|
||||
networking "k8s.io/api/networking/v1beta1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
|
||||
|
|
@ -40,6 +41,8 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() {
|
|||
|
||||
var doOnce sync.Once
|
||||
|
||||
testIngressClassName := "test-new-ingress-class"
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
f.NewEchoDeploymentWithReplicas(1)
|
||||
|
||||
|
|
@ -63,6 +66,20 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() {
|
|||
Name: "ingress-nginx-class",
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
|
||||
_, err := f.KubeClientSet.NetworkingV1beta1().IngressClasses().
|
||||
Create(context.TODO(), &networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: testIngressClassName,
|
||||
},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: k8s.IngressNGINXController,
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "creating IngressClass")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -193,28 +210,11 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() {
|
|||
})
|
||||
|
||||
ginkgo.It("check scenarios for IngressClass and ingress.class annotation", func() {
|
||||
if !f.IsIngressV1Ready {
|
||||
if !f.IsIngressV1Beta1Ready {
|
||||
ginkgo.Skip("Test requires Kubernetes v1.18 or higher")
|
||||
}
|
||||
|
||||
ingressClassName := "test-new-ingress-class"
|
||||
|
||||
ingressClass, err := f.KubeClientSet.NetworkingV1beta1().IngressClasses().
|
||||
Create(context.TODO(), &networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ingressClassName,
|
||||
},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: k8s.IngressNGINXController,
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
|
||||
if ingressClass == nil {
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "creating IngressClass")
|
||||
}
|
||||
|
||||
pod := f.GetIngressNGINXPod()
|
||||
serviceAccount := pod.Spec.ServiceAccountName
|
||||
|
||||
crb, err := f.KubeClientSet.RbacV1().ClusterRoleBindings().Get(context.Background(), "ingress-nginx-class", metav1.GetOptions{})
|
||||
assert.Nil(ginkgo.GinkgoT(), err, "searching cluster role binding")
|
||||
|
|
@ -223,7 +223,7 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() {
|
|||
crb.Subjects = append(crb.Subjects, rbacv1.Subject{
|
||||
APIGroup: "",
|
||||
Kind: "ServiceAccount",
|
||||
Name: serviceAccount,
|
||||
Name: pod.Spec.ServiceAccountName,
|
||||
Namespace: f.Namespace,
|
||||
})
|
||||
|
||||
|
|
@ -240,7 +240,7 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() {
|
|||
args = append(args, v)
|
||||
}
|
||||
|
||||
args = append(args, fmt.Sprintf("--ingress-class=%v", ingressClassName))
|
||||
args = append(args, fmt.Sprintf("--ingress-class=%v", testIngressClassName))
|
||||
deployment.Spec.Template.Spec.Containers[0].Args = args
|
||||
_, err := f.KubeClientSet.AppsV1().Deployments(f.Namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{})
|
||||
return err
|
||||
|
|
@ -251,10 +251,10 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() {
|
|||
|
||||
ginkgo.By("only having IngressClassName")
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)
|
||||
ing.Spec.IngressClassName = &ingressClassName
|
||||
ing.Spec.IngressClassName = &testIngressClassName
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host, func(cfg string) bool {
|
||||
f.WaitForNginxConfiguration(func(cfg string) bool {
|
||||
return strings.Contains(cfg, fmt.Sprintf("server_name %v", host))
|
||||
})
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ var _ = framework.IngressNginxDescribe("[Flag] ingress-class", func() {
|
|||
assert.Nil(ginkgo.GinkgoT(), err)
|
||||
|
||||
ing.Annotations = map[string]string{
|
||||
class.IngressKey: ingressClassName,
|
||||
class.IngressKey: testIngressClassName,
|
||||
}
|
||||
ing.Spec.IngressClassName = nil
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue