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

Commit ffd2763

Browse files
authored
Merge pull request #290 from keisukesakasai/cherry-pick-of-#286-v1.1
Cherry pick of #283, #286, #294
2 parents 61562c5 + 131d496 commit ffd2763

File tree

104 files changed

+13580
-2344
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+13580
-2344
lines changed

go.mod

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ require (
3535
github.com/BurntSushi/toml v1.2.1 // indirect
3636
github.com/PuerkitoBio/purell v1.1.1 // indirect
3737
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
38-
github.com/aws/aws-sdk-go v1.23.20 // indirect
38+
github.com/aws/aws-sdk-go v1.38.49 // indirect
3939
github.com/beorn7/perks v1.0.1 // indirect
4040
github.com/census-instrumentation/opencensus-proto v0.2.1 // indirect
4141
github.com/cespare/xxhash/v2 v2.1.1 // indirect
@@ -63,10 +63,10 @@ require (
6363
github.com/googleapis/gnostic v0.5.5 // indirect
6464
github.com/imdario/mergo v0.3.12 // indirect
6565
github.com/inconshreveable/mousetrap v1.0.0 // indirect
66-
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
66+
github.com/jmespath/go-jmespath v0.4.0 // indirect
6767
github.com/josharian/intern v1.0.0 // indirect
6868
github.com/json-iterator/go v1.1.12 // indirect
69-
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
69+
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
7070
github.com/mailru/easyjson v0.7.6 // indirect
7171
github.com/mattn/go-colorable v0.1.8 // indirect
7272
github.com/mattn/go-isatty v0.0.12 // indirect
@@ -99,9 +99,8 @@ require (
9999
golang.org/x/text v0.5.0
100100
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
101101
golang.org/x/tools v0.4.1-0.20221208213631-3f74d914ae6d // indirect
102-
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
103102
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
104-
google.golang.org/api v0.44.0 // indirect
103+
google.golang.org/api v0.46.0 // indirect
105104
google.golang.org/appengine v1.6.7 // indirect
106105
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 // indirect
107106
google.golang.org/grpc v1.40.0 // indirect
@@ -120,7 +119,6 @@ require (
120119
sigs.k8s.io/yaml v1.3.0 // indirect
121120
)
122121

123-
require (
124-
github.com/spf13/afero v1.6.0 // indirect
125-
sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20230208013708-22718275bffe // indirect
126-
)
122+
require sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20230208013708-22718275bffe
123+
124+
require github.com/spf13/afero v1.6.0 // indirect

go.sum

Lines changed: 12 additions & 31 deletions
Large diffs are not rendered by default.

internal/kubectl/hrq.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package kubectl
17+
18+
import (
19+
"bytes"
20+
"fmt"
21+
"os"
22+
"sort"
23+
"strings"
24+
"time"
25+
26+
"github.com/liggitt/tabwriter"
27+
"github.com/spf13/cobra"
28+
v1 "k8s.io/api/core/v1"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/apimachinery/pkg/runtime"
31+
"k8s.io/apimachinery/pkg/runtime/schema"
32+
"k8s.io/apimachinery/pkg/util/duration"
33+
"k8s.io/cli-runtime/pkg/printers"
34+
api "sigs.k8s.io/hierarchical-namespaces/api/v1alpha2"
35+
)
36+
37+
const (
38+
tabwriterMinWidth = 6
39+
tabwriterWidth = 4
40+
tabwriterPadding = 3
41+
tabwriterPadChar = ' '
42+
tabwriterFlags = tabwriter.RememberWidths
43+
)
44+
45+
var namespace string
46+
47+
// Define HierarchicalResourceQuota Table Column
48+
var hierarchicalResourceQuotaColumnDefinitions = []metav1.TableColumnDefinition{
49+
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
50+
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
51+
{Name: "Request", Type: "string", Description: "Request represents a minimum amount of cpu/memory that a container may consume."},
52+
{Name: "Limit", Type: "string", Description: "Limits control the maximum amount of cpu/memory that a container may use independent of contention on the node."},
53+
}
54+
55+
var hrqCmd = &cobra.Command{
56+
Use: "hrq [NAME]",
57+
Short: "Display one or more HierarchicalResourceQuota",
58+
Run: Run,
59+
}
60+
61+
func Run(cmd *cobra.Command, args []string) {
62+
flags := cmd.Flags()
63+
table := &metav1.Table{ColumnDefinitions: hierarchicalResourceQuotaColumnDefinitions}
64+
65+
showLabels := flags.Changed("show-labels")
66+
67+
allResourcesNamespaced := !flags.Changed("all-namespaces")
68+
if !allResourcesNamespaced {
69+
namespace = ""
70+
}
71+
72+
// Get HierarchicalResourceQuotaList from the specified namespace
73+
hrqList := client.getHRQ(args, namespace)
74+
75+
if len(hrqList.Items) < 1 {
76+
if allResourcesNamespaced {
77+
fmt.Printf("No resources found in %s namespace.\n", namespace)
78+
os.Exit(1)
79+
} else {
80+
fmt.Println("No resources found")
81+
os.Exit(1)
82+
}
83+
}
84+
85+
// Create []metav1.TableRow from HierarchicalResourceQuotaList
86+
tableRaws, err := printHierarchicalResourceQuotaList(hrqList)
87+
if err != nil {
88+
fmt.Printf("Error reading hierarchicalresourcequotas: %s\n", err)
89+
}
90+
table.Rows = tableRaws
91+
92+
// Create writer
93+
w := tabwriter.NewWriter(os.Stdout, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags)
94+
95+
// Create TablePrinter
96+
p := printers.NewTablePrinter(printers.PrintOptions{
97+
NoHeaders: false,
98+
WithNamespace: !allResourcesNamespaced,
99+
WithKind: true,
100+
Wide: true,
101+
ShowLabels: showLabels,
102+
Kind: schema.GroupKind{},
103+
ColumnLabels: nil,
104+
SortBy: "",
105+
AllowMissingKeys: false,
106+
})
107+
108+
p.PrintObj(table, w)
109+
110+
w.Flush()
111+
}
112+
113+
func printHierarchicalResourceQuota(hierarchicalResourceQuota *api.HierarchicalResourceQuota) ([]metav1.TableRow, error) {
114+
row := metav1.TableRow{
115+
Object: runtime.RawExtension{Object: hierarchicalResourceQuota},
116+
}
117+
118+
resources := make([]v1.ResourceName, 0, len(hierarchicalResourceQuota.Status.Hard))
119+
for resource := range hierarchicalResourceQuota.Status.Hard {
120+
resources = append(resources, resource)
121+
}
122+
sort.Sort(SortableResourceNames(resources))
123+
124+
requestColumn := bytes.NewBuffer([]byte{})
125+
limitColumn := bytes.NewBuffer([]byte{})
126+
for i := range resources {
127+
w := requestColumn
128+
resource := resources[i]
129+
usedQuantity := hierarchicalResourceQuota.Status.Used[resource]
130+
hardQuantity := hierarchicalResourceQuota.Status.Hard[resource]
131+
132+
// use limitColumn writer if a resource name prefixed with "limits" is found
133+
if pieces := strings.Split(resource.String(), "."); len(pieces) > 1 && pieces[0] == "limits" {
134+
w = limitColumn
135+
}
136+
137+
fmt.Fprintf(w, "%s: %s/%s, ", resource, usedQuantity.String(), hardQuantity.String())
138+
}
139+
140+
age := translateTimestampSince(hierarchicalResourceQuota.CreationTimestamp)
141+
row.Cells = append(row.Cells, hierarchicalResourceQuota.Name, age, strings.TrimSuffix(requestColumn.String(), ", "), strings.TrimSuffix(limitColumn.String(), ", "))
142+
return []metav1.TableRow{row}, nil
143+
}
144+
145+
func printHierarchicalResourceQuotaList(list *api.HierarchicalResourceQuotaList) ([]metav1.TableRow, error) {
146+
rows := make([]metav1.TableRow, 0, len(list.Items))
147+
for i := range list.Items {
148+
r, err := printHierarchicalResourceQuota(&list.Items[i])
149+
if err != nil {
150+
return nil, err
151+
}
152+
rows = append(rows, r...)
153+
}
154+
return rows, nil
155+
}
156+
157+
// translateTimestampSince returns the elapsed time since timestamp in
158+
// human-readable approximation.
159+
func translateTimestampSince(timestamp metav1.Time) string {
160+
if timestamp.IsZero() {
161+
return "<unknown>"
162+
}
163+
164+
return duration.HumanDuration(time.Since(timestamp.Time))
165+
}
166+
167+
// SortableResourceNames - An array of sortable resource names
168+
type SortableResourceNames []v1.ResourceName
169+
170+
func (list SortableResourceNames) Len() int {
171+
return len(list)
172+
}
173+
174+
func (list SortableResourceNames) Swap(i, j int) {
175+
list[i], list[j] = list[j], list[i]
176+
}
177+
178+
func (list SortableResourceNames) Less(i, j int) bool {
179+
return list[i] < list[j]
180+
}
181+
182+
func newHrqCmd() *cobra.Command {
183+
hrqCmd.Flags().StringVarP(&namespace, "namespace", "n", v1.NamespaceDefault, "If present, the namespace scope for this CLI request")
184+
185+
hrqCmd.Flags().BoolP("all-namespaces", "A", false, "Displays all HierarchicalResourceQuota on the cluster")
186+
hrqCmd.Flags().BoolP("show-labels", "", false, "Displays Labels of HierarchicalResourceQuota")
187+
return hrqCmd
188+
}

internal/kubectl/root.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type Client interface {
5252
getAnchorStatus(nnm string) anchorStatus
5353
getHNCConfig() *api.HNCConfiguration
5454
updateHNCConfig(*api.HNCConfiguration)
55+
getHRQ(names []string, nnm string) *api.HierarchicalResourceQuotaList
5556
}
5657

5758
func init() {
@@ -103,6 +104,7 @@ func init() {
103104
rootCmd.AddCommand(newCreateCmd())
104105
rootCmd.AddCommand(newConfigCmd())
105106
rootCmd.AddCommand(newVersionCmd())
107+
rootCmd.AddCommand(newHrqCmd())
106108
}
107109

108110
func Execute() {
@@ -213,3 +215,27 @@ func (cl *realClient) updateHNCConfig(config *api.HNCConfiguration) {
213215
os.Exit(1)
214216
}
215217
}
218+
219+
func (cl *realClient) getHRQ(names []string, nnm string) *api.HierarchicalResourceQuotaList {
220+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
221+
defer cancel()
222+
hrqList := &api.HierarchicalResourceQuotaList{}
223+
224+
if len(names) > 0 && nnm != "" {
225+
for _, name := range names {
226+
hrq := &api.HierarchicalResourceQuota{}
227+
if err := hncClient.Get().Resource("hierarchicalresourcequotas").Namespace(namespace).Name(name).Do(ctx).Into(hrq); err != nil {
228+
fmt.Printf("Error reading hierarchicalresourcequota %s: %s\n", name, err)
229+
os.Exit(1)
230+
}
231+
232+
hrqList.Items = append(hrqList.Items, *hrq)
233+
}
234+
} else {
235+
if err := hncClient.Get().Resource("hierarchicalresourcequotas").Namespace(namespace).Do(ctx).Into(hrqList); err != nil {
236+
fmt.Printf("Error reading hierarchicalresourcequotas: %s\n", err)
237+
os.Exit(1)
238+
}
239+
}
240+
return hrqList
241+
}

test/e2e/hrq_test.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ import (
1919

2020
const (
2121
prefix = "hrq-test-"
22-
nsA = prefix+"a"
23-
nsB = prefix+"b"
24-
nsC = prefix+"c"
25-
nsD = prefix+"d"
22+
nsA = prefix + "a"
23+
nsB = prefix + "b"
24+
nsC = prefix + "c"
25+
nsD = prefix + "d"
2626

27-
propagationTime = 5
27+
propagationTime = 5
2828
resourceQuotaSingleton = "hrq.hnc.x-k8s.io"
2929
)
3030

@@ -295,6 +295,32 @@ var _ = PDescribe("Hierarchical Resource Quota", func() {
295295
// Should allow creating another pod under limit.
296296
createPod("pod2", nsC, "memory 200Mi cpu 300m", "memory 100Mi cpu 150m")
297297
})
298+
299+
It("should get HRQ status using kubectl-hns 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 hns hrq", "a-hrq", "-n", nsA)
320+
RunShouldContainMultiple([]string{"b-hrq", "persistentvolumeclaims: 1/3"}, propogationTimeout, "kubectl hns hrq", "b-hrq", "-n", nsB)
321+
RunShouldContainMultiple([]string{"a-hrq", "a-hrq-another"}, propogationTimeout, "kubectl hns hrq", "-n", nsA)
322+
RunShouldContainMultiple([]string{"a-hrq", "a-hrq-another", "a-hrq"}, propogationTimeout, "kubectl hns hrq", "--all-namespaces")
323+
})
298324
})
299325

300326
func generateHRQManifest(nm, nsnm string, args ...string) string {
@@ -340,8 +366,8 @@ func createPVC(nm, nsnm string) func() error {
340366
apiVersion: v1
341367
kind: PersistentVolumeClaim
342368
metadata:
343-
name: `+nm+`
344-
namespace: `+nsnm+`
369+
name: ` + nm + `
370+
namespace: ` + nsnm + `
345371
spec:
346372
storageClassName: manual
347373
accessModes:
@@ -350,7 +376,7 @@ spec:
350376
requests:
351377
storage: 1Gi`
352378
fn := writeTempFile(pvc)
353-
GinkgoT().Log("Wrote "+fn+":\n"+pvc)
379+
GinkgoT().Log("Wrote " + fn + ":\n" + pvc)
354380
defer removeFile(fn)
355381
return TryRun("kubectl apply -f", fn)
356382
}

vendor/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/aws/aws-sdk-go/aws/client/client.go

Lines changed: 2 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)