mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-19 20:46:42 +00:00
feat: upstream enterprise preview --------- Signed-off-by: Oliver Baehler <oliver@sudo-i.net> Co-authored-by: CorentinPtrl <pitrel.corentin@gmail.com>
180 lines
5.6 KiB
Go
180 lines
5.6 KiB
Go
package e2e
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
|
|
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
|
"github.com/projectcapsule/capsule/pkg/api/meta"
|
|
"github.com/projectcapsule/capsule/pkg/api/rbac"
|
|
corev1 "k8s.io/api/core/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
)
|
|
|
|
var _ = Describe("NamespaceStatus objects", Ordered, Label("tenant", "rules", "status"), func() {
|
|
ctx := context.Background()
|
|
|
|
// Two tenants, each with one owner (reuse your existing ownerClient/NamespaceCreation helpers)
|
|
tntA := &capsulev1beta2.Tenant{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "e2e-rule-status-a",
|
|
Labels: map[string]string{
|
|
"env": "e2e",
|
|
},
|
|
},
|
|
Spec: capsulev1beta2.TenantSpec{
|
|
Owners: rbac.OwnerListSpec{
|
|
{
|
|
CoreOwnerSpec: rbac.CoreOwnerSpec{
|
|
UserSpec: rbac.UserSpec{Name: "e2e-rule-status-a", Kind: "User"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
tntB := &capsulev1beta2.Tenant{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "e2e-rule-status-b",
|
|
Labels: map[string]string{
|
|
"env": "e2e",
|
|
},
|
|
},
|
|
Spec: capsulev1beta2.TenantSpec{
|
|
Owners: rbac.OwnerListSpec{
|
|
{
|
|
CoreOwnerSpec: rbac.CoreOwnerSpec{
|
|
UserSpec: rbac.UserSpec{Name: "e2e-rule-status-b", Kind: "User"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
var (
|
|
nsA1 *corev1.Namespace
|
|
nsA2 *corev1.Namespace
|
|
nsB1 *corev1.Namespace
|
|
)
|
|
|
|
JustBeforeEach(func() {
|
|
// Create tenants
|
|
EventuallyCreation(func() error {
|
|
tntA.ResourceVersion = ""
|
|
return k8sClient.Create(ctx, tntA)
|
|
}).Should(Succeed())
|
|
|
|
TenantReady(tntA, metav1.ConditionTrue, defaultTimeoutInterval)
|
|
|
|
EventuallyCreation(func() error {
|
|
tntB.ResourceVersion = ""
|
|
return k8sClient.Create(ctx, tntB)
|
|
}).Should(Succeed())
|
|
|
|
TenantReady(tntB, metav1.ConditionTrue, defaultTimeoutInterval)
|
|
|
|
nsA1 = NewNamespace("e2e-rule-status-a-1", map[string]string{
|
|
meta.TenantLabel: tntA.GetName(),
|
|
})
|
|
nsA2 = NewNamespace("e2e-rule-status-a-2", map[string]string{
|
|
meta.TenantLabel: tntA.GetName(),
|
|
})
|
|
nsB1 = NewNamespace("e2e-rule-status-b-1", map[string]string{
|
|
meta.TenantLabel: tntB.GetName(),
|
|
})
|
|
|
|
NamespaceCreation(nsA1, tntA.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
|
NamespaceCreation(nsA2, tntA.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
|
NamespaceCreation(nsB1, tntB.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
|
|
|
// Wait until tenants list their namespaces (optional but makes debugging easier)
|
|
NamespaceIsPartOfTenant(tntA, nsA1).Should(Succeed())
|
|
NamespaceIsPartOfTenant(tntA, nsA2).Should(Succeed())
|
|
NamespaceIsPartOfTenant(tntB, nsB1).Should(Succeed())
|
|
})
|
|
|
|
JustAfterEach(func() {
|
|
// Best-effort cleanup namespaces first (your env may already handle this)
|
|
for _, n := range []*corev1.Namespace{nsA1, nsA2, nsB1} {
|
|
if n == nil {
|
|
continue
|
|
}
|
|
_ = k8sClient.Delete(ctx, n)
|
|
}
|
|
|
|
// Delete tenants
|
|
if tntA != nil {
|
|
EventuallyDeletion(tntA)
|
|
}
|
|
if tntB != nil {
|
|
EventuallyDeletion(tntB)
|
|
}
|
|
})
|
|
|
|
expectNamespaceStatusFor := func(ns *corev1.Namespace, tenant *capsulev1beta2.Tenant) {
|
|
By(fmt.Sprintf("verifying NamespaceStatus for namespace %q (tenant=%q)", ns.Name, tenant.GetName()))
|
|
|
|
Eventually(func(g Gomega) {
|
|
// Re-read namespace to get UID reliably (in case local object is stale)
|
|
curNS := &corev1.Namespace{}
|
|
g.Expect(k8sClient.Get(ctx, client.ObjectKey{Name: ns.Name}, curNS)).To(Succeed())
|
|
|
|
nsStatus := &capsulev1beta2.RuleStatus{}
|
|
g.Expect(k8sClient.Get(ctx, client.ObjectKey{Name: meta.NameForManagedRuleStatus(), Namespace: ns.Name}, nsStatus)).To(Succeed())
|
|
|
|
// 2) OwnerReference must point to the Namespace and be controller owner
|
|
g.Expect(nsStatus.OwnerReferences).NotTo(BeEmpty())
|
|
|
|
var found bool
|
|
for _, or := range nsStatus.OwnerReferences {
|
|
if or.APIVersion == capsulev1beta2.GroupVersion.String() &&
|
|
or.Kind == "Tenant" &&
|
|
or.Name == tenant.Name &&
|
|
or.UID == tenant.UID {
|
|
|
|
found = true
|
|
|
|
break
|
|
}
|
|
}
|
|
g.Expect(found).To(BeTrue(), "expected NamespaceStatus to have Tenant controller OwnerReference")
|
|
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
|
}
|
|
|
|
It("creates one NamespaceStatus per namespace, with correct Status.Tenant and Namespace controller OwnerReference", func() {
|
|
getTenantA := &capsulev1beta2.Tenant{}
|
|
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: tntA.GetName()}, getTenantA)).To(Succeed())
|
|
|
|
getTenantB := &capsulev1beta2.Tenant{}
|
|
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: tntB.GetName()}, getTenantB)).To(Succeed())
|
|
|
|
expectNamespaceStatusFor(nsA1, getTenantA)
|
|
expectNamespaceStatusFor(nsA2, getTenantA)
|
|
expectNamespaceStatusFor(nsB1, getTenantB)
|
|
})
|
|
|
|
It("removes NamespaceStatus when the Namespace is deleted (ownerReference GC)", func() {
|
|
getTenantA := &capsulev1beta2.Tenant{}
|
|
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: tntA.GetName()}, getTenantA)).To(Succeed())
|
|
|
|
// Ensure it exists first
|
|
expectNamespaceStatusFor(nsA1, getTenantA)
|
|
|
|
// Delete namespace
|
|
Expect(k8sClient.Delete(ctx, nsA1)).To(Succeed())
|
|
|
|
// Namespace deletion can take time; once it's gone, the status should be GC'd
|
|
Eventually(func() bool {
|
|
// confirm namespace gone or terminating; either way, check status disappears eventually
|
|
nsStatus := &capsulev1beta2.RuleStatus{}
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: meta.NameForManagedRuleStatus(), Namespace: nsA1.Name}, nsStatus)
|
|
return apierrors.IsNotFound(err)
|
|
}, defaultTimeoutInterval, defaultPollInterval).Should(BeTrue())
|
|
})
|
|
})
|