mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-23 06:26:43 +00:00
Using matrix strategy for e2e on multiple k8s versions (#111)
* Using matrix strategy for e2e on multiple k8s versions * EndpointSlice version support according to Kubernetes release * Utility helper for testing various Kubernetes versions
This commit is contained in:
@@ -28,6 +28,9 @@ jobs:
|
||||
args: --timeout 2m
|
||||
kind:
|
||||
name: e2e
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ['v1.16.15', 'v1.17.11', 'v1.18.8', 'v1.19.1']
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
@@ -40,10 +43,10 @@ jobs:
|
||||
~/go/pkg/mod
|
||||
/var/lib/docker
|
||||
/home/runner/work/capsule/capsule
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}
|
||||
key: ${{ matrix.k8s-version }}-build-${{ env.cache-name }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
${{ matrix.k8s-version }}-build-
|
||||
${{ matrix.k8s-version }}-
|
||||
- name: Removing kustomize
|
||||
run: sudo snap remove kustomize && sudo rm -rf $(which kustomize)
|
||||
- name: Installing Ginkgo
|
||||
@@ -55,4 +58,4 @@ jobs:
|
||||
with:
|
||||
skipClusterCreation: true
|
||||
- name: e2e testing
|
||||
run: make e2e
|
||||
run: make e2e/${{ matrix.k8s-version }}
|
||||
|
||||
@@ -135,8 +135,8 @@ golint:
|
||||
|
||||
# Running e2e tests in a KinD instance
|
||||
.PHONY: e2e
|
||||
e2e:
|
||||
kind create cluster --name capsule --image=kindest/node:v1.18.0
|
||||
e2e/%:
|
||||
kind create cluster --name capsule --image=kindest/node:$*
|
||||
make docker-build
|
||||
kind load docker-image --nodes capsule-control-plane --name capsule quay.io/clastix/capsule:latest
|
||||
make deploy
|
||||
|
||||
+31
-44
@@ -17,16 +17,14 @@ package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1beta12 "k8s.io/api/extensions/v1beta1"
|
||||
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"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/clastix/capsule/api/v1alpha1"
|
||||
@@ -75,12 +73,12 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
|
||||
|
||||
By("non-specifying the class", func() {
|
||||
Eventually(func() (err error) {
|
||||
i := &v1beta12.Ingress{
|
||||
i := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denied-ingress",
|
||||
},
|
||||
Spec: v1beta12.IngressSpec{
|
||||
Backend: &v1beta12.IngressBackend{
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Backend: &extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "foo",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
@@ -92,15 +90,15 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
|
||||
})
|
||||
By("using a forbidden class as Annotation", func() {
|
||||
Eventually(func() (err error) {
|
||||
i := &v1beta12.Ingress{
|
||||
i := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denied-ingress",
|
||||
Annotations: map[string]string{
|
||||
"kubernetes.io/ingress.class": "the-worst-ingress-available",
|
||||
},
|
||||
},
|
||||
Spec: v1beta12.IngressSpec{
|
||||
Backend: &v1beta12.IngressBackend{
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Backend: &extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "foo",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
@@ -112,13 +110,13 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
|
||||
})
|
||||
By("specifying a forbidden class", func() {
|
||||
Eventually(func() (err error) {
|
||||
i := &v1beta12.Ingress{
|
||||
i := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denied-ingress",
|
||||
},
|
||||
Spec: v1beta12.IngressSpec{
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
IngressClassName: pointer.StringPtr("the-worst-ingress-available"),
|
||||
Backend: &v1beta12.IngressBackend{
|
||||
Backend: &extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "foo",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
@@ -138,15 +136,15 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
|
||||
|
||||
for _, c := range tnt.Spec.IngressClasses.Allowed {
|
||||
Eventually(func() (err error) {
|
||||
i := &v1beta12.Ingress{
|
||||
i := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: c,
|
||||
Annotations: map[string]string{
|
||||
"kubernetes.io/ingress.class": c,
|
||||
},
|
||||
},
|
||||
Spec: v1beta12.IngressSpec{
|
||||
Backend: &v1beta12.IngressBackend{
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Backend: &extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "foo",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
@@ -161,16 +159,9 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
|
||||
ns := NewNamespace("ingress-class-allowed-annotation")
|
||||
cs := ownerClient(tnt)
|
||||
|
||||
cs, err := kubernetes.NewForConfig(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
v, err := cs.Discovery().ServerVersion()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
major, err := strconv.Atoi(v.Major)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
minor, err := strconv.Atoi(v.Minor)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
if major == 1 && minor < 18 {
|
||||
Skip("Running test on Kubernetes " + v.String() + ", doesn't provide .spec.ingressClassName")
|
||||
maj, min, v := GetKubernetesSemVer()
|
||||
if maj == 1 && min < 18 {
|
||||
Skip("Running test on Kubernetes " + v + ", doesn't provide .spec.ingressClassName")
|
||||
}
|
||||
|
||||
NamespaceCreationShouldSucceed(ns, tnt, defaultTimeoutInterval)
|
||||
@@ -178,13 +169,13 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
|
||||
|
||||
for _, c := range tnt.Spec.IngressClasses.Allowed {
|
||||
Eventually(func() (err error) {
|
||||
i := &v1beta12.Ingress{
|
||||
i := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: c,
|
||||
},
|
||||
Spec: v1beta12.IngressSpec{
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
IngressClassName: &c,
|
||||
Backend: &v1beta12.IngressBackend{
|
||||
Backend: &extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "foo",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
@@ -204,15 +195,15 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
|
||||
NamespaceShouldBeManagedByTenant(ns, tnt, defaultTimeoutInterval)
|
||||
|
||||
Eventually(func() (err error) {
|
||||
i := &v1beta12.Ingress{
|
||||
i := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ingressClass,
|
||||
Annotations: map[string]string{
|
||||
"kubernetes.io/ingress.class": ingressClass,
|
||||
},
|
||||
},
|
||||
Spec: v1beta12.IngressSpec{
|
||||
Backend: &v1beta12.IngressBackend{
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
Backend: &extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "foo",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
@@ -227,29 +218,25 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
|
||||
cs := ownerClient(tnt)
|
||||
ingressClass := "oil-haproxy"
|
||||
|
||||
cs, err := kubernetes.NewForConfig(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
v, err := cs.Discovery().ServerVersion()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
major, err := strconv.Atoi(v.Major)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
minor, err := strconv.Atoi(v.Minor)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
if major == 1 && minor < 18 {
|
||||
Skip("Running test ont Kubernetes " + v.String() + ", doesn't provide .spec.ingressClassName")
|
||||
maj, min, v := GetKubernetesSemVer()
|
||||
if maj == 1 && min < 18 {
|
||||
Skip("Running test on Kubernetes " + v + ", doesn't provide .spec.ingressClassName")
|
||||
}
|
||||
if maj == 1 && min < 18 {
|
||||
Skip("Running test ont Kubernetes " + v + ", doesn't provide .spec.ingressClassName")
|
||||
}
|
||||
|
||||
NamespaceCreationShouldSucceed(ns, tnt, defaultTimeoutInterval)
|
||||
NamespaceShouldBeManagedByTenant(ns, tnt, defaultTimeoutInterval)
|
||||
|
||||
Eventually(func() (err error) {
|
||||
i := &v1beta12.Ingress{
|
||||
i := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ingressClass,
|
||||
},
|
||||
Spec: v1beta12.IngressSpec{
|
||||
Spec: extensionsv1beta1.IngressSpec{
|
||||
IngressClassName: &ingressClass,
|
||||
Backend: &v1beta12.IngressBackend{
|
||||
Backend: &extensionsv1beta1.IngressBackend{
|
||||
ServiceName: "foo",
|
||||
ServicePort: intstr.FromInt(8080),
|
||||
},
|
||||
|
||||
@@ -18,15 +18,15 @@ package e2e
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/clastix/capsule/api/v1alpha1"
|
||||
)
|
||||
@@ -132,24 +132,8 @@ var _ = Describe("creating a Service/Endpoint/EndpointSlice for a Tenant with ad
|
||||
},
|
||||
}
|
||||
|
||||
epsName := "foo"
|
||||
epsPort := int32(9999)
|
||||
eps := &discoveryv1beta1.EndpointSlice{
|
||||
ObjectMeta: meta,
|
||||
AddressType: discoveryv1beta1.AddressTypeIPv4,
|
||||
Endpoints: []discoveryv1beta1.Endpoint{
|
||||
{
|
||||
Addresses: []string{"10.10.1.1"},
|
||||
},
|
||||
},
|
||||
Ports: []discoveryv1beta1.EndpointPort{
|
||||
{
|
||||
Name: &epsName,
|
||||
Port: &epsPort,
|
||||
},
|
||||
},
|
||||
}
|
||||
cs := ownerClient(tnt)
|
||||
|
||||
Eventually(func() (err error) {
|
||||
_, err = cs.CoreV1().Services(ns.GetName()).Create(context.TODO(), svc, metav1.CreateOptions{})
|
||||
return
|
||||
@@ -158,13 +142,9 @@ var _ = Describe("creating a Service/Endpoint/EndpointSlice for a Tenant with ad
|
||||
_, err = cs.CoreV1().Endpoints(ns.GetName()).Create(context.TODO(), ep, metav1.CreateOptions{})
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
Eventually(func() (err error) {
|
||||
_, err = cs.DiscoveryV1beta1().EndpointSlices(ns.GetName()).Create(context.TODO(), eps, metav1.CreateOptions{})
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: svc.GetName(), Namespace: ns.GetName()}, svc)).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ep.GetName(), Namespace: ns.GetName()}, ep)).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: eps.GetName(), Namespace: ns.GetName()}, eps)).Should(Succeed())
|
||||
|
||||
By("checking additional labels on service", func() {
|
||||
for _, l := range tnt.Spec.ServicesMetadata.AdditionalLabels {
|
||||
Expect(svc.Labels).Should(ContainElement(l))
|
||||
@@ -185,15 +165,43 @@ var _ = Describe("creating a Service/Endpoint/EndpointSlice for a Tenant with ad
|
||||
Expect(ep.Annotations).Should(ContainElement(a))
|
||||
}
|
||||
})
|
||||
By("checking additional labels on endpointslices", func() {
|
||||
for _, l := range tnt.Spec.ServicesMetadata.AdditionalLabels {
|
||||
Expect(eps.Labels).Should(ContainElement(l))
|
||||
|
||||
epsName := "foo"
|
||||
epsPort := int32(9999)
|
||||
var eps client.Object
|
||||
|
||||
maj, min, _ := GetKubernetesSemVer()
|
||||
if maj == 1 && min > 16 {
|
||||
eps = &discoveryv1beta1.EndpointSlice{
|
||||
ObjectMeta: meta,
|
||||
AddressType: discoveryv1beta1.AddressTypeIPv4,
|
||||
Endpoints: []discoveryv1beta1.Endpoint{
|
||||
{
|
||||
Addresses: []string{"10.10.1.1"},
|
||||
},
|
||||
},
|
||||
Ports: []discoveryv1beta1.EndpointPort{
|
||||
{
|
||||
Name: &epsName,
|
||||
Port: &epsPort,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
By("checking additional annotations endpointslices", func() {
|
||||
for _, a := range tnt.Spec.NamespacesMetadata.AdditionalAnnotations {
|
||||
Expect(eps.Annotations).Should(ContainElement(a))
|
||||
}
|
||||
})
|
||||
Eventually(func() (err error) {
|
||||
_, err = cs.DiscoveryV1beta1().EndpointSlices(ns.GetName()).Create(context.TODO(), eps.(*discoveryv1beta1.EndpointSlice), metav1.CreateOptions{})
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: eps.GetName(), Namespace: ns.GetName()}, eps)).Should(Succeed())
|
||||
By("checking additional annotations endpointslices", func() {
|
||||
for _, a := range tnt.Spec.NamespacesMetadata.AdditionalAnnotations {
|
||||
Expect(eps.GetAnnotations()).Should(ContainElement(a))
|
||||
}
|
||||
})
|
||||
By("checking additional labels on endpointslices", func() {
|
||||
for _, l := range tnt.Spec.ServicesMetadata.AdditionalLabels {
|
||||
Expect(eps.GetLabels()).Should(ContainElement(l))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -20,6 +20,7 @@ package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -28,6 +29,8 @@ import (
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/clastix/capsule/api/v1alpha1"
|
||||
@@ -127,3 +130,22 @@ func GroupShouldBeUsedInTenantRoleBinding(ns *corev1.Namespace, t *v1alpha1.Tena
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func GetKubernetesSemVer() (major, minor int, ver string) {
|
||||
var v *version.Info
|
||||
var err error
|
||||
var cs kubernetes.Interface
|
||||
|
||||
cs, err = kubernetes.NewForConfig(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
v, err = cs.Discovery().ServerVersion()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
major, err = strconv.Atoi(v.Major)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
minor, err = strconv.Atoi(v.Minor)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ver = v.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -22,14 +22,18 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/clastix/capsule/api/v1alpha1"
|
||||
capsulewebhook "github.com/clastix/capsule/pkg/webhook"
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
discoveryv1alpha1 "k8s.io/api/discovery/v1alpha1"
|
||||
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
"github.com/clastix/capsule/api/v1alpha1"
|
||||
capsulewebhook "github.com/clastix/capsule/pkg/webhook"
|
||||
)
|
||||
|
||||
// +kubebuilder:webhook:path=/mutate-v1-service-labels,mutating=true,failurePolicy=ignore,groups="";discovery.k8s.io,resources=services;endpoints;endpointslices,verbs=create;update,versions=v1;v1beta1,name=service.labels.capsule.clastix.io
|
||||
@@ -102,11 +106,22 @@ func (h *handler) svcFromRequest(req admission.Request, decoder *admission.Decod
|
||||
}
|
||||
svc = Endpoints{ep}
|
||||
case "EndpointSlice":
|
||||
eps := &discoveryv1beta1.EndpointSlice{}
|
||||
if err := decoder.Decode(req, eps); err != nil {
|
||||
return nil, err
|
||||
var eps runtime.Object
|
||||
if v := req.Kind.Version; v == "v1beta1" {
|
||||
eps = &discoveryv1beta1.EndpointSlice{}
|
||||
if err := decoder.Decode(req, eps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else if v == "v1alpha1" {
|
||||
eps = &discoveryv1alpha1.EndpointSlice{}
|
||||
if err := decoder.Decode(req, eps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported EndpointSlice version: %s", v)
|
||||
}
|
||||
svc = EndpointSlice{eps}
|
||||
svc = EndpointSlice{eps.(metav1.Object)}
|
||||
default:
|
||||
err = fmt.Errorf("cannot recognize type %s", req.Kind.Kind)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ package service_labels
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type ServiceType interface {
|
||||
@@ -60,7 +60,7 @@ func (ep Endpoints) Annotations() map[string]string {
|
||||
}
|
||||
|
||||
type EndpointSlice struct {
|
||||
*discoveryv1beta1.EndpointSlice
|
||||
metav1.Object
|
||||
}
|
||||
|
||||
func (eps EndpointSlice) Namespace() string {
|
||||
|
||||
Reference in New Issue
Block a user