Skip to content
This repository was archived by the owner on Apr 17, 2025. It is now read-only.

Commit 62f275c

Browse files
committed
show HRQ status by kubectl get
1 parent 168dd7e commit 62f275c

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

api/v1alpha2/hierarchicalresourcequota_types.go

+10
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,20 @@ type HierarchicalResourceQuotaStatus struct {
3838
// and its descendant namespaces.
3939
// +optional
4040
Used corev1.ResourceList `json:"used,omitempty"`
41+
// RequestsSummary is used by kubectl get hrq, and summarizes the relevant information
42+
// from .status.hard.requests and .status.used.requests.
43+
// +optional
44+
RequestsSummary string `json:"requestsSummary,omitempty"`
45+
// LimitsSummary is used by kubectl get hrq, and summarizes the relevant information
46+
// from .status.hard.limits and .status.used.limits.
47+
// +optional
48+
LimitsSummary string `json:"limitsSummary,omitempty"`
4149
}
4250

4351
// +kubebuilder:object:root=true
4452
// +kubebuilder:resource:path=hierarchicalresourcequotas,shortName=hrq,scope=Namespaced
53+
// +kubebuilder:printcolumn:name="Request",type="string",JSONPath=".status.requestsSummary"
54+
// +kubebuilder:printcolumn:name="Limit",type="string",JSONPath=".status.limitsSummary"
4555

4656
// HierarchicalResourceQuota sets aggregate quota restrictions enforced for a
4757
// namespace and descendant namespaces

config/crd/bases/hnc.x-k8s.io_hierarchicalresourcequotas.yaml

+17-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ spec:
1717
singular: hierarchicalresourcequota
1818
scope: Namespaced
1919
versions:
20-
- name: v1alpha2
20+
- additionalPrinterColumns:
21+
- jsonPath: .status.requestsSummary
22+
name: Request
23+
type: string
24+
- jsonPath: .status.limitsSummary
25+
name: Limit
26+
type: string
27+
name: v1alpha2
2128
schema:
2229
openAPIV3Schema:
2330
description: HierarchicalResourceQuota sets aggregate quota restrictions enforced
@@ -63,6 +70,14 @@ spec:
6370
description: Hard is the set of enforced hard limits for each named
6471
resource
6572
type: object
73+
limitsSummary:
74+
description: LimitsSummary is used by kubectl get hrq, and summarizes
75+
the relevant information from .status.hard.limits and .status.used.limits.
76+
type: string
77+
requestsSummary:
78+
description: RequestsSummary is used by kubectl get hrq, and summarizes
79+
the relevant information from .status.hard.requests and .status.used.requests.
80+
type: string
6681
used:
6782
additionalProperties:
6883
anyOf:
@@ -77,6 +92,7 @@ spec:
7792
type: object
7893
served: true
7994
storage: true
95+
subresources: {}
8096
status:
8197
acceptedNames:
8298
kind: ""

internal/hrq/hrqreconciler.go

+46
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package hrq
22

