mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-25 16:07:24 +00:00
test(e2e): scoped Ingress hostname and path collision
This commit is contained in:
@@ -30,12 +30,14 @@ var _ = Describe("when Tenant handles Ingress classes with extensions/v1beta1",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressClasses: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{
|
||||
"nginx",
|
||||
"haproxy",
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
AllowedClasses: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{
|
||||
"nginx",
|
||||
"haproxy",
|
||||
},
|
||||
Regex: "^oil-.*$",
|
||||
},
|
||||
Regex: "^oil-.*$",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -133,7 +135,7 @@ var _ = Describe("when Tenant handles Ingress classes with extensions/v1beta1",
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
for _, c := range tnt.Spec.IngressClasses.Exact {
|
||||
for _, c := range tnt.Spec.IngressOptions.AllowedClasses.Exact {
|
||||
Eventually(func() (err error) {
|
||||
i := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -171,7 +173,7 @@ var _ = Describe("when Tenant handles Ingress classes with extensions/v1beta1",
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
for _, c := range tnt.Spec.IngressClasses.Exact {
|
||||
for _, c := range tnt.Spec.IngressOptions.AllowedClasses.Exact {
|
||||
Eventually(func() (err error) {
|
||||
i := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
||||
@@ -29,12 +29,14 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressClasses: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{
|
||||
"nginx",
|
||||
"haproxy",
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
AllowedClasses: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{
|
||||
"nginx",
|
||||
"haproxy",
|
||||
},
|
||||
Regex: "^oil-.*$",
|
||||
},
|
||||
Regex: "^oil-.*$",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -144,7 +146,7 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
for _, c := range tnt.Spec.IngressClasses.Exact {
|
||||
for _, c := range tnt.Spec.IngressOptions.AllowedClasses.Exact {
|
||||
Eventually(func() (err error) {
|
||||
i := &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -183,7 +185,7 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
for _, c := range tnt.Spec.IngressClasses.Exact {
|
||||
for _, c := range tnt.Spec.IngressOptions.AllowedClasses.Exact {
|
||||
Eventually(func() (err error) {
|
||||
i := &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
//+build e2e
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1alpha1 "github.com/clastix/capsule/api/v1alpha1"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("when handling Ingress hostnames collision", func() {
|
||||
tnt := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ingress-hostnames-allowed-collision",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-allowed",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// scaffold a basic networking.k8s.io Ingress with name and host
|
||||
networkingIngress := func(name, hostname string) *networkingv1.Ingress {
|
||||
return &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: networkingv1.IngressSpec{
|
||||
Rules: []networkingv1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
// scaffold a basic extensions Ingress with name and host
|
||||
extensionsIngress := func(name, hostname string) *extensionsv1beta1.Ingress {
|
||||
return &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Rules: []extensionsv1beta1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1alpha1.CapsuleConfiguration) {
|
||||
configuration.Spec.AllowIngressHostnameCollision = true
|
||||
})
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1alpha1.CapsuleConfiguration) {
|
||||
configuration.Spec.AllowIngressHostnameCollision = false
|
||||
})
|
||||
})
|
||||
|
||||
It("should not allow creating several Ingress with same hostname", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1alpha1.CapsuleConfiguration) {
|
||||
configuration.Spec.AllowIngressHostnameCollision = false
|
||||
})
|
||||
|
||||
maj, min, _ := GetKubernetesSemVer()
|
||||
|
||||
ns := NewNamespace("denied-collision")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
if maj == 1 && min > 18 {
|
||||
By("testing networking.k8s.io", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-1", "kubernetes.io")
|
||||
_, err = cs.NetworkingV1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}).Should(Succeed())
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-2", "kubernetes.io")
|
||||
_, err = cs.NetworkingV1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
|
||||
if maj == 1 && min < 22 {
|
||||
By("testing extensions", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-1", "cncf.io")
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}).Should(Succeed())
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-2", "cncf.io")
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
It("should allow creating several Ingress with same hostname", func() {
|
||||
maj, min, _ := GetKubernetesSemVer()
|
||||
|
||||
ns := NewNamespace("allowed-collision")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
if maj == 1 && min > 18 {
|
||||
By("testing networking.k8s.io", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-1", "kubernetes.io")
|
||||
_, err = cs.NetworkingV1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}).Should(Succeed())
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-2", "kubernetes.io")
|
||||
_, err = cs.NetworkingV1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}).Should(Succeed())
|
||||
})
|
||||
}
|
||||
|
||||
if maj == 1 && min < 22 {
|
||||
By("testing extensions", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-1", "cncf.io")
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}).Should(Succeed())
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-2", "cncf.io")
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}).Should(Succeed())
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,221 @@
|
||||
//+build e2e
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("when handling Cluster scoped Ingress hostnames collision", func() {
|
||||
tnt1 := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "hostnames-collision-cluster-one",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-tenant-one",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
HostnameCollisionScope: capsulev1beta1.HostnameCollisionScopeCluster,
|
||||
},
|
||||
},
|
||||
}
|
||||
tnt2 := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "hostnames-collision-cluster-two",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-tenant-two",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
HostnameCollisionScope: capsulev1beta1.HostnameCollisionScopeCluster,
|
||||
},
|
||||
},
|
||||
}
|
||||
// scaffold a basic networking.k8s.io Ingress with name and host
|
||||
networkingIngress := func(name, hostname, path string) *networkingv1.Ingress {
|
||||
return &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: networkingv1.IngressSpec{
|
||||
Rules: []networkingv1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
IngressRuleValue: networkingv1.IngressRuleValue{
|
||||
HTTP: &networkingv1.HTTPIngressRuleValue{
|
||||
Paths: []networkingv1.HTTPIngressPath{
|
||||
{
|
||||
Path: path,
|
||||
PathType: func(v networkingv1.PathType) *networkingv1.PathType {
|
||||
return &v
|
||||
}(networkingv1.PathTypeExact),
|
||||
Backend: networkingv1.IngressBackend{
|
||||
Service: &networkingv1.IngressServiceBackend{
|
||||
Name: "example",
|
||||
Port: networkingv1.ServiceBackendPort{
|
||||
Number: 8080,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
// scaffold a basic extensions Ingress with name and host
|
||||
extensionsIngress := func(name, hostname, path string) *extensionsv1beta1.Ingress {
|
||||
return &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Rules: []extensionsv1beta1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
IngressRuleValue: extensionsv1beta1.IngressRuleValue{
|
||||
HTTP: &extensionsv1beta1.HTTPIngressRuleValue{
|
||||
Paths: []extensionsv1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: path,
|
||||
PathType: func(v extensionsv1beta1.PathType) *extensionsv1beta1.PathType {
|
||||
return &v
|
||||
}(extensionsv1beta1.PathTypeExact),
|
||||
Backend: extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "example",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
tnt1.ResourceVersion = ""
|
||||
|
||||
return k8sClient.Create(context.TODO(), tnt1)
|
||||
}).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
tnt2.ResourceVersion = ""
|
||||
|
||||
return k8sClient.Create(context.TODO(), tnt2)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt1)).Should(Succeed())
|
||||
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt2)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should ensure Cluster scope for Ingress hostname and path collision", func() {
|
||||
maj, min, _ := GetKubernetesSemVer()
|
||||
|
||||
ns1 := NewNamespace("tenant-one-ns")
|
||||
|
||||
cs1 := ownerClient(tnt1.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns1, tnt1.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
TenantNamespaceList(tnt1, defaultTimeoutInterval).Should(ContainElement(ns1.GetName()))
|
||||
|
||||
ns2 := NewNamespace("tenant-two-ns")
|
||||
|
||||
cs2 := ownerClient(tnt2.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns2, tnt2.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
TenantNamespaceList(tnt2, defaultTimeoutInterval).Should(ContainElement(ns2.GetName()))
|
||||
|
||||
if maj == 1 && min > 18 {
|
||||
By("testing networking.k8s.io", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-1", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs1.NetworkingV1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// Creating a second Ingress with same hostname but a different path in a Namespace managed by the same
|
||||
// Tenant should not trigger a collision...
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-2", "kubernetes.io", "/docs")
|
||||
|
||||
_, err = cs2.NetworkingV1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// ...but it happens if hostname and path collide with the first Ingress,
|
||||
// although in a different Namespace
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-3", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs2.NetworkingV1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
|
||||
if maj == 1 && min < 22 {
|
||||
By("testing extensions", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-1", "cncf.io", "/foo")
|
||||
|
||||
_, err = cs1.ExtensionsV1beta1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// Creating a second Ingress with same hostname but a different path in a Namespace managed by the same
|
||||
// Tenant should not trigger a collision...
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-2", "cncf.io", "/bar")
|
||||
|
||||
_, err = cs2.ExtensionsV1beta1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// ...but it happens if hostname and path collide with the first Ingress,
|
||||
// although in a different Namespace
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-3", "cncf.io", "/foo")
|
||||
|
||||
_, err = cs2.ExtensionsV1beta1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,204 @@
|
||||
//+build e2e
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("when disabling Ingress hostnames collision", func() {
|
||||
tnt := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "hostnames-collision-disabled",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-disabled",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
HostnameCollisionScope: capsulev1beta1.HostnameCollisionScopeDisabled,
|
||||
},
|
||||
},
|
||||
}
|
||||
// scaffold a basic networking.k8s.io Ingress with name and host
|
||||
networkingIngress := func(name, hostname, path string) *networkingv1.Ingress {
|
||||
return &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: networkingv1.IngressSpec{
|
||||
Rules: []networkingv1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
IngressRuleValue: networkingv1.IngressRuleValue{
|
||||
HTTP: &networkingv1.HTTPIngressRuleValue{
|
||||
Paths: []networkingv1.HTTPIngressPath{
|
||||
{
|
||||
Path: path,
|
||||
PathType: func(v networkingv1.PathType) *networkingv1.PathType {
|
||||
return &v
|
||||
}(networkingv1.PathTypeExact),
|
||||
Backend: networkingv1.IngressBackend{
|
||||
Service: &networkingv1.IngressServiceBackend{
|
||||
Name: "example",
|
||||
Port: networkingv1.ServiceBackendPort{
|
||||
Number: 8080,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
// scaffold a basic extensions Ingress with name and host
|
||||
extensionsIngress := func(name, hostname, path string) *extensionsv1beta1.Ingress {
|
||||
return &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Rules: []extensionsv1beta1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
IngressRuleValue: extensionsv1beta1.IngressRuleValue{
|
||||
HTTP: &extensionsv1beta1.HTTPIngressRuleValue{
|
||||
Paths: []extensionsv1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: path,
|
||||
PathType: func(v extensionsv1beta1.PathType) *extensionsv1beta1.PathType {
|
||||
return &v
|
||||
}(extensionsv1beta1.PathTypeExact),
|
||||
Backend: extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "example",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should not check any kind of collision", func() {
|
||||
maj, min, _ := GetKubernetesSemVer()
|
||||
|
||||
ns1 := NewNamespace("namespace-collision-one")
|
||||
|
||||
ns2 := NewNamespace("namespace-collision-two")
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns1.GetName()))
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns2.GetName()))
|
||||
|
||||
if maj == 1 && min > 18 {
|
||||
By("testing networking.k8s.io", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-1", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-2", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-3", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-4", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
})
|
||||
}
|
||||
|
||||
if maj == 1 && min < 22 {
|
||||
By("testing extensions", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-1", "cncf.io", "/docs")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-2", "cncf.io", "/docs")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-3", "cncf.io", "/docs")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-4", "cncf.io", "/docs")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,206 @@
|
||||
//+build e2e
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("when handling Namespace scoped Ingress hostnames collision", func() {
|
||||
tnt := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "hostnames-collision-namespace",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-namespace",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
HostnameCollisionScope: capsulev1beta1.HostnameCollisionScopeNamespace,
|
||||
},
|
||||
},
|
||||
}
|
||||
// scaffold a basic networking.k8s.io Ingress with name and host
|
||||
networkingIngress := func(name, hostname, path string) *networkingv1.Ingress {
|
||||
return &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: networkingv1.IngressSpec{
|
||||
Rules: []networkingv1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
IngressRuleValue: networkingv1.IngressRuleValue{
|
||||
HTTP: &networkingv1.HTTPIngressRuleValue{
|
||||
Paths: []networkingv1.HTTPIngressPath{
|
||||
{
|
||||
Path: path,
|
||||
PathType: func(v networkingv1.PathType) *networkingv1.PathType {
|
||||
return &v
|
||||
}(networkingv1.PathTypeExact),
|
||||
Backend: networkingv1.IngressBackend{
|
||||
Service: &networkingv1.IngressServiceBackend{
|
||||
Name: "example",
|
||||
Port: networkingv1.ServiceBackendPort{
|
||||
Number: 8080,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
// scaffold a basic extensions Ingress with name and host
|
||||
extensionsIngress := func(name, hostname, path string) *extensionsv1beta1.Ingress {
|
||||
return &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Rules: []extensionsv1beta1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
IngressRuleValue: extensionsv1beta1.IngressRuleValue{
|
||||
HTTP: &extensionsv1beta1.HTTPIngressRuleValue{
|
||||
Paths: []extensionsv1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: path,
|
||||
PathType: func(v extensionsv1beta1.PathType) *extensionsv1beta1.PathType {
|
||||
return &v
|
||||
}(extensionsv1beta1.PathTypeExact),
|
||||
Backend: extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "example",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should ensure Namespace scope for Ingress hostname and path collision", func() {
|
||||
maj, min, _ := GetKubernetesSemVer()
|
||||
|
||||
ns1 := NewNamespace("namespace-collision-one")
|
||||
|
||||
ns2 := NewNamespace("namespace-collision-two")
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns1.GetName()))
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns2.GetName()))
|
||||
|
||||
if maj == 1 && min > 18 {
|
||||
By("testing networking.k8s.io", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-1", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// A same Ingress with hostname and path pair can be created in a different Namespace,
|
||||
// although of the same Tenant
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-2", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// ...but a collision occurs if the same pair is created in the same Namespace
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-3", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-4", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
|
||||
if maj == 1 && min < 22 {
|
||||
By("testing extensions", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-1", "cncf.io", "/docs")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// A same Ingress with hostname and path pair can be created in a different Namespace,
|
||||
// although of the same Tenant
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-2", "cncf.io", "/docs")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// ...but a collision occurs if the same pair is created in the same Namespace
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-3", "cncf.io", "/docs")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-4", "cncf.io", "/docs")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,192 @@
|
||||
//+build e2e
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("when handling Tenant scoped Ingress hostnames collision", func() {
|
||||
tnt := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "hostnames-collision-tenant",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-tenant",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
HostnameCollisionScope: capsulev1beta1.HostnameCollisionScopeTenant,
|
||||
},
|
||||
},
|
||||
}
|
||||
// scaffold a basic networking.k8s.io Ingress with name and host
|
||||
networkingIngress := func(name, hostname, path string) *networkingv1.Ingress {
|
||||
return &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: networkingv1.IngressSpec{
|
||||
Rules: []networkingv1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
IngressRuleValue: networkingv1.IngressRuleValue{
|
||||
HTTP: &networkingv1.HTTPIngressRuleValue{
|
||||
Paths: []networkingv1.HTTPIngressPath{
|
||||
{
|
||||
Path: path,
|
||||
PathType: func(v networkingv1.PathType) *networkingv1.PathType {
|
||||
return &v
|
||||
}(networkingv1.PathTypeExact),
|
||||
Backend: networkingv1.IngressBackend{
|
||||
Service: &networkingv1.IngressServiceBackend{
|
||||
Name: "example",
|
||||
Port: networkingv1.ServiceBackendPort{
|
||||
Number: 8080,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
// scaffold a basic extensions Ingress with name and host
|
||||
extensionsIngress := func(name, hostname, path string) *extensionsv1beta1.Ingress {
|
||||
return &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Rules: []extensionsv1beta1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
IngressRuleValue: extensionsv1beta1.IngressRuleValue{
|
||||
HTTP: &extensionsv1beta1.HTTPIngressRuleValue{
|
||||
Paths: []extensionsv1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: path,
|
||||
PathType: func(v extensionsv1beta1.PathType) *extensionsv1beta1.PathType {
|
||||
return &v
|
||||
}(extensionsv1beta1.PathTypeExact),
|
||||
Backend: extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "example",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
|
||||
It("should ensure Tenant scope for Ingress hostname and path collision", func() {
|
||||
maj, min, _ := GetKubernetesSemVer()
|
||||
|
||||
ns1 := NewNamespace("cluster-collision-one")
|
||||
|
||||
ns2 := NewNamespace("cluster-collision-two")
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns1.GetName()))
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns2.GetName()))
|
||||
|
||||
if maj == 1 && min > 18 {
|
||||
By("testing networking.k8s.io", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-1", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// Creating a second Ingress with same hostname but a different path in a Namespace managed by the same
|
||||
// Tenant should not trigger a collision...
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-2", "kubernetes.io", "/docs")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// ...but it happens if hostname and path collide with the first Ingress,
|
||||
// although in a different Namespace
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := networkingIngress("networking-3", "kubernetes.io", "/path")
|
||||
|
||||
_, err = cs.NetworkingV1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
|
||||
if maj == 1 && min < 22 {
|
||||
By("testing extensions", func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-1", "cncf.io", "/foo")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns1.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// Creating a second Ingress with same hostname but a different path in a Namespace managed by the same
|
||||
// Tenant should not trigger a collision...
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-2", "cncf.io", "/bar")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
// ...but it happens if hostname and path collide with the first Ingress,
|
||||
// although in a different Namespace
|
||||
EventuallyCreation(func() (err error) {
|
||||
obj := extensionsIngress("extensions-3", "cncf.io", "/foo")
|
||||
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns2.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,125 +0,0 @@
|
||||
//+build e2e
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1alpha1 "github.com/clastix/capsule/api/v1alpha1"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("when handling Ingress hostnames collision", func() {
|
||||
tnt := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ingress-hostnames-denied-collision",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-denied",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// scaffold a basic networking.k8s.io Ingress with name and host
|
||||
networkingIngress := func(name, hostname string) *networkingv1.Ingress {
|
||||
return &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: networkingv1.IngressSpec{
|
||||
Rules: []networkingv1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
// scaffold a basic extensions Ingress with name and host
|
||||
extensionsIngress := func(name, hostname string) *extensionsv1beta1.Ingress {
|
||||
return &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Rules: []extensionsv1beta1.IngressRule{
|
||||
{
|
||||
Host: hostname,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1alpha1.CapsuleConfiguration) {
|
||||
configuration.Spec.AllowIngressHostnameCollision = true
|
||||
})
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1alpha1.CapsuleConfiguration) {
|
||||
configuration.Spec.AllowIngressHostnameCollision = false
|
||||
})
|
||||
})
|
||||
|
||||
It("should not allow creating several Ingress with same hostname", func() {
|
||||
maj, min, _ := GetKubernetesSemVer()
|
||||
|
||||
ns := NewNamespace("allowed-collision")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
if maj == 1 && min > 18 {
|
||||
By("testing networking.k8s.io", func() {
|
||||
Eventually(func() (err error) {
|
||||
obj := networkingIngress("networking-1", "kubernetes.io")
|
||||
_, err = cs.NetworkingV1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
Eventually(func() (err error) {
|
||||
obj := networkingIngress("networking-2", "kubernetes.io")
|
||||
_, err = cs.NetworkingV1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
|
||||
if maj == 1 && min < 22 {
|
||||
By("testing extensions", func() {
|
||||
Eventually(func() (err error) {
|
||||
obj := extensionsIngress("extensions-1", "cncf.io")
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
Eventually(func() (err error) {
|
||||
obj := extensionsIngress("extensions-2", "cncf.io")
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -31,9 +31,11 @@ var _ = Describe("when Tenant handles Ingress hostnames", func() {
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressHostnames: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{"sigs.k8s.io", "operator.sdk", "domain.tld"},
|
||||
Regex: `.*\.clastix\.io`,
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
AllowedHostnames: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{"sigs.k8s.io", "operator.sdk", "domain.tld"},
|
||||
Regex: `.*\.clastix\.io`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -174,7 +176,7 @@ var _ = Describe("when Tenant handles Ingress hostnames", func() {
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("testing networking.k8s.io", func() {
|
||||
for i, h := range tnt.Spec.IngressHostnames.Exact {
|
||||
for i, h := range tnt.Spec.IngressOptions.AllowedHostnames.Exact {
|
||||
Eventually(func() (err error) {
|
||||
obj := networkingIngress(fmt.Sprintf("allowed-networking-%d", i), h)
|
||||
_, err = cs.NetworkingV1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
@@ -200,7 +202,7 @@ var _ = Describe("when Tenant handles Ingress hostnames", func() {
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("testing extensions", func() {
|
||||
for i, h := range tnt.Spec.IngressHostnames.Exact {
|
||||
for i, h := range tnt.Spec.IngressOptions.AllowedHostnames.Exact {
|
||||
Eventually(func() (err error) {
|
||||
obj := extensionsIngress(fmt.Sprintf("allowed-extensions-%d", i), h)
|
||||
_, err = cs.ExtensionsV1beta1().Ingresses(ns.GetName()).Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
//+build e2e
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1alpha1 "github.com/clastix/capsule/api/v1alpha1"
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("when a second Tenant contains an already declared allowed Ingress hostname", func() {
|
||||
tnt := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "allowed-collision-ingress-hostnames",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "first-user",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressHostnames: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{"capsule.clastix.io", "docs.capsule.k8s", "42.clatix.io"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1alpha1.CapsuleConfiguration) {
|
||||
configuration.Spec.AllowTenantIngressHostnamesCollision = false
|
||||
})
|
||||
})
|
||||
|
||||
It("should block creation if contains collided Ingress hostnames", func() {
|
||||
var cleanupFuncs []func()
|
||||
|
||||
for i, h := range tnt.Spec.IngressHostnames.Exact {
|
||||
duplicated := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-%d", tnt.GetName(), i),
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "second-user",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressHostnames: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{h},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), duplicated)
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
cleanupFuncs = append(cleanupFuncs, func() {
|
||||
duplicatedTnt := *duplicated
|
||||
|
||||
_ = k8sClient.Delete(context.TODO(), &duplicatedTnt)
|
||||
})
|
||||
}
|
||||
|
||||
for _, fn := range cleanupFuncs {
|
||||
fn()
|
||||
}
|
||||
})
|
||||
|
||||
It("should not block creation if contains collided Ingress hostnames", func() {
|
||||
var cleanupFuncs []func()
|
||||
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1alpha1.CapsuleConfiguration) {
|
||||
configuration.Spec.AllowTenantIngressHostnamesCollision = true
|
||||
})
|
||||
|
||||
for i, h := range tnt.Spec.IngressHostnames.Exact {
|
||||
duplicated := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-%d", tnt.GetName(), i),
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "second-user",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressHostnames: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{h},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), duplicated)
|
||||
}).Should(Succeed())
|
||||
|
||||
cleanupFuncs = append(cleanupFuncs, func() {
|
||||
duplicatedTnt := *duplicated
|
||||
|
||||
_ = k8sClient.Delete(context.TODO(), &duplicatedTnt)
|
||||
})
|
||||
}
|
||||
|
||||
for _, fn := range cleanupFuncs {
|
||||
fn()
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,83 +0,0 @@
|
||||
//+build e2e
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("when a second Tenant contains an already declared allowed Ingress hostname", func() {
|
||||
tnt := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "no-collision-ingress-hostnames",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "first-user",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressHostnames: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{"capsule.clastix.io", "docs.capsule.k8s", "42.clatix.io"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should block creation if contains collided Ingress hostnames", func() {
|
||||
var cleanupFuncs []func()
|
||||
|
||||
for i, h := range tnt.Spec.IngressHostnames.Exact {
|
||||
duplicated := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-%d", tnt.GetName(), i),
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "second-user",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
IngressHostnames: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{h},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), duplicated)
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
cleanupFuncs = append(cleanupFuncs, func() {
|
||||
duplicatedTenant := *duplicated
|
||||
|
||||
k8sClient.Delete(context.TODO(), &duplicatedTenant)
|
||||
})
|
||||
}
|
||||
|
||||
for _, fn := range cleanupFuncs {
|
||||
fn()
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -208,7 +208,9 @@ var _ = Describe("creating namespaces within a Tenant with resources", func() {
|
||||
n := fmt.Sprintf("capsule-%s-%d", tnt.GetName(), i)
|
||||
|
||||
rq := &corev1.ResourceQuota{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: n, Namespace: name}, rq)).Should(Succeed())
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: n, Namespace: name}, rq); err != nil {
|
||||
return corev1.ResourceQuotaSpec{}
|
||||
}
|
||||
|
||||
return rq.Spec
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Equal(s))
|
||||
|
||||
Reference in New Issue
Block a user