Files
capsule/e2e/customquota_namespaced_test.go
Oliver BählerandGitHub 2252c530f4 feat(performance): removed duplicate client calls from all admission paths (#2054)
* chore

* perfromance improvements

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* perfromance improvements

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

---------

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
2026-07-28 14:32:42 +02:00

2306 lines
74 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/runtime"
"github.com/projectcapsule/capsule/pkg/runtime/quota"
"github.com/projectcapsule/capsule/pkg/runtime/selectors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes/scheme"
)
var _ = Describe("when CustomQuota uses ledger-backed reconciliation", Ordered, Label("namespaced", "namespacedcustomquota", "customquota", "ledger", "skip-on-openshift"), Ordered, func() {
const (
testNamespace = "custom-quota-e2e-test"
tenantLabel = "e2e.capsule.dev/test-suite"
tenantValue = "custom-quota-e2e"
)
var (
ctx context.Context
ns *corev1.Namespace
)
BeforeAll(func() {
ctx = context.Background()
utilruntime.Must(capsulev1beta2.AddToScheme(scheme.Scheme))
})
BeforeEach(func() {
ns = &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: testNamespace,
Labels: map[string]string{
tenantLabel: tenantValue,
"env": "e2e",
},
},
}
EventuallyCreation(func() error {
ns.ResourceVersion = ""
return k8sClient.Create(ctx, ns)
}).Should(Succeed())
})
AfterEach(func() {
// Delete quota coordination resources while the namespace still accepts
// updates. Namespace termination must not race ledger settlement.
quotaList := &capsulev1beta2.CustomQuotaList{}
if err := k8sClient.List(ctx, quotaList); err == nil {
for i := range quotaList.Items {
item := quotaList.Items[i]
if item.Name == "" {
continue
}
if item.Labels["e2e.capsule.dev/test-suite"] == "customquota-ledger" {
EventuallyDeletion(&item)
}
}
}
gquotaList := &capsulev1beta2.GlobalCustomQuotaList{}
if err := k8sClient.List(ctx, gquotaList); err == nil {
for i := range gquotaList.Items {
item := gquotaList.Items[i]
if item.Name == "" {
continue
}
if item.Labels["e2e.capsule.dev/test-suite"] == "customquota-ledger" {
EventuallyDeletion(&item)
}
}
}
ForceDeleteNamespace(ctx, testNamespace)
})
It("marks the quota not ready when an existing matching object has no value at the configured quantity path", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-missing-path-not-ready",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
},
},
}
pod := MakePod(testNamespace, "cq-missing-emptydir-size", nil, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
Eventually(func(g Gomega) {
obj := &capsulev1beta2.CustomQuota{}
g.Expect(k8sClient.Get(ctx, types.NamespacedName{
Name: q.GetName(),
Namespace: testNamespace,
}, obj)).To(Succeed())
cond := obj.Status.Conditions.GetConditionByType(meta.ReadyCondition)
g.Expect(cond).NotTo(BeNil())
g.Expect(cond.Status).To(Equal(metav1.ConditionFalse))
g.Expect(cond.Message).To(ContainSubstring("did not resolve to any value"))
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
})
It("denies creating a matching object when the configured quantity path resolves to no value", Label("skip-on-openshift"), func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-missing-path-deny-create",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
Eventually(func() error {
pod := MakePod(testNamespace, "cq-missing-path-denied", nil, nil, "nginx:1.27.0", "", "")
return k8sClient.Create(ctx, pod)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring("did not resolve to any value")),
)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
expectLedgerSettled(ctx, testNamespace, q.GetName())
})
It("remains consistent under concurrent pod creations for a CustomQuota", Label("skip-on-openshift"), func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-concurrent-pod-count",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
},
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
const total = 100
type result struct {
name string
err error
}
results := make(chan result, total)
for i := 0; i < total; i++ {
i := i
go func() {
name := fmt.Sprintf("cq-concurrent-pod-%02d", i)
pod := MakePod(
testNamespace,
name,
map[string]string{"track": "yes"},
nil,
"nginx:1.27.0",
"",
"",
)
err := k8sClient.Create(ctx, pod)
results <- result{name: name, err: err}
}()
}
var succeeded, failed int
for i := 0; i < total; i++ {
res := <-results
if res.err == nil {
succeeded++
} else {
failed++
}
}
Expect(succeeded).To(Equal(10))
Expect(failed).To(Equal(90))
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "10", 10)
expectLedgerSettled(ctx, testNamespace, q.GetName())
})
It("tracks different paths independently when global and namespaced quotas match the same pod gvk", Label("skip-on-openshift"), func() {
gq := &capsulev1beta2.GlobalCustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "gq-mixed-path-cpu-from-cq-suite",
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.GlobalCustomQuotaSpec{
CustomQuotaSpec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("500m"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.containers[*].resources.requests.cpu",
},
},
},
},
NamespaceSelectors: []selectors.NamespaceSelector{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
tenantLabel: tenantValue,
},
},
},
},
},
}
cq := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-mixed-path-emptydir-from-cq-suite",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("2Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, gq) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, cq) }).Should(Succeed())
awaitGlobalQuotaReady(ctx, gq.GetName())
awaitCustomQuotaReady(ctx, testNamespace, cq.GetName())
p1 := MakePod(testNamespace, "mixed-path-cq-suite-1", nil, nil, "nginx:1.27.0", "100m", "1Gi")
p2 := MakePod(testNamespace, "mixed-path-cq-suite-2", nil, nil, "nginx:1.27.0", "100m", "1Gi")
EventuallyCreation(func() error { p1.ResourceVersion = ""; return k8sClient.Create(ctx, p1) }).Should(Succeed())
EventuallyCreation(func() error { p2.ResourceVersion = ""; return k8sClient.Create(ctx, p2) }).Should(Succeed())
expectGlobalQuotaUsedAndClaims(ctx, gq.GetName(), "200m", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, cq.GetName(), "2Gi", 2)
expectLedgerSettled(ctx, ControllerNamespace, gq.GetName())
expectLedgerSettled(ctx, testNamespace, cq.GetName())
Eventually(func() error {
p3 := MakePod(testNamespace, "mixed-path-cq-suite-3", nil, nil, "nginx:1.27.0", "100m", "1Gi")
return k8sClient.Create(ctx, p3)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring(`CustomQuota "cq-mixed-path-emptydir-from-cq-suite"`)),
)
})
It("treats missing quantity paths as error", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-wrong-path-zero",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].doesNotExist.sizeLimit",
},
},
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "PersistentVolumeClaim",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.resources.requests.thisDoesNotExist",
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "wrong-path-pod", nil, nil, "nginx:1.27.0", "", "1Gi")
pvc := MakePVC(testNamespace, "wrong-path-pvc", "2Gi")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).ShouldNot(Succeed())
EventuallyCreation(func() error {
pvc.ResourceVersion = ""
return k8sClient.Create(ctx, pvc)
}).ShouldNot(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
expectLedgerSettled(ctx, testNamespace, q.GetName())
})
It("aggregates a custom pod quantity path and settles the corresponding ledger", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-cpu-requests",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("500m"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.containers[*].resources.requests.cpu",
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
},
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
dep := MakeDeployment(testNamespace, "cpu-requests", 2, map[string]string{
"track": "yes",
}, "100m")
EventuallyCreation(func() error {
dep.ResourceVersion = ""
return k8sClient.Create(ctx, dep)
}).Should(Succeed())
ExpectPodsForDeployment(ctx, testNamespace, "cpu-requests", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "200m", 2)
expectLedgerSettled(ctx, testNamespace, q.GetName())
ScaleDeployment(ctx, testNamespace, "cpu-requests", 4)
ExpectPodsForDeployment(ctx, testNamespace, "cpu-requests", 4)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "400m", 4)
expectLedgerSettled(ctx, testNamespace, q.GetName())
ledger := getLedger(ctx, testNamespace, q.GetName())
Expect(ledger.Spec.TargetRef.Kind).To(Equal("CustomQuota"))
Expect(ledger.Spec.TargetRef.Name).To(Equal(q.GetName()))
Expect(ledger.Spec.TargetRef.Namespace).To(Equal(testNamespace))
})
It("counts pods correctly while scaling a deployment", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-count",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("5"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
dep := MakeDeployment(testNamespace, "counted", 1, nil, "")
EventuallyCreation(func() error {
dep.ResourceVersion = ""
return k8sClient.Create(ctx, dep)
}).Should(Succeed())
ExpectPodsForDeployment(ctx, testNamespace, "counted", 1)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "1", 1)
ScaleDeployment(ctx, testNamespace, "counted", 3)
ExpectPodsForDeployment(ctx, testNamespace, "counted", 3)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "3", 3)
ScaleDeployment(ctx, testNamespace, "counted", 2)
ExpectPodsForDeployment(ctx, testNamespace, "counted", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "2", 2)
expectLedgerSettled(ctx, testNamespace, q.GetName())
})
It("tracks count with a single MatchLabels selector and updates when the pod no longer matches", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-count-single-matchlabel",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
},
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "single-matchlabel", map[string]string{"track": "yes"}, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "1", 1)
UpdatePodLabels(ctx, testNamespace, "single-matchlabel", map[string]string{"track": "no"})
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
})
It("tracks count with multiple MatchLabels and updates when the pod no longer matches", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-count-multi-matchlabel",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
"tier": "frontend",
},
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "multi-matchlabel", map[string]string{
"track": "yes",
"tier": "frontend",
}, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "1", 1)
UpdatePodLabels(ctx, testNamespace, "multi-matchlabel", map[string]string{
"track": "yes",
"tier": "backend",
})
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
})
It("tracks count with a single field selector and updates when the pod no longer matches", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-count-single-fieldselector",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
FieldSelectors: []string{
`.spec.containers[?(@.image=="nginx:1.27.0")]`,
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "single-fieldselector", nil, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "1", 1)
UpdatePodImage(ctx, testNamespace, "single-fieldselector", "nginx:1.26.0")
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
})
It("tracks count with multiple field selectors and updates when the pod no longer matches", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-count-multi-fieldselector",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
FieldSelectors: []string{
`.spec.containers[?(@.image=="nginx:1.27.0")]`,
`.spec.containers[?(@.name=="main")]`,
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "multi-fieldselector", nil, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "1", 1)
UpdatePodImage(ctx, testNamespace, "multi-fieldselector", "nginx:1.26.0")
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
})
It("calculates usage with CEL while combining JSONPath and CEL selectors", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-cel-usage-mixed-selectors",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("500m"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
CEL: `object.spec.containers` +
`.map(c, quantity(c.resources.requests["cpu"]))`,
Selectors: []selectors.SelectorWithFields{
{
FieldSelectors: []string{
".spec.restartPolicy=Always",
},
CELExpressions: []string{
`object.spec.containers.exists(c, c.image == "nginx:1.27.0")`,
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
matching := MakePod(
testNamespace,
"cel-usage-matching",
nil,
nil,
"nginx:1.27.0",
"100m",
"",
)
EventuallyCreation(func() error {
matching.ResourceVersion = ""
return k8sClient.Create(ctx, matching)
}).Should(Succeed())
ignored := MakePod(
testNamespace,
"cel-usage-ignored",
nil,
nil,
"nginx:1.26.0",
"200m",
"",
)
EventuallyCreation(func() error {
ignored.ResourceVersion = ""
return k8sClient.Create(ctx, ignored)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "100m", 1)
expectLedgerSettled(ctx, testNamespace, q.GetName())
UpdatePodImage(ctx, testNamespace, "cel-usage-matching", "nginx:1.26.0")
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
})
It("aggregates multiple sources across pod emptyDir size and pvc storage size", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-multi-source-storage",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("3Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "PersistentVolumeClaim",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.resources.requests.storage",
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "multi-source-pod", nil, nil, "nginx:1.27.0", "", "1Gi")
pvc := MakePVC(testNamespace, "multi-source-pvc", "2Gi")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
EventuallyCreation(func() error {
pvc.ResourceVersion = ""
return k8sClient.Create(ctx, pvc)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "3Gi", 2)
expectLedgerSettled(ctx, testNamespace, q.GetName())
})
It("aggregates multiple sources with selectors across pod emptyDir and pvc storage", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-multi-source-selectors-storage",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Path: ".spec.volumes[*].emptyDir.sizeLimit",
Operation: quota.OpAdd,
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
},
},
},
},
},
},
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "PersistentVolumeClaim",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Path: ".spec.resources.requests.storage",
Operation: quota.OpAdd,
Selectors: []selectors.SelectorWithFields{
{
FieldSelectors: []string{
`.spec.accessModes[?(@=="ReadWriteOnce")]`,
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
matchingPod := MakePod(testNamespace, "matching-emptydir", map[string]string{"track": "yes"}, nil, "nginx:1.27.0", "", "1Gi")
nonMatchingPod := MakePod(testNamespace, "ignored-emptydir", map[string]string{"track": "no"}, nil, "nginx:1.27.0", "", "5Gi")
matchingPVC := MakePVC(testNamespace, "matching-pvc", "2Gi")
nonMatchingPVC := MakePVC(testNamespace, "ignored-pvc", "4Gi")
nonMatchingPVC.Spec.AccessModes = []corev1.PersistentVolumeAccessMode{corev1.ReadOnlyMany}
EventuallyCreation(func() error {
matchingPod.ResourceVersion = ""
return k8sClient.Create(ctx, matchingPod)
}).Should(Succeed())
EventuallyCreation(func() error {
nonMatchingPod.ResourceVersion = ""
return k8sClient.Create(ctx, nonMatchingPod)
}).Should(Succeed())
EventuallyCreation(func() error {
matchingPVC.ResourceVersion = ""
return k8sClient.Create(ctx, matchingPVC)
}).Should(Succeed())
EventuallyCreation(func() error {
nonMatchingPVC.ResourceVersion = ""
return k8sClient.Create(ctx, nonMatchingPVC)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "3Gi", 2)
expectLedgerSettled(ctx, testNamespace, q.GetName())
})
It("clamps usage to zero for a pure subtraction source", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-sub-only-clamps-zero",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "PersistentVolumeClaim",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpSub,
Path: ".spec.resources.requests.storage",
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pvc := MakePVC(testNamespace, "sub-only-pvc", "2Gi")
EventuallyCreation(func() error {
pvc.ResourceVersion = ""
return k8sClient.Create(ctx, pvc)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 1)
expectLedgerSettled(ctx, testNamespace, q.GetName())
})
It("subtracts matching pvc storage from added pod emptyDir storage", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-add-sub-storage",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "PersistentVolumeClaim",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpSub,
Path: ".spec.resources.requests.storage",
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "add-sub-pod", nil, nil, "nginx:1.27.0", "", "3Gi")
pvc := MakePVC(testNamespace, "add-sub-pvc", "1Gi")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
EventuallyCreation(func() error {
pvc.ResourceVersion = ""
return k8sClient.Create(ctx, pvc)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "2Gi", 2)
expectLedgerSettled(ctx, testNamespace, q.GetName())
})
It("clamps mixed add and subtraction result to zero when subtraction exceeds additions", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-add-sub-clamp-zero",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "PersistentVolumeClaim",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpSub,
Path: ".spec.resources.requests.storage",
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "add-sub-clamp-pod", nil, nil, "nginx:1.27.0", "", "1Gi")
pvc := MakePVC(testNamespace, "add-sub-clamp-pvc", "2Gi")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
EventuallyCreation(func() error {
pvc.ResourceVersion = ""
return k8sClient.Create(ctx, pvc)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 2)
expectLedgerSettled(ctx, testNamespace, q.GetName())
})
It("supports subtraction with label selectors and removes the subtraction when the object no longer matches", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-sub-label-selector",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "PersistentVolumeClaim",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpSub,
Path: ".spec.resources.requests.storage",
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"discount": "yes",
},
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "sub-label-pod", nil, nil, "nginx:1.27.0", "", "3Gi")
pvc := MakePVC(testNamespace, "sub-label-pvc", "1Gi")
pvc.Labels = map[string]string{"discount": "yes"}
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
EventuallyCreation(func() error {
pvc.ResourceVersion = ""
return k8sClient.Create(ctx, pvc)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "2Gi", 2)
expectLedgerSettled(ctx, testNamespace, q.GetName())
Eventually(func() error {
obj := &corev1.PersistentVolumeClaim{}
if err := k8sClient.Get(ctx, types.NamespacedName{Name: pvc.Name, Namespace: pvc.Namespace}, obj); err != nil {
return err
}
obj.Labels = map[string]string{"discount": "no"}
return k8sClient.Update(ctx, obj)
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "3Gi", 1)
})
It("reconciles subtraction correctly when the subtracting resource is deleted", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-sub-delete-reconcile",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "PersistentVolumeClaim",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpSub,
Path: ".spec.resources.requests.storage",
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "sub-delete-pod", nil, nil, "nginx:1.27.0", "", "3Gi")
pvc := MakePVC(testNamespace, "sub-delete-pvc", "1Gi")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
EventuallyCreation(func() error {
pvc.ResourceVersion = ""
return k8sClient.Create(ctx, pvc)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "2Gi", 2)
expectLedgerSettled(ctx, testNamespace, q.GetName())
EventuallyDeletion(pvc)
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "3Gi", 1)
})
It("uses the smallest matching custom quota as authoritative while accounting successful pod count in both quotas", func() {
small := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-count-small",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("2"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
},
},
},
},
}
large := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-count-large",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("5"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, small) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, large) }).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, small.GetName())
awaitCustomQuotaReady(ctx, testNamespace, large.GetName())
pod1 := MakePod(testNamespace, "multi-cq-count-1", nil, nil, "nginx:1.27.0", "", "")
pod2 := MakePod(testNamespace, "multi-cq-count-2", nil, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error {
pod1.ResourceVersion = ""
return k8sClient.Create(ctx, pod1)
}).Should(Succeed())
EventuallyCreation(func() error {
pod2.ResourceVersion = ""
return k8sClient.Create(ctx, pod2)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, small.GetName(), "2", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, large.GetName(), "2", 2)
expectLedgerSettled(ctx, testNamespace, small.GetName())
expectLedgerSettled(ctx, testNamespace, large.GetName())
Eventually(func() error {
pod3 := MakePod(testNamespace, "multi-cq-count-3", nil, nil, "nginx:1.27.0", "", "")
return k8sClient.Create(ctx, pod3)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring(`CustomQuota "cq-pod-count-small"`)),
)
})
It("uses the smallest matching custom quota as authoritative while accounting successful cpu usage in both quotas", func() {
small := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-cpu-small",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("200m"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.containers[*].resources.requests.cpu",
},
},
},
},
}
large := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-cpu-large",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("500m"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.containers[*].resources.requests.cpu",
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, small) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, large) }).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, small.GetName())
awaitCustomQuotaReady(ctx, testNamespace, large.GetName())
pod1 := MakePod(testNamespace, "multi-cq-cpu-1", nil, nil, "nginx:1.27.0", "100m", "")
pod2 := MakePod(testNamespace, "multi-cq-cpu-2", nil, nil, "nginx:1.27.0", "100m", "")
EventuallyCreation(func() error {
pod1.ResourceVersion = ""
return k8sClient.Create(ctx, pod1)
}).Should(Succeed())
EventuallyCreation(func() error {
pod2.ResourceVersion = ""
return k8sClient.Create(ctx, pod2)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, small.GetName(), "200m", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, large.GetName(), "200m", 2)
expectLedgerSettled(ctx, testNamespace, small.GetName())
expectLedgerSettled(ctx, testNamespace, large.GetName())
Eventually(func() error {
pod3 := MakePod(testNamespace, "multi-cq-cpu-3", nil, nil, "nginx:1.27.0", "100m", "")
return k8sClient.Create(ctx, pod3)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring(`CustomQuota "cq-pod-cpu-small"`)),
)
})
It("accounts only the matching subset for overlapping selectors on the same pod gvk", func() {
broad := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-track-broad",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("5"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
},
},
},
},
},
},
},
},
}
narrow := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-track-frontend",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("2"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
"tier": "frontend",
},
},
},
},
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, broad) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, narrow) }).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, broad.GetName())
awaitCustomQuotaReady(ctx, testNamespace, narrow.GetName())
p1 := MakePod(testNamespace, "cq-overlap-1", map[string]string{"track": "yes", "tier": "frontend"}, nil, "nginx:1.27.0", "", "")
p2 := MakePod(testNamespace, "cq-overlap-2", map[string]string{"track": "yes", "tier": "frontend"}, nil, "nginx:1.27.0", "", "")
p3 := MakePod(testNamespace, "cq-overlap-3", map[string]string{"track": "yes", "tier": "backend"}, nil, "nginx:1.27.0", "", "")
p4 := MakePod(testNamespace, "cq-overlap-4", map[string]string{"track": "yes", "tier": "backend"}, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error { p1.ResourceVersion = ""; return k8sClient.Create(ctx, p1) }).Should(Succeed())
EventuallyCreation(func() error { p2.ResourceVersion = ""; return k8sClient.Create(ctx, p2) }).Should(Succeed())
EventuallyCreation(func() error { p3.ResourceVersion = ""; return k8sClient.Create(ctx, p3) }).Should(Succeed())
EventuallyCreation(func() error { p4.ResourceVersion = ""; return k8sClient.Create(ctx, p4) }).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, broad.GetName(), "4", 4)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, narrow.GetName(), "2", 2)
expectLedgerSettled(ctx, testNamespace, broad.GetName())
expectLedgerSettled(ctx, testNamespace, narrow.GetName())
Eventually(func() error {
p5 := MakePod(testNamespace, "cq-overlap-5", map[string]string{"track": "yes", "tier": "frontend"}, nil, "nginx:1.27.0", "", "")
return k8sClient.Create(ctx, p5)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring(`CustomQuota "cq-pod-track-frontend"`)),
)
EventuallyCreation(func() error {
p6 := MakePod(testNamespace, "cq-overlap-6", map[string]string{"track": "yes", "tier": "backend"}, nil, "nginx:1.27.0", "", "")
return k8sClient.Create(ctx, p6)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, broad.GetName(), "5", 5)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, narrow.GetName(), "2", 2)
})
It("tracks different paths independently when multiple custom quotas match the same pod gvk", func() {
cpuQuota := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-path-cpu",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("400m"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.containers[*].resources.requests.cpu",
},
},
},
},
}
emptyDirQuota := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-pod-path-emptydir",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("2Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, cpuQuota) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, emptyDirQuota) }).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, cpuQuota.GetName())
awaitCustomQuotaReady(ctx, testNamespace, emptyDirQuota.GetName())
p1 := MakePod(testNamespace, "cq-path-pod-1", nil, nil, "nginx:1.27.0", "100m", "1Gi")
p2 := MakePod(testNamespace, "cq-path-pod-2", nil, nil, "nginx:1.27.0", "100m", "1Gi")
EventuallyCreation(func() error { p1.ResourceVersion = ""; return k8sClient.Create(ctx, p1) }).Should(Succeed())
EventuallyCreation(func() error { p2.ResourceVersion = ""; return k8sClient.Create(ctx, p2) }).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, cpuQuota.GetName(), "200m", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, emptyDirQuota.GetName(), "2Gi", 2)
expectLedgerSettled(ctx, testNamespace, cpuQuota.GetName())
expectLedgerSettled(ctx, testNamespace, emptyDirQuota.GetName())
Eventually(func() error {
p3 := MakePod(testNamespace, "cq-path-pod-3", nil, nil, "nginx:1.27.0", "100m", "1Gi")
return k8sClient.Create(ctx, p3)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring(`CustomQuota "cq-pod-path-emptydir"`)),
)
})
It("accounts deployment scaling in multiple custom quotas and denies when the smaller quota is exceeded", func() {
small := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-scale-small",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("3"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
},
},
},
},
}
large := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-scale-large",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, small) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, large) }).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, small.GetName())
awaitCustomQuotaReady(ctx, testNamespace, large.GetName())
dep := MakeDeployment(testNamespace, "cq-scale", 1, nil, "")
EventuallyCreation(func() error {
dep.ResourceVersion = ""
return k8sClient.Create(ctx, dep)
}).Should(Succeed())
ExpectPodsForDeployment(ctx, testNamespace, "cq-scale", 1)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, small.GetName(), "1", 1)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, large.GetName(), "1", 1)
ScaleDeployment(ctx, testNamespace, "cq-scale", 3)
ExpectPodsForDeployment(ctx, testNamespace, "cq-scale", 3)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, small.GetName(), "3", 3)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, large.GetName(), "3", 3)
ScaleDeployment(ctx, testNamespace, "cq-scale", 4)
Eventually(func(g Gomega) {
obj := &capsulev1beta2.CustomQuota{}
g.Expect(k8sClient.Get(ctx, types.NamespacedName{
Name: small.GetName(),
Namespace: testNamespace,
}, obj)).To(Succeed())
g.Expect(obj.Status.Usage.Used.Cmp(resource.MustParse("3"))).To(Equal(0))
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
ExpectPodsForDeployment(ctx, testNamespace, "cq-scale", 3)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, large.GetName(), "3", 3)
})
It("uses the smallest matching quota as authoritative while accounting successful pod count in both global and namespaced quotas", func() {
gq := &capsulev1beta2.GlobalCustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "gq-mixed-pod-count-from-cq-suite",
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.GlobalCustomQuotaSpec{
CustomQuotaSpec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("5"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
},
},
},
},
NamespaceSelectors: []selectors.NamespaceSelector{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
tenantLabel: tenantValue,
},
},
},
},
},
}
cq := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-mixed-pod-count-from-cq-suite",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("2"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, gq) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, cq) }).Should(Succeed())
awaitGlobalQuotaReady(ctx, gq.GetName())
awaitCustomQuotaReady(ctx, testNamespace, cq.GetName())
p1 := MakePod(testNamespace, "mixed-cq-suite-1", nil, nil, "nginx:1.27.0", "", "")
p2 := MakePod(testNamespace, "mixed-cq-suite-2", nil, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error { p1.ResourceVersion = ""; return k8sClient.Create(ctx, p1) }).Should(Succeed())
EventuallyCreation(func() error { p2.ResourceVersion = ""; return k8sClient.Create(ctx, p2) }).Should(Succeed())
expectGlobalQuotaUsedAndClaims(ctx, gq.GetName(), "2", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, cq.GetName(), "2", 2)
expectLedgerSettled(ctx, ControllerNamespace, gq.GetName())
expectLedgerSettled(ctx, testNamespace, cq.GetName())
Eventually(func() error {
p3 := MakePod(testNamespace, "mixed-cq-suite-3", nil, nil, "nginx:1.27.0", "", "")
return k8sClient.Create(ctx, p3)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring(`CustomQuota "cq-mixed-pod-count-from-cq-suite"`)),
)
})
It("uses the smallest matching quota as authoritative while accounting successful cpu usage in both global and namespaced quotas", func() {
gq := &capsulev1beta2.GlobalCustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "gq-mixed-pod-cpu-from-cq-suite",
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.GlobalCustomQuotaSpec{
CustomQuotaSpec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("500m"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.containers[*].resources.requests.cpu",
},
},
},
},
NamespaceSelectors: []selectors.NamespaceSelector{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
tenantLabel: tenantValue,
},
},
},
},
},
}
cq := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-mixed-pod-cpu-from-cq-suite",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("200m"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.containers[*].resources.requests.cpu",
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, gq) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, cq) }).Should(Succeed())
awaitGlobalQuotaReady(ctx, gq.GetName())
awaitCustomQuotaReady(ctx, testNamespace, cq.GetName())
p1 := MakePod(testNamespace, "mixed-cpu-cq-suite-1", nil, nil, "nginx:1.27.0", "100m", "")
p2 := MakePod(testNamespace, "mixed-cpu-cq-suite-2", nil, nil, "nginx:1.27.0", "100m", "")
EventuallyCreation(func() error { p1.ResourceVersion = ""; return k8sClient.Create(ctx, p1) }).Should(Succeed())
EventuallyCreation(func() error { p2.ResourceVersion = ""; return k8sClient.Create(ctx, p2) }).Should(Succeed())
expectGlobalQuotaUsedAndClaims(ctx, gq.GetName(), "200m", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, cq.GetName(), "200m", 2)
expectLedgerSettled(ctx, ControllerNamespace, gq.GetName())
expectLedgerSettled(ctx, testNamespace, cq.GetName())
Eventually(func() error {
p3 := MakePod(testNamespace, "mixed-cpu-cq-suite-3", nil, nil, "nginx:1.27.0", "100m", "")
return k8sClient.Create(ctx, p3)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring(`CustomQuota "cq-mixed-pod-cpu-from-cq-suite"`)),
)
})
It("reconciles multiple sources when objects are deleted", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-multi-source-reconcile",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
},
},
},
},
},
},
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "PersistentVolumeClaim",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.resources.requests.storage",
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "reconcile-emptydir", map[string]string{"track": "yes"}, nil, "nginx:1.27.0", "", "1Gi")
pvc := MakePVC(testNamespace, "reconcile-pvc", "2Gi")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
EventuallyCreation(func() error {
pvc.ResourceVersion = ""
return k8sClient.Create(ctx, pvc)
}).Should(Succeed())
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "3Gi", 2)
EventuallyDeletion(pod)
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "2Gi", 1)
EventuallyDeletion(pvc)
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
})
It("does not produce negative usage when a matching pod is relabeled to no longer match", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-no-negative-on-relabel",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
},
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "no-negative-on-relabel", map[string]string{"track": "yes"}, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "1", 1)
UpdatePodLabels(ctx, testNamespace, "no-negative-on-relabel", map[string]string{"track": "no"})
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
})
It("retracts emptyDir usage when a pod no longer matches source selectors", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-emptydir-relabel",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
},
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
pod := MakePod(testNamespace, "emptydir-relabel", map[string]string{"track": "yes"}, nil, "nginx:1.27.0", "", "1Gi")
EventuallyCreation(func() error {
pod.ResourceVersion = ""
return k8sClient.Create(ctx, pod)
}).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "1Gi", 1)
UpdatePodLabels(ctx, testNamespace, "emptydir-relabel", map[string]string{"track": "no"})
expectLedgerSettled(ctx, testNamespace, q.GetName())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, q.GetName(), "0", 0)
})
It("accounts only the quotas that actually match when multiple custom quotas share the same gvk", func() {
labelQuota := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-track-only",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"track": "yes",
},
},
},
},
},
},
},
},
}
fieldQuota := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-nginx-only",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
FieldSelectors: []string{
`.spec.containers[?(@.image=="nginx:1.27.0")]`,
},
},
},
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, labelQuota) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, fieldQuota) }).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, labelQuota.GetName())
awaitCustomQuotaReady(ctx, testNamespace, fieldQuota.GetName())
matchBoth := MakePod(testNamespace, "subset-both", map[string]string{"track": "yes"}, nil, "nginx:1.27.0", "", "")
matchLabelOnly := MakePod(testNamespace, "subset-label-only", map[string]string{"track": "yes"}, nil, "busybox:1.36.1", "", "")
matchFieldOnly := MakePod(testNamespace, "subset-field-only", nil, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error { matchBoth.ResourceVersion = ""; return k8sClient.Create(ctx, matchBoth) }).Should(Succeed())
EventuallyCreation(func() error { matchLabelOnly.ResourceVersion = ""; return k8sClient.Create(ctx, matchLabelOnly) }).Should(Succeed())
EventuallyCreation(func() error { matchFieldOnly.ResourceVersion = ""; return k8sClient.Create(ctx, matchFieldOnly) }).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, labelQuota.GetName(), "2", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, fieldQuota.GetName(), "2", 2)
expectLedgerSettled(ctx, testNamespace, labelQuota.GetName())
expectLedgerSettled(ctx, testNamespace, fieldQuota.GetName())
})
It("uses deterministic tie-breaking when multiple custom quotas have the same remaining availability", func() {
quotaA := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-tie-a",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("3"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
},
},
},
},
}
quotaB := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-tie-b",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("4"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, quotaA) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, quotaB) }).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, quotaA.GetName())
awaitCustomQuotaReady(ctx, testNamespace, quotaB.GetName())
p1 := MakePod(testNamespace, "tie-1", nil, nil, "nginx:1.27.0", "", "")
p2 := MakePod(testNamespace, "tie-2", nil, nil, "nginx:1.27.0", "", "")
p3 := MakePod(testNamespace, "tie-3", nil, nil, "nginx:1.27.0", "", "")
EventuallyCreation(func() error { p1.ResourceVersion = ""; return k8sClient.Create(ctx, p1) }).Should(Succeed())
EventuallyCreation(func() error { p2.ResourceVersion = ""; return k8sClient.Create(ctx, p2) }).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, quotaA.GetName(), "2", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, quotaB.GetName(), "2", 2)
EventuallyCreation(func() error { p3.ResourceVersion = ""; return k8sClient.Create(ctx, p3) }).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, quotaA.GetName(), "3", 3)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, quotaB.GetName(), "3", 3)
Eventually(func() error {
p4 := MakePod(testNamespace, "tie-4", nil, nil, "nginx:1.27.0", "", "")
return k8sClient.Create(ctx, p4)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring(`CustomQuota "cq-tie-a"`)),
)
})
It("aggregates the same successful pod into multiple custom quotas with different paths on the same gvk", func() {
cpuQuota := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-multi-path-cpu",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("300m"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.containers[*].resources.requests.cpu",
},
},
},
},
}
emptyDirQuota := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-multi-path-emptydir",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("3Gi"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpAdd,
Path: ".spec.volumes[*].emptyDir.sizeLimit",
},
},
},
},
}
EventuallyCreation(func() error { return k8sClient.Create(ctx, cpuQuota) }).Should(Succeed())
EventuallyCreation(func() error { return k8sClient.Create(ctx, emptyDirQuota) }).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, cpuQuota.GetName())
awaitCustomQuotaReady(ctx, testNamespace, emptyDirQuota.GetName())
p1 := MakePod(testNamespace, "multi-path-shared-1", nil, nil, "nginx:1.27.0", "100m", "1Gi")
p2 := MakePod(testNamespace, "multi-path-shared-2", nil, nil, "nginx:1.27.0", "100m", "1Gi")
EventuallyCreation(func() error { p1.ResourceVersion = ""; return k8sClient.Create(ctx, p1) }).Should(Succeed())
EventuallyCreation(func() error { p2.ResourceVersion = ""; return k8sClient.Create(ctx, p2) }).Should(Succeed())
expectCustomQuotaUsedAndClaims(ctx, testNamespace, cpuQuota.GetName(), "200m", 2)
expectCustomQuotaUsedAndClaims(ctx, testNamespace, emptyDirQuota.GetName(), "2Gi", 2)
expectLedgerSettled(ctx, testNamespace, cpuQuota.GetName())
expectLedgerSettled(ctx, testNamespace, emptyDirQuota.GetName())
})
It("rejects admission when a field selector uses an invalid jsonpath filter on a scalar", func() {
q := &capsulev1beta2.CustomQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "cq-invalid-fieldselector",
Namespace: testNamespace,
Labels: map[string]string{
"e2e.capsule.dev/test-suite": "customquota-ledger",
},
},
Spec: capsulev1beta2.CustomQuotaSpec{
Limit: resource.MustParse("10"),
Sources: []capsulev1beta2.CustomQuotaSpecSource{
{
VersionKind: runtime.VersionKind{
APIVersion: "v1",
Kind: "Pod",
},
CustomQuotaSpecSourceConfig: capsulev1beta2.CustomQuotaSpecSourceConfig{
Operation: quota.OpCount,
Selectors: []selectors.SelectorWithFields{
{
FieldSelectors: []string{
`.spec.restartPolicy[?(@=="Always")]`,
},
},
},
},
},
},
},
}
EventuallyCreation(func() error {
return k8sClient.Create(ctx, q)
}).Should(Succeed())
awaitCustomQuotaReady(ctx, testNamespace, q.GetName())
Eventually(func() error {
pod := MakePod(testNamespace, "invalid-selector-pod", nil, nil, "nginx:1.27.0", "", "")
return k8sClient.Create(ctx, pod)
}, defaultTimeoutInterval, defaultPollInterval).Should(
MatchError(ContainSubstring("is not array or slice and cannot be filtered")),
)
})
})