33
import (
4+
"bytes"
45
"context"
56
"fmt"
7+
"sort"
8+
"strings"
69

710
"github.com/go-logr/logr"
811
"k8s.io/apimachinery/pkg/api/errors"
@@ -13,6 +16,7 @@ import (
1316
"sigs.k8s.io/controller-runtime/pkg/handler"
1417
"sigs.k8s.io/controller-runtime/pkg/source"
1518

19+
v1 "k8s.io/api/core/v1"
1620
api "sigs.k8s.io/hierarchical-namespaces/api/v1alpha2"
1721
"sigs.k8s.io/hierarchical-namespaces/internal/forest"
1822
"sigs.k8s.io/hierarchical-namespaces/internal/hrq/utils"
@@ -144,6 +148,33 @@ func (r *HierarchicalResourceQuotaReconciler) syncUsages(inst *api.HierarchicalR
144148
// Filter the usages to only include the resource types being limited by this HRQ and write those
145149
// usages back to the HRQ status.
146150
inst.Status.Used = utils.FilterUnlimited(ns.GetSubtreeUsages(), inst.Spec.Hard)
151+
152+
// Update status.request and status.limit to show HRQ status by using kubectl get
153+
resources := make([]v1.ResourceName, 0, len(inst.Status.Hard))
154+
for resource := range inst.Status.Hard {
155+
resources = append(resources, resource)
156+
}
157+
sort.Sort(sortableResourceNames(resources))
158+
159+
requestColumn := bytes.NewBuffer([]byte{})
160+
limitColumn := bytes.NewBuffer([]byte{})
161+
for i := range resources {
162+
w := requestColumn
163+
resource := resources[i]
164+
usedQuantity := inst.Status.Used[resource]
165+
hardQuantity := inst.Status.Hard[resource]
166+
167+
// use limitColumn writer if a resource name prefixed with "limits" is found
168+
if pieces := strings.Split(resource.String(), "."); len(pieces) > 1 && pieces[0] == "limits" {
169+
w = limitColumn
170+
}
171+
172+
fmt.Fprintf(w, "%s: %s/%s, ", resource, usedQuantity.String(), hardQuantity.String())
173+
}
174+
175+
inst.Status.RequestsSummary = strings.TrimSuffix(requestColumn.String(), ", ")
176+
inst.Status.LimitsSummary = strings.TrimSuffix(limitColumn.String(), ", ")
177+
147178
}
148179

149180
func isDeleted(inst *api.HierarchicalResourceQuota) bool {
@@ -203,3 +234,18 @@ func (r *HierarchicalResourceQuotaReconciler) SetupWithManager(mgr ctrl.Manager)
203234
Watches(&source.Channel{Source: r.trigger}, &handler.EnqueueRequestForObject{}).
204235
Complete(r)
205236
}
237+
238+
// sortableResourceNames - An array of sortable resource names
239+
type sortableResourceNames []v1.ResourceName
240+
241+
func (list sortableResourceNames) Len() int {
242+
return len(list)
243+
}
244+
245+
func (list sortableResourceNames) Swap(i, j int) {
246+
list[i], list[j] = list[j], list[i]
247+
}
248+
249+
func (list sortableResourceNames) Less(i, j int) bool {
250+
return list[i] < list[j]
251+
}

test/e2e/hrq_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,32 @@ var _ = PDescribe("Hierarchical Resource Quota", func() {
296296
createPod("pod2", nsC, "memory 200Mi cpu 300m", "memory 100Mi cpu 150m")
297297
})
298298

299+
It("should get HRQ status using kubectl get hrq command", func() {
300+
// set up namespaces
301+
CreateNamespace(nsA)
302+
CreateSubnamespace(nsB, nsA)
303+
304+
// Set up an HRQ with limits of 2 secrets in namespace a.
305+
setHRQ("a-hrq", nsA, "secrets", "2")
306+
// Set up an HRQ with limits of 4 pvcs in namespace a.
307+
setHRQ("a-hrq-another", nsA, "persistentvolumeclaims", "4")
308+
// Set up an HRQ with limits of 3 pvcs in namespace b.
309+
setHRQ("b-hrq", nsB, "persistentvolumeclaims", "3")
310+
311+
// Should allow creating a secret in namespace b.
312+
Eventually(createSecret("b-secret", nsB)).Should(Succeed())
313+
// Should allow creating a pvc in namespace a.
314+
Eventually(createPVC("a-pvc", nsA)).Should(Succeed())
315+
// Should allow creating a pvc in namespace b.
316+
Eventually(createPVC("b-pvc", nsB)).Should(Succeed())
317+
318+
// Verify the HRQ status are shown.
319+
RunShouldContainMultiple([]string{"a-hrq", "secrets: 1/2"}, propogationTimeout, "kubectl get hrq", "a-hrq", "-n", nsA)
320+
RunShouldContainMultiple([]string{"b-hrq", "persistentvolumeclaims: 1/3"}, propogationTimeout, "kubectl get hrq", "b-hrq", "-n", nsB)
321+
RunShouldContainMultiple([]string{"a-hrq", "a-hrq-another"}, propogationTimeout, "kubectl get hrq", "-n", nsA)
322+
RunShouldContainMultiple([]string{"a-hrq", "a-hrq-another", "a-hrq"}, propogationTimeout, "kubectl get hrq", "--all-namespaces")
323+
})
324+
299325
It("should get HRQ status using kubectl-hns hrq command", func() {
300326
// set up namespaces
301327
CreateNamespace(nsA)

0 commit comments

Comments
 (0)