Skip to content

Commit eaa66fc

Browse files
authored
Add the tests for Scoped HRQ (#8)
* Mark the serial falg in the tests if necessary Signed-off-by: utam0k <utam0k@preferred.jp> * Add the Scoped HRQ as the forked tests Signed-off-by: utam0k <utam0k@preferred.jp> --------- Signed-off-by: utam0k <utam0k@preferred.jp>
1 parent 9f3f4a8 commit eaa66fc

File tree

7 files changed

+247
-6
lines changed

7 files changed

+247
-6
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,6 @@ buildx-setup:
304304
docker-push-multi: buildx-setup generate fmt vet staticcheck
305305
@echo "Warning: this does not run tests. Run 'make test' to ensure tests are passing."
306306
docker buildx build \
307-
--push \
308307
--platform linux/arm64,linux/amd64,linux/arm/v7 --tag ${HNC_IMG} .
309308

310309
###################### KIND ACTIONS #########################
@@ -357,6 +356,13 @@ test-e2e-batch: warn-hnc-repair
357356
go test -v -timeout 0 ./test/e2e/... ; \
358357
done
359358

359+
PHONY: test-pfnet-e2e-parallel
360+
test-pfnet-e2e-parallel:
361+
kind get kubeconfig --name kind > /tmp/kind-hnc-config
362+
export KUBECONFIG=/tmp/kind-hnc-config && kubectl -n hnc-system patch deployment hnc-controller-manager -p '{"spec":{"template":{"spec":{"containers":[{"name":"manager","resources":{"limits":{"cpu":null}}}]}}}}'
363+
export KUBECONFIG=/tmp/kind-hnc-config && kubectl -n hnc-system wait --for=condition=available deployment/hnc-controller-manager --timeout=5m
364+
export KUBECONFIG=/tmp/kind-hnc-config && ginkgo run -p --label-filter pfnet ./test/e2e/...
365+
360366
# This will *only* run a small number of tests (specifically, the quickstarts). You can run this when you're fairly confident that
361367
# everything will work, but be sure to watch for the postsubmits and periodic tests to ensure they pass too.
362368
.PHONY: test-smoke

test/e2e/delete_crd_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
. "sigs.k8s.io/hierarchical-namespaces/pkg/testutils"
88
)
99

