Files
capsule/e2e/rules_managed_test.go
Oliver Bähler a6b830b1af feat: add ruleset api(#1844)
* fix(controller): decode old object for delete requests

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(config): remove usergroups default

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(config): remove usergroups default

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* sec(ghsa-2ww6-hf35-mfjm): intercept namespace subresource

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(api): add rulestatus api

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: conflicts

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(api): add rulestatus api

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(api): add rulestatus api

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(api): add rulestatus api

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(api): add rulestatus api

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(api): add rulestatus api

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(api): add rulestatus api

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2026-01-27 14:28:48 +01:00

159 lines
4.9 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"
"github.com/projectcapsule/capsule/pkg/api/meta"
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", Label("tenant", "rules"), func() {
ctx := context.Background()
// Two tenants, each with one owner (reuse your existing ownerClient/NamespaceCreation helpers)
tntA := &capsulev1beta2.Tenant{
ObjectMeta: metav1.ObjectMeta{Name: "nsstatus-a"},
Spec: capsulev1beta2.TenantSpec{
Owners: api.OwnerListSpec{
{
CoreOwnerSpec: api.CoreOwnerSpec{
UserSpec: api.UserSpec{Name: "matt", Kind: "User"},
},
},
},
},
}
tntB := &capsulev1beta2.Tenant{
ObjectMeta: metav1.ObjectMeta{Name: "nsstatus-b"},
Spec: capsulev1beta2.TenantSpec{
Owners: api.OwnerListSpec{
{
CoreOwnerSpec: api.CoreOwnerSpec{
UserSpec: api.UserSpec{Name: "matt", 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())
EventuallyCreation(func() error {
tntB.ResourceVersion = ""
return k8sClient.Create(ctx, tntB)
}).Should(Succeed())
// Create namespaces for each tenant using your helper
nsA1 = NewNamespace("rule-status-ns1", map[string]string{
meta.TenantLabel: tntA.GetName(),
})
nsA2 = NewNamespace("rule-status-ns2", map[string]string{
meta.TenantLabel: tntA.GetName(),
})
nsB1 = NewNamespace("rule-status-ns3", 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)
TenantNamespaceList(tntA, defaultTimeoutInterval).Should(ContainElements(nsA1.GetName(), nsA2.GetName()))
TenantNamespaceList(tntB, defaultTimeoutInterval).Should(ContainElement(nsB1.GetName()))
})
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 {
_ = k8sClient.Delete(ctx, tntA)
}
if tntB != nil {
_ = k8sClient.Delete(ctx, tntB)
}
})
// --- Helpers ---
expectNamespaceStatusFor := func(ns *corev1.Namespace, tenantName string) {
By(fmt.Sprintf("verifying NamespaceStatus for namespace %q (tenant=%q)", ns.Name, tenantName))
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 == "v1" &&
or.Kind == "Namespace" &&
or.Name == curNS.Name &&
or.UID == curNS.UID {
found = true
break
}
}
g.Expect(found).To(BeTrue(), "expected NamespaceStatus to have Namespace controller OwnerReference")
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
}
It("creates one NamespaceStatus per namespace, with correct Status.Tenant and Namespace controller OwnerReference", func() {
expectNamespaceStatusFor(nsA1, tntA.Name)
expectNamespaceStatusFor(nsA2, tntA.Name)
expectNamespaceStatusFor(nsB1, tntB.Name)
})
It("removes NamespaceStatus when the Namespace is deleted (ownerReference GC)", func() {
// Ensure it exists first
expectNamespaceStatusFor(nsA1, tntA.Name)
// 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())
})
})