[cozy-lib] Improve flatten function (#1647)

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.
```

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[]
```
This commit is contained in:
Andrei Kvapil
2025-11-25 14:48:03 +01:00
committed by GitHub
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"