10-
var _ = Describe("When deleting CRDs", func() {
10+
var _ = Describe("When deleting CRDs", Serial, func() {
1111

1212
const (
1313
nsParent = "delete-crd-parent"

test/e2e/hrq_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const (
2929
)
3030

3131
// HRQ tests are pending (disabled) until we turn them on in all the default manifests
32-
var _ = PDescribe("Hierarchical Resource Quota", func() {
32+
var _ = PDescribe("Hierarchical Resource Quota", Serial, func() {
3333
BeforeEach(func() {
3434
rand.Seed(time.Now().UnixNano())
3535
CleanupTestNamespaces()

test/e2e/issues_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
. "sigs.k8s.io/hierarchical-namespaces/pkg/testutils"
88
)
99

10-
var _ = Describe("Issues", func() {
10+
var _ = Describe("Issues", Serial, func() {
1111

1212
const (
1313
nsParent = "parent"

test/e2e/namespace_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
. "sigs.k8s.io/hierarchical-namespaces/pkg/testutils"
99
)
1010

11-
var _ = Describe("Namespace", func() {
11+
var _ = Describe("Namespace", Serial, func() {
1212
const (
1313
prefix = namspacePrefix + "namespace-"
1414
nsA = prefix + "a"

test/e2e/pfn_hrq_test.go

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package e2e
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
"sync"
8+
9+
corev1 "k8s.io/api/core/v1"
10+
"k8s.io/apimachinery/pkg/api/resource"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
api "sigs.k8s.io/hierarchical-namespaces/api/v1alpha2"
13+
"sigs.k8s.io/yaml"
14+
15+
"github.com/google/uuid"
16+
. "github.com/onsi/ginkgo/v2"
17+
. "github.com/onsi/gomega"
18+
. "sigs.k8s.io/hierarchical-namespaces/pkg/testutils"
19+
)
20+
21+
var _ = Describe("Scoped Hierarchical Resource Quota", Label("pfnet"), func() {
22+
var (
23+
parentNs, childNs, priorityName string
24+
cleanupNs, cleanupPriority func()
25+
scopeSelector corev1.ScopeSelector
26+
normalHRQ api.HierarchicalResourceQuota
27+
)
28+
29+
BeforeEach(func() {
30+
parentNs, childNs, cleanupNs = setUpParentAndChild()
31+
scopeSelector, priorityName, cleanupPriority = genPriorityScopeSelector()
32+
33+
// For checking not-scoped HRQ with scoped HRQ.
34+
normalHRQ = setScopedHRQ("normal-hrq", parentNs, corev1.ResourceList{corev1.ResourcePods: resource.MustParse("100")}, nil)
35+
})
36+
37+
AfterEach(func() {
38+
var podCount int
39+
for _, ns := range []string{parentNs, childNs} {
40+
out, err := RunCommand("kubectl get --no-headers pods -n " + ns)
41+
Expect(err).NotTo(HaveOccurred())
42+
if strings.Contains(out, "No resources found") {
43+
continue
44+
}
45+
46+
pods := strings.Count(out, "\n")
47+
Expect(err).NotTo(HaveOccurred())
48+
podCount += pods
49+
}
50+
51+
FieldShouldContainWithTimeout("hrq", parentNs, normalHRQ.Name, ".status.used", "pods:"+strconv.Itoa(podCount), 300)
52+
53+
// For debugging after failed tests
54+
if !CurrentSpecReport().Failed() {
55+
cleanupPriority()
56+
cleanupNs()
57+
}
58+
})
59+
60+
It("should create RQs with correct limits in the descendants (including itself) for Scoped HRQs", func() {
61+
hrqA := setScopedHRQ("a-hrq", parentNs, corev1.ResourceList{corev1.ResourcePods: resource.MustParse("3")}, &scopeSelector)
62+
hrqB := setScopedHRQ("b-hrq", childNs, corev1.ResourceList{corev1.ResourcePods: resource.MustParse("2")}, &scopeSelector)
63+
64+
rqAName := api.ResourceQuotaSingletonName + "-" + hrqA.Name
65+
rqBName := api.ResourceQuotaSingletonName + "-" + hrqB.Name
66+
67+
FieldShouldContain("resourcequota", parentNs, rqAName, ".spec.hard", "pods:3")
68+
FieldShouldContain("resourcequota", childNs, rqBName, ".spec.hard", "pods:2")
69+
70+
expect := selectorStr(priorityName)
71+
FieldShouldContain("resourcequota", parentNs, rqAName, ".spec.scopeSelector", expect)
72+
FieldShouldContain("resourcequota", childNs, rqBName, ".spec.scopeSelector", expect)
73+
})
74+
75+
It("should remove obsolete (empty) RQ if there's no longer a Scoped HRQ in the ancestor", func() {
76+
hrq := setScopedHRQ("a-hrq", parentNs, corev1.ResourceList{corev1.ResourcePods: resource.MustParse("3")}, &scopeSelector)
77+
rqName := api.ResourceQuotaSingletonName + "-" + hrq.Name
78+
79+
MustRun("kubectl delete hrq -n", parentNs, hrq.Name)
80+
81+
RunShouldNotContain(rqName, propagationTime, "kubectl get resourcequota -n" , parentNs)
82+
RunShouldNotContain(rqName, propagationTime, "kubectl get resourcequota -n" , childNs)
83+
})
84+
85+
It("should update the .status.used field of the HRQ when pods are created", func() {
86+
hrq := setScopedHRQ("status-updated", parentNs, corev1.ResourceList{corev1.ResourcePods: resource.MustParse("2")}, &scopeSelector)
87+
88+
for i := 0; i < 2; i++ {
89+
_, err := mustCreatePodWithPrioirty(fmt.Sprint(i), childNs, priorityName)
90+
Expect(err).NotTo(HaveOccurred())
91+
FieldShouldContain("hrq", parentNs, hrq.Name, ".status.used", "pods:"+fmt.Sprint(i+1))
92+
}
93+
94+
_, err := mustCreatePod("normal", childNs)
95+
Expect(err).NotTo(HaveOccurred())
96+
})
97+
98+
It("should reject creating a pod if the HRQ is exceeded", func() {
99+
hrq := setScopedHRQ("ok-ng", parentNs, corev1.ResourceList{corev1.ResourcePods: resource.MustParse("1")}, &scopeSelector)
100+
101+
_, err := mustCreatePodWithPrioirty("ok", childNs, priorityName)
102+
Expect(err).NotTo(HaveOccurred())
103+
FieldShouldContain("hrq", parentNs, hrq.Name, ".status.used", "pods:1")
104+
105+
_, err = mustCreatePodWithPrioirty("ng", childNs, priorityName)
106+
Expect(err).ShouldNot(Succeed())
107+
108+
_, err = mustCreatePod("normal-pod", childNs)
109+
Expect(err).NotTo(HaveOccurred())
110+
111+
FieldShouldContain("hrq", parentNs, hrq.Name, ".status.used", "pods:1")
112+
})
113+
})
114+
115+
func mustCreatePod(prefix, nsnm string) (corev1.Pod, error) {
116+
return mustCreatePodWithPrioirty(prefix, nsnm, "")
117+
}
118+
119+
func mustCreatePodWithPrioirty(prefix, nsnm, priority string) (corev1.Pod, error) {
120+
name := prefix + "-" + uuid.New().String()
121+
spec := corev1.PodSpec{
122+
PriorityClassName: priority,
123+
Containers: []corev1.Container{
124+
{
125+
Name: "test",
126+
Image: "nginx",
127+
},
128+
},
129+
}
130+
if priority != "" {
131+
spec.PriorityClassName = priority
132+
}
133+
134+
pod := corev1.Pod {
135+
TypeMeta: metav1.TypeMeta{
136+
Kind: "Pod",
137+
APIVersion: "v1",
138+
},
139+
ObjectMeta: metav1.ObjectMeta{
140+
Name: name,
141+
Namespace: nsnm,
142+
},
143+
Spec: spec,
144+
}
145+
146+
manifest, err := yaml.Marshal(pod)
147+
Expect(err).NotTo(HaveOccurred())
148+
149+
fn := writeTempFile(string(manifest))
150+
GinkgoT().Log("Wrote " + fn + ":\n" + string(manifest))
151+
defer removeFile(fn)
152+
153+
return pod, TryRun("kubectl apply -f " + fn)
154+
}
155+
156+
func setScopedHRQ(nm, nsnm string, resourceList corev1.ResourceList, scopeSelector *corev1.ScopeSelector) api.HierarchicalResourceQuota {
157+
hrq := api.HierarchicalResourceQuota{
158+
TypeMeta: metav1.TypeMeta{
159+
Kind: "HierarchicalResourceQuota",
160+
APIVersion: "hnc.x-k8s.io/v1alpha2",
161+
},
162+
ObjectMeta: metav1.ObjectMeta{
163+
Name: nm,
164+
Namespace: nsnm,
165+
},
166+
Spec: api.HierarchicalResourceQuotaSpec{
167+
Hard: resourceList,
168+
ScopeSelector: scopeSelector,
169+
},
170+
}
171+
manifest, err := yaml.Marshal(hrq)
172+
Expect(err).NotTo(HaveOccurred())
173+
174+
MustApplyYAML(string(manifest))
175+
RunShouldContain(nm, propagationTime, "kubectl get hrq -n", nsnm, nm)
176+
177+
return hrq
178+
}
179+
180+
func setUpParentAndChild() (string, string, func()) {
181+
parentNs := createNS("hrq-test-parent-")
182+
childNs := createSubNS(parentNs)
183+
cleanup := func() {
184+
MustRunWithTimeout(cleanupTimeout, "kubectl annotate ns", parentNs, "hnc.x-k8s.io/subnamespace-of-")
185+
MustRunWithTimeout(cleanupTimeout, "kubectl annotate ns", childNs, "hnc.x-k8s.io/subnamespace-of-")
186+
var wg sync.WaitGroup
187+
for _, ns := range []string{parentNs, childNs} {
188+
wg.Add(1)
189+
go func(ns string) {
190+
MustRun("kubectl delete ns", ns)
191+
wg.Done()
192+
}(ns)
193+
}
194+
wg.Wait()
195+
}
196+
197+
RunShouldContain(childNs, propagationTime, "kubectl get ns")
198+
return parentNs, childNs, cleanup
199+
}
200+
201+
func createNS(prefix string) string {
202+
nsName := prefix + uuid.New().String()
203+
MustRun("kubectl create ns", nsName)
204+
return nsName
205+
}
206+
207+
func createSubNS(parent string) string {
208+
nsName := "hrq-test-child-" + uuid.New().String()
209+
MustRun("kubectl hns create", nsName, "-n", parent)
210+
return nsName
211+
}
212+
213+
func genPriorityScopeSelector() (corev1.ScopeSelector, string, func()) {
214+
priority := uuid.New().String()
215+
err := TryRun("kubectl create priorityclass", priority, "--value=100")
216+
Expect(err).NotTo(HaveOccurred())
217+
cleanup := func() {
218+
err := TryRun("kubectl delete priorityclass", priority)
219+
Expect(err).NotTo(HaveOccurred())
220+
}
221+
222+
return corev1.ScopeSelector{
223+
MatchExpressions: []corev1.ScopedResourceSelectorRequirement{
224+
{
225+
Operator: corev1.ScopeSelectorOpIn,
226+
ScopeName: "PriorityClass",
227+
Values: []string{priority},
228+
},
229+
},
230+
}, priority, cleanup
231+
}
232+
233+
func selectorStr(priorityName string) string {
234+
return "map[matchExpressions:[map[operator:In scopeName:PriorityClass values:[" + priorityName + "]]]]"
235+
}

test/e2e/quickstart_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
. "sigs.k8s.io/hierarchical-namespaces/pkg/testutils"
99
)
1010

11-
var _ = Describe("Quickstart", func() {
11+
var _ = Describe("Quickstart", Serial, func() {
1212
// Tests for the HNC user guide quickstarts
1313
const (
1414
nsOrg = "acme-org"

0 commit comments

Comments
 (0)