Files
capsule/e2e/rules_managed_test.go
T
cc4fb45d70 feat: upstream enterprise preview (#1841)
feat: upstream enterprise preview

---------

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
Co-authored-by: CorentinPtrl <pitrel.corentin@gmail.com>
2026-05-28 00:58:58 +02:00

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())
})
})