[cozy-lib] Improve flatten function

This patch breaks introduces a helper function in cozy-lib to correctly
handle special case resources when transforming a nested map of limits
and requests to a flat map suitable for use in resourceQuotas. As a
result, admins can now specify any types of resources as resource quotas
for tenants, and they will be correctly transformed to the correct
format for the underlying kubernetes ResourceQuota. In addition to the
previously supported compute resources, such as CPU, memory, and custom
resources, like GPUs, special quota strings such as
"services.loadbalancers" are now correctly handled.

```release-note
[cozy-lib,platform] Support resource quotas for special kubernetes
quotas, such as service.loadbalncer count and others.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
This commit is contained in:
Timofei Larkin
2025-11-21 17:41:16 +03:00
parent a070573af9
commit 91ddbb06ef
3 changed files with 65 additions and 65 deletions

View File

@@ -174,68 +174,46 @@
{{- end }}
{{- define "cozy-lib.resources.flatten" -}}
{{- /*
This helper either outputs raw ResourceQuota fields (e.g., services.loadbalancers)
as-is, or flattens sanitized resource maps into limits.* / requests.* keys.
{{- $out := dict -}}
{{- $res := include "cozy-lib.resources.sanitize" . | fromYaml -}}
{{- range $section, $values := $res }}
{{- range $k, $v := $values }}
{{- with include "cozy-lib.resources.flattenResource" (list $section $k) }}
{{- $_ := set $out . $v }}
{{- end }}
{{- end }}
{{- end }}
{{- $out | toYaml }}
{{- end }}
If ALL keys in the input are recognized quota keys (pods, services.*, etc.),
the input is output directly as YAML. Otherwise, the input is treated as a
resource map and processed through sanitize + flatten.
Do not mix quota keys and resource keys in a single call.
*/ -}}
{{- $input := index . 0 -}}
{{- $ctx := index . 1 -}}
{{- $rawQuotaKeys := list
"pods"
"services"
"services.loadbalancers"
"services.nodeports"
"services.clusterip"
"configmaps"
"secrets"
"persistentvolumeclaims"
"replicationcontrollers"
"resourcequotas"
{{/*
This is a helper function that takes an argument like `list "limits" "services.loadbalancers"`
or `list "limits" "storage"` or `list "requests" "cpu"` and returns "services.loadbalancers",
"", and "requests.cpu", respectively, thus transforming them to an acceptable format for k8s
ResourceQuotas objects.
*/}}
{{- define "cozy-lib.resources.flattenResource" }}
{{- $rawQuotaKeys := list
"pods"
"services"
"services.loadbalancers"
"services.nodeports"
"services.clusterip"
"configmaps"
"secrets"
"persistentvolumeclaims"
"replicationcontrollers"
"resourcequotas"
-}}
{{- $computeKeys := list
"cpu"
"memory"
"ephemeral-storage"
-}}
{{- $out := dict -}}
{{- $computeResources := dict -}}
{{- $quotaResources := dict -}}
{{- range $k, $v := $input }}
{{- if or (has $k $computeKeys) (hasPrefix "limits." $k) (hasPrefix "requests." $k) }}
{{- $_ := set $computeResources $k $v }}
{{- else if has $k $rawQuotaKeys }}
{{- $_ := set $quotaResources $k $v }}
{{- else }}
{{- $_ := set $computeResources $k $v }}
{{- end }}
{{- end }}
{{- if $computeResources }}
{{- $res := include "cozy-lib.resources.sanitize" (list $computeResources (index . 1)) | fromYaml -}}
{{- range $section, $values := $res }}
{{- range $k, $v := $values }}
{{- $key := printf "%s.%s" $section $k }}
{{- if ne $key "limits.storage" }}
{{- $_ := set $out $key $v }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- range $k, $v := $quotaResources }}
{{- $_ := set $out $k $v }}
{{- end }}
{{- $out | toYaml }}
{{- $section := index . 0 }}
{{- $type := index . 1 }}
{{- $out := "" }}
{{- if and (eq $section "limits") (eq $type "storage") }}
{{- $out = "" }}
{{- else if and (eq $section "limits") (has $type $rawQuotaKeys) }}
{{- $out = $type }}
{{- else if not (has $type $rawQuotaKeys) }}
{{- $out = printf "%s.%s" $section $type }}
{{- end }}
{{- $out -}}
{{- end }}

View File

@@ -18,13 +18,33 @@ tests:
asserts:
- equal:
path: spec.hard["limits.cpu"]
value: "2"
value: "20"
- equal:
path: spec.hard["requests.cpu"]
value: "0.2"
value: "2"
- equal:
path: spec.hard["limits.foobar"]
value: "3"
- equal:
path: spec.hard["requests.foobar"]
value: "3"
- equal:
path: spec.hard["services.loadbalancers"]
value: "2"
- equal:
path: spec.hard["requests.storage"]
value: "5Gi"
- notExists:
path: spec.hard["limits.storage"]
- notExists:
path: spec.hard["limits.services.loadbalancers"]
- notExists:
path: spec.hard["requests.services.loadbalancers"]

View File

@@ -1,3 +1,5 @@
quota:
services.loadbalancers: "2"
cpu: "2"
cpu: "20"
storage: "5Gi"
foobar: "3"