mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-25 16:07:24 +00:00
feat: add globalresourcequota api (#2068)
* feat: add globalresourcequota api Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
This commit is contained in:
@@ -21,6 +21,8 @@ func ValidateRuleStatusBody(
|
||||
mapper k8smeta.RESTMapper,
|
||||
bodies []*rules.NamespaceRuleBodyNamespace,
|
||||
) error {
|
||||
quotaNames := make(map[string]string)
|
||||
|
||||
for i, rule := range bodies {
|
||||
if rule == nil {
|
||||
continue
|
||||
@@ -30,6 +32,10 @@ func ValidateRuleStatusBody(
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateQuotaRules(i, rule.Quota, quotaNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rule.Enforce == nil {
|
||||
continue
|
||||
}
|
||||
@@ -54,6 +60,43 @@ func ValidateRuleStatusBody(
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateQuotaRules(ruleIndex int, quotas []rules.ResourceQuotaRule, names map[string]string) error {
|
||||
for quotaIndex, quota := range quotas {
|
||||
path := fmt.Sprintf("rules[%d].quota[%d]", ruleIndex, quotaIndex)
|
||||
if errs := k8svalidation.IsDNS1123Label(quota.Name); len(errs) > 0 {
|
||||
return fmt.Errorf("%s.name %q is invalid: %s", path, quota.Name, strings.Join(errs, "; "))
|
||||
}
|
||||
|
||||
if previous, found := names[quota.Name]; found {
|
||||
return fmt.Errorf(
|
||||
"%s.name %q is invalid: quota name is already used by %s",
|
||||
path,
|
||||
quota.Name,
|
||||
previous,
|
||||
)
|
||||
}
|
||||
|
||||
names[quota.Name] = path
|
||||
|
||||
if len(quota.Hard) == 0 {
|
||||
return fmt.Errorf("%s.hard is invalid: at least one resource is required", path)
|
||||
}
|
||||
|
||||
for name, quantity := range quota.Hard {
|
||||
if quantity.Sign() < 0 {
|
||||
return fmt.Errorf(
|
||||
"rules[%d].quota[%d].hard[%q] is invalid: quantity must not be negative",
|
||||
ruleIndex,
|
||||
quotaIndex,
|
||||
name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateIngressRules(
|
||||
ruleIndex int,
|
||||
ingress rules.NamespaceRuleEnforceIngressBody,
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
@@ -37,6 +39,73 @@ func TestValidateRuleStatusBody(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid quota-only rule",
|
||||
mapper: mapper,
|
||||
bodies: []*rules.NamespaceRuleBodyNamespace{{
|
||||
Quota: []rules.ResourceQuotaRule{{
|
||||
Name: "shared-compute",
|
||||
ResourceQuotaSpec: corev1.ResourceQuotaSpec{Hard: corev1.ResourceList{
|
||||
corev1.ResourceRequestsCPU: resource.MustParse("8"),
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "quota name is required",
|
||||
mapper: mapper,
|
||||
bodies: []*rules.NamespaceRuleBodyNamespace{{
|
||||
Quota: []rules.ResourceQuotaRule{{
|
||||
ResourceQuotaSpec: corev1.ResourceQuotaSpec{Hard: corev1.ResourceList{
|
||||
corev1.ResourceRequestsCPU: resource.MustParse("8"),
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
wantErr: "rules[0].quota[0].name",
|
||||
},
|
||||
{
|
||||
name: "quota name must be a DNS label",
|
||||
mapper: mapper,
|
||||
bodies: []*rules.NamespaceRuleBodyNamespace{{
|
||||
Quota: []rules.ResourceQuotaRule{{
|
||||
Name: "Shared_Compute",
|
||||
ResourceQuotaSpec: corev1.ResourceQuotaSpec{Hard: corev1.ResourceList{
|
||||
corev1.ResourceRequestsCPU: resource.MustParse("8"),
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
wantErr: `rules[0].quota[0].name "Shared_Compute" is invalid`,
|
||||
},
|
||||
{
|
||||
name: "quota hard must not be empty",
|
||||
mapper: mapper,
|
||||
bodies: []*rules.NamespaceRuleBodyNamespace{{
|
||||
Quota: []rules.ResourceQuotaRule{{Name: "shared-compute"}},
|
||||
}},
|
||||
wantErr: "rules[0].quota[0].hard is invalid",
|
||||
},
|
||||
{
|
||||
name: "quota hard must not be negative",
|
||||
mapper: mapper,
|
||||
bodies: []*rules.NamespaceRuleBodyNamespace{{
|
||||
Quota: []rules.ResourceQuotaRule{{
|
||||
Name: "shared-compute",
|
||||
ResourceQuotaSpec: corev1.ResourceQuotaSpec{Hard: corev1.ResourceList{
|
||||
corev1.ResourceRequestsCPU: resource.MustParse("-1"),
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
wantErr: `rules[0].quota[0].hard["requests.cpu"] is invalid`,
|
||||
},
|
||||
{
|
||||
name: "quota names must be unique across rules",
|
||||
mapper: mapper,
|
||||
bodies: []*rules.NamespaceRuleBodyNamespace{
|
||||
{Quota: []rules.ResourceQuotaRule{{Name: "shared-compute", ResourceQuotaSpec: corev1.ResourceQuotaSpec{Hard: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1")}}}}},
|
||||
{Quota: []rules.ResourceQuotaRule{{Name: "shared-compute", ResourceQuotaSpec: corev1.ResourceQuotaSpec{Hard: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("1Gi")}}}}},
|
||||
},
|
||||
wantErr: `rules[1].quota[0].name "shared-compute" is invalid: quota name is already used by rules[0].quota[0]`,
|
||||
},
|
||||
{
|
||||
name: "valid workload service and metadata rules",
|
||||
mapper: mapper,
|
||||
|
||||
Reference in New Issue
Block a user