Files
capsule/pkg/ruleengine/validate_resources_test.go
Oliver BählerandGitHub 65a4e19e02 feat: requests and limit policies (#2095)
* chore: save progress

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

* feat: requests and limit policies

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

* feat: requests and limit policies

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

* feat: requests and limit policies

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

* feat: requests and limit policies

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

* feat: requests and limit policies

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

* feat: requests and limit policies

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

* feat: requests and limit policies

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

* feat: requests and limit policies

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

* feat: requests and limit policies

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

---------

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
2026-08-21 14:31:06 +02:00

146 lines
4.7 KiB
Go

// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package ruleengine
import (
"strings"
"testing"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/projectcapsule/capsule/pkg/api/rules"
)
func TestValidateRuleStatusBodyWorkloadResources(t *testing.T) {
t.Parallel()
tests := []struct {
name string
body *rules.NamespaceRuleBodyNamespace
wantErr string
}{
{
name: "valid",
body: workloadResourceRuleForValidation(
[]rules.WorkloadValidationTarget{rules.ValidateContainers},
rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyDefault, Value: validationQuantity("1Gi")},
rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
),
},
{
name: "omitted target permits container-only resource names",
body: workloadResourceRuleForValidationWithName(
nil,
corev1.ResourceEphemeralStorage,
rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
),
},
{
name: "ratio below one",
body: workloadResourceRuleForValidation(
nil,
rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("0.5")},
),
wantErr: "Ratio must be greater than or equal to 1",
},
{
name: "ratio missing value",
body: workloadResourceRuleForValidation(
nil,
rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio},
),
wantErr: "Ratio requires a value",
},
{
name: "unsupported target",
body: workloadResourceRuleForValidation(
[]rules.WorkloadValidationTarget{rules.ValidateEphemeralContainers},
rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
),
wantErr: "does not support resource policies",
},
{
name: "pod-level ephemeral storage",
body: workloadResourceRuleForValidation(
[]rules.WorkloadValidationTarget{rules.ValidatePod},
rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyPreserve},
rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
),
wantErr: "is not supported by pod-level resources",
},
{
name: "ratio request removed",
body: workloadResourceRuleForValidation(
nil,
rules.WorkloadResourceRequestPolicy{Policy: rules.WorkloadResourceRequestPolicyRemove},
rules.WorkloadResourceLimitPolicy{Policy: rules.WorkloadResourceLimitPolicyRatio, Value: validationQuantity("1.5")},
),
wantErr: "requires a request which is removed",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
err := ValidateRuleStatusBody(nil, []*rules.NamespaceRuleBodyNamespace{tt.body})
if tt.wantErr == "" {
if err != nil {
t.Fatalf("ValidateRuleStatusBody() error = %v", err)
}
return
}
if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
t.Fatalf("ValidateRuleStatusBody() error = %v, want containing %q", err, tt.wantErr)
}
})
}
}
func workloadResourceRuleForValidation(
targets []rules.WorkloadValidationTarget,
requestPolicy rules.WorkloadResourceRequestPolicy,
limitPolicy rules.WorkloadResourceLimitPolicy,
) *rules.NamespaceRuleBodyNamespace {
name := corev1.ResourceMemory
if len(targets) == 1 && targets[0] == rules.ValidatePod {
name = corev1.ResourceEphemeralStorage
}
return workloadResourceRuleForValidationWithName(targets, name, requestPolicy, limitPolicy)
}
func workloadResourceRuleForValidationWithName(
targets []rules.WorkloadValidationTarget,
name corev1.ResourceName,
requestPolicy rules.WorkloadResourceRequestPolicy,
limitPolicy rules.WorkloadResourceLimitPolicy,
) *rules.NamespaceRuleBodyNamespace {
return &rules.NamespaceRuleBodyNamespace{
Enforce: &rules.NamespaceRuleEnforceBody{
Workloads: rules.NamespaceRuleEnforceWorkloadsBody{
Targets: targets,
Resources: &rules.WorkloadResourceRules{
Requests: map[corev1.ResourceName]rules.WorkloadResourceRequestPolicy{name: requestPolicy},
Limits: map[corev1.ResourceName]rules.WorkloadResourceLimitPolicy{name: limitPolicy},
},
},
},
}
}
func validationQuantity(value string) *resource.Quantity {
quantity := resource.MustParse(value)
return &quantity
}