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

Show HierarchicalResourceQuota status by kubectl get #295

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions api/v1alpha2/hierarchicalresourcequota_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,20 @@ type HierarchicalResourceQuotaStatus struct {
// and its descendant namespaces.
// +optional
Used corev1.ResourceList `json:"used,omitempty"`
// RequestsSummary is used by kubectl get hrq, and summarizes the relevant information
// from .status.hard.requests and .status.used.requests.
// +optional
RequestsSummary string `json:"requestsSummary,omitempty"`
// LimitsSummary is used by kubectl get hrq, and summarizes the relevant information
// from .status.hard.limits and .status.used.limits.
// +optional
LimitsSummary string `json:"limitsSummary,omitempty"`
}

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

// HierarchicalResourceQuota sets aggregate quota restrictions enforced for a
// namespace and descendant namespaces
Expand Down
17 changes: 16 additions & 1 deletion config/crd/bases/hnc.x-k8s.io_hierarchicalresourcequotas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ spec:
singular: hierarchicalresourcequota
scope: Namespaced
versions:
- name: v1alpha2
- additionalPrinterColumns:
- jsonPath: .status.requestsSummary
name: Request
type: string
- jsonPath: .status.limitsSummary
name: Limit
type: string
name: v1alpha2
schema:
openAPIV3Schema:
description: HierarchicalResourceQuota sets aggregate quota restrictions enforced
Expand Down Expand Up @@ -62,6 +69,14 @@ spec:
description: Hard is the set of enforced hard limits for each named
resource
type: object
limitsSummary:
description: LimitsSummary is used by kubectl get hrq, and summarizes
the relevant information from .status.hard.limits and .status.used.limits.
type: string
requestsSummary:
description: RequestsSummary is used by kubectl get hrq, and summarizes
the relevant information from .status.hard.requests and .status.used.requests.
type: string
used:
additionalProperties:
anyOf:
Expand Down
46 changes: 46 additions & 0 deletions internal/hrq/hrqreconciler.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package hrq

import (
"bytes"
"context"
"fmt"
"sort"
"strings"

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

v1 "k8s.io/api/core/v1"
api "sigs.k8s.io/hierarchical-namespaces/api/v1alpha2"
"sigs.k8s.io/hierarchical-namespaces/internal/forest"
"sigs.k8s.io/hierarchical-namespaces/internal/hrq/utils"
Expand Down Expand Up @@ -144,6 +148,33 @@ func (r *HierarchicalResourceQuotaReconciler) syncUsages(inst *api.HierarchicalR
// Filter the usages to only include the resource types being limited by this HRQ and write those
// usages back to the HRQ status.
inst.Status.Used = utils.FilterUnlimited(ns.GetSubtreeUsages(), inst.Spec.Hard)

// Update status.request and status.limit to show HRQ status by using kubectl get
resources := make([]v1.ResourceName, 0, len(inst.Status.Hard))
for resource := range inst.Status.Hard {
resources = append(resources, resource)
}
sort.Sort(sortableResourceNames(resources))

requestColumn := bytes.NewBuffer([]byte{})
limitColumn := bytes.NewBuffer([]byte{})
for i := range resources {
w := requestColumn
resource := resources[i]
usedQuantity := inst.Status.Used[resource]
hardQuantity := inst.Status.Hard[resource]

// use limitColumn writer if a resource name prefixed with "limits" is found
if pieces := strings.Split(resource.String(), "."); len(pieces) > 1 && pieces[0] == "limits" {
w = limitColumn
}

fmt.Fprintf(w, "%s: %s/%s, ", resource, usedQuantity.String(), hardQuantity.String())
}

inst.Status.RequestsSummary = strings.TrimSuffix(requestColumn.String(), ", ")
inst.Status.LimitsSummary = strings.TrimSuffix(limitColumn.String(), ", ")

}

func isDeleted(inst *api.HierarchicalResourceQuota) bool {
Expand Down Expand Up @@ -203,3 +234,18 @@ func (r *HierarchicalResourceQuotaReconciler) SetupWithManager(mgr ctrl.Manager)
Watches(&source.Channel{Source: r.trigger}, &handler.EnqueueRequestForObject{}).
Complete(r)
}

// sortableResourceNames - An array of sortable resource names
type sortableResourceNames []v1.ResourceName

func (list sortableResourceNames) Len() int {
return len(list)
}

func (list sortableResourceNames) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}

func (list sortableResourceNames) Less(i, j int) bool {
return list[i] < list[j]
}
26 changes: 26 additions & 0 deletions test/e2e/hrq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,32 @@ var _ = PDescribe("Hierarchical Resource Quota", func() {
createPod("pod2", nsC, "memory 200Mi cpu 300m", "memory 100Mi cpu 150m")
})

It("should get HRQ status using kubectl get hrq command", func() {
// set up namespaces
CreateNamespace(nsA)
CreateSubnamespace(nsB, nsA)

// Set up an HRQ with limits of 2 secrets in namespace a.
setHRQ("a-hrq", nsA, "secrets", "2")
// Set up an HRQ with limits of 4 pvcs in namespace a.
setHRQ("a-hrq-another", nsA, "persistentvolumeclaims", "4")
// Set up an HRQ with limits of 3 pvcs in namespace b.
setHRQ("b-hrq", nsB, "persistentvolumeclaims", "3")

// Should allow creating a secret in namespace b.
Eventually(createSecret("b-secret", nsB)).Should(Succeed())
// Should allow creating a pvc in namespace a.
Eventually(createPVC("a-pvc", nsA)).Should(Succeed())
// Should allow creating a pvc in namespace b.
Eventually(createPVC("b-pvc", nsB)).Should(Succeed())

// Verify the HRQ status are shown.
RunShouldContainMultiple([]string{"a-hrq", "secrets: 1/2"}, propogationTimeout, "kubectl get hrq", "a-hrq", "-n", nsA)
RunShouldContainMultiple([]string{"b-hrq", "persistentvolumeclaims: 1/3"}, propogationTimeout, "kubectl get hrq", "b-hrq", "-n", nsB)
RunShouldContainMultiple([]string{"a-hrq", "a-hrq-another"}, propogationTimeout, "kubectl get hrq", "-n", nsA)
RunShouldContainMultiple([]string{"a-hrq", "a-hrq-another", "a-hrq"}, propogationTimeout, "kubectl get hrq", "--all-namespaces")
})

It("should get HRQ status using kubectl-hns hrq command", func() {
// set up namespaces
CreateNamespace(nsA)
Expand Down