Skip to content

Commit 52d6cb5

Browse files
authored
tests: add tests for custom NodeAffinityLabels feature (#197)
Signed-off-by: Niladri Halder <[email protected]>
1 parent cab53c4 commit 52d6cb5

File tree

3 files changed

+237
-95
lines changed

3 files changed

+237
-95
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package tests
2+
3+
import (
4+
ctx "context"
5+
"time"
6+
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
appsv1 "k8s.io/api/apps/v1"
10+
corev1 "k8s.io/api/core/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
13+
pvc "github.com/openebs/dynamic-localpv-provisioner/pkg/kubernetes/api/core/v1/persistentvolumeclaim"
14+
sc "github.com/openebs/dynamic-localpv-provisioner/pkg/kubernetes/api/storage/v1/storageclass"
15+
)
16+
17+
var _ = Describe("VOLUME PROVISIONING/DE-PROVISIONING WITH 'NodeAffinityLabels' CAS-CONFIG ON STORAGECLASS", func() {
18+
var (
19+
pvcNamePrefix = "pvc-nod-aff-lab"
20+
scNamePrefix = "sc-nod-aff-lab"
21+
deployNamePrefix = "busybox-nod-aff-lab"
22+
deployment *appsv1.Deployment
23+
scName string
24+
pvcName string
25+
pvcCapacity = "2Gi"
26+
pvName string
27+
scNodeAffinityLabelKeys = []string{"kubernetes.io/hostname", "kubernetes.io/os", "kubernetes.io/arch"}
28+
)
29+
30+
When("an application with a PVC which has custom NodeAffinityLabels cas-config on the StorageClass, is created", func() {
31+
It("should provision the volume", func() {
32+
By("creating the StorageClass with custom NodeAffinityLabels", func() {
33+
storageClass, err := sc.NewStorageClass(
34+
sc.WithGenerateName(scNamePrefix),
35+
sc.WithLabels(map[string]string{
36+
"openebs.io/test-sc": "true",
37+
}),
38+
sc.WithLocalPV(),
39+
sc.WithHostpath(hostpathDir),
40+
sc.WithNodeAffinityLabels(scNodeAffinityLabelKeys),
41+
sc.WithVolumeBindingMode("WaitForFirstConsumer"),
42+
sc.WithReclaimPolicy("Delete"),
43+
)
44+
Expect(err).To(
45+
BeNil(),
46+
"while building StorageClass with name prefix {%s}",
47+
scNamePrefix,
48+
)
49+
storageClass, err = ops.SCClient.Create(ctx.TODO(), storageClass)
50+
Expect(err).To(
51+
BeNil(),
52+
"while creating StorageClass with name prefix %s",
53+
scNamePrefix,
54+
)
55+
scName = storageClass.Name
56+
})
57+
By("creating the PVC with the StorageClass "+scName, func() {
58+
pvc, err := pvc.NewBuilder().
59+
WithGenerateName(pvcNamePrefix).
60+
WithNamespace(namespaceObj.Name).
61+
WithStorageClass(scName).
62+
WithAccessModes([]corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}).
63+
WithCapacity(pvcCapacity).
64+
Build()
65+
Expect(err).To(
66+
BeNil(),
67+
"while building PVC with name prefix %s in namespace %s",
68+
pvcNamePrefix,
69+
namespaceObj.Name,
70+
)
71+
pvc, err = ops.PVCClient.WithNamespace(namespaceObj.Name).Create(ctx.TODO(), pvc)
72+
Expect(err).To(
73+
BeNil(),
74+
"while creating PVC with name prefix %s in namespace %s",
75+
pvcNamePrefix,
76+
namespaceObj.Name,
77+
)
78+
pvcName = pvc.Name
79+
})
80+
By("creating a bound PV", func() {
81+
deployment, err = ops.createDeploymentWhichConsumesHostpath(deployNamePrefix, namespaceObj.Name,
82+
pvcName)
83+
Expect(err).To(
84+
BeNil(),
85+
"while creating Deployment with name %s in namespace %s",
86+
deployment.Name,
87+
namespaceObj.Name,
88+
)
89+
Expect(ops.IsPVCBoundEventually(namespaceObj.Name, pvcName)).To(
90+
BeTrue(),
91+
"while checking if the PVC %s in namespace %s was bound to a PV",
92+
pvcName, namespaceObj.Name,
93+
)
94+
Expect(ops.GetPodRunningCountEventually(namespaceObj.Name, "app="+deployNamePrefix, 1)).To(BeNumerically("==",
95+
1))
96+
Expect(Eventually(func() string {
97+
pvName = ops.GetPVNameFromPVCName(namespaceObj.Name, pvcName)
98+
return pvName
99+
}).
100+
WithTimeout(time.Minute).
101+
WithPolling(time.Second).
102+
WithContext(ctx.TODO()).
103+
Should(Not(BeEmpty()))).To(BeTrue())
104+
})
105+
106+
By("having the SC NodeAffinityLabels cas-config set correctly", func() {
107+
nodeAffinityLabelKeys, err := ops.GetNodeAffinityLabelKeysFromPv(pvName)
108+
Expect(err).To(BeNil(), "while getting NodeAffinityLabels from PV '%s'", pvName)
109+
Expect(isLabelSelectorsEqual(scNodeAffinityLabelKeys, nodeAffinityLabelKeys)).To(
110+
BeTrue(),
111+
"while checking if PV %s had the NodeAffinityLabels requested on the SC %s",
112+
scName,
113+
)
114+
})
115+
})
116+
})
117+
118+
When("an application with a PV which has custom NodeAffinityLabels is deleted", func() {
119+
It("should de-provision the volume", func() {
120+
By("deleting the PV", func() {
121+
podList, err := ops.PodClient.List(ctx.TODO(), metav1.ListOptions{LabelSelector: "app=" + deployNamePrefix})
122+
Expect(err).To(BeNil(), "while listing Pods for busybox application deployment")
123+
Expect(len(podList.Items)).To(BeNumerically("==", 1))
124+
pod := &podList.Items[0]
125+
err = ops.DeployClient.WithNamespace(namespaceObj.Name).Delete(ctx.TODO(), deployment.Name, &metav1.DeleteOptions{})
126+
Expect(err).To(BeNil(), "while deleting busybox application deployment")
127+
Expect(ops.IsPodDeletedEventually(pod.Namespace, pod.Name)).To(
128+
BeTrue(),
129+
"while checking to see if the Pod %s in namespace %s for the busybox deployment is deleted",
130+
pod.Name, pod.Namespace,
131+
)
132+
133+
ops.DeletePersistentVolumeClaim(pvcName, namespaceObj.Name)
134+
Expect(ops.IsPVCDeletedEventually(pvcName, namespaceObj.Name)).To(
135+
BeTrue(),
136+
"while checking if PVC %s in namespace %s is deleted",
137+
pvcName, namespaceObj.Namespace,
138+
)
139+
Expect(ops.IsPVDeletedEventually(pvName)).To(
140+
BeTrue(),
141+
"when checking to see if the underlying PV %s is deleted",
142+
pvName,
143+
)
144+
})
145+
})
146+
})
147+
})

tests/operations.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,3 +1146,89 @@ func BuildPod(namespace, podName, pvcName string, labelselector map[string]strin
11461146
).
11471147
Build()
11481148
}
1149+
1150+
// createDeploymentWhichConsumesHostpath creates a single-replica Deployment whose Pod consumes hostpath PVC.
1151+
func (ops *Operations) createDeploymentWhichConsumesHostpath(namePrefix, namespace, pvcName string) (*appsv1.Deployment, error) {
1152+
labelSelector := map[string]string{
1153+
"app": namePrefix,
1154+
}
1155+
deployment, err := deploy.NewBuilder().
1156+
WithGenerateName(namePrefix).
1157+
WithNamespace(namespace).
1158+
WithLabelsNew(labelSelector).
1159+
WithSelectorMatchLabelsNew(labelSelector).
1160+
WithPodTemplateSpecBuilder(
1161+
pts.NewBuilder().
1162+
WithLabelsNew(labelSelector).
1163+
WithContainerBuildersNew(
1164+
container.NewBuilder().
1165+
WithName("busybox").
1166+
WithImage("busybox").
1167+
WithCommandNew(
1168+
[]string{
1169+
"sleep",
1170+
"3600",
1171+
},
1172+
).
1173+
WithVolumeMountsNew(
1174+
[]corev1.VolumeMount{
1175+
{
1176+
Name: "demo-vol1",
1177+
MountPath: "/mnt/store1",
1178+
},
1179+
},
1180+
),
1181+
).
1182+
WithVolumeBuilders(
1183+
k8svolume.NewBuilder().
1184+
WithName("demo-vol1").
1185+
WithPVCSource(pvcName),
1186+
),
1187+
).
1188+
Build()
1189+
if err != nil {
1190+
return nil, err
1191+
}
1192+
1193+
return ops.DeployClient.WithNamespace(namespace).Create(context.TODO(), deployment)
1194+
}
1195+
1196+
// isLabelSelectorsEqual compares two arrays of label selector keys.
1197+
func isLabelSelectorsEqual(request, result []string) bool {
1198+
if len(request) != len(result) {
1199+
return false
1200+
}
1201+
1202+
ch := make(chan struct{}, 2)
1203+
collectFrequency := func(labelKeys []string, freq *map[string]int) {
1204+
for _, elem := range labelKeys {
1205+
(*freq)[elem]++
1206+
}
1207+
1208+
ch <- struct{}{}
1209+
}
1210+
1211+
// Maps to hold the frequency of strings in the string slices.
1212+
freqRequest := make(map[string]int)
1213+
freqResult := make(map[string]int)
1214+
1215+
go collectFrequency(request, &freqRequest)
1216+
go collectFrequency(result, &freqResult)
1217+
1218+
for i := 0; i < 2; i++ {
1219+
select {
1220+
case <-ch:
1221+
continue
1222+
}
1223+
}
1224+
1225+
// Compare frequencies
1226+
for key, countRequest := range freqRequest {
1227+
countResult, ok := freqResult[key]
1228+
if !ok || countRequest != countResult {
1229+
return false
1230+
}
1231+
}
1232+
1233+
return true
1234+
}

tests/pvc_cas_config_test.go

Lines changed: 4 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -7,106 +7,15 @@ import (
77
. "github.com/onsi/ginkgo/v2"
88
. "github.com/onsi/gomega"
99
mayav1alpha1 "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
10-
"golang.org/x/net/context"
1110
appsv1 "k8s.io/api/apps/v1"
1211
corev1 "k8s.io/api/core/v1"
1312
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1413
"sigs.k8s.io/yaml"
1514

16-
deploy "github.com/openebs/dynamic-localpv-provisioner/pkg/kubernetes/api/apps/v1/deployment"
17-
"github.com/openebs/dynamic-localpv-provisioner/pkg/kubernetes/api/core/v1/container"
1815
pvc "github.com/openebs/dynamic-localpv-provisioner/pkg/kubernetes/api/core/v1/persistentvolumeclaim"
19-
pts "github.com/openebs/dynamic-localpv-provisioner/pkg/kubernetes/api/core/v1/podtemplatespec"
20-
k8svolume "github.com/openebs/dynamic-localpv-provisioner/pkg/kubernetes/api/core/v1/volume"
2116
sc "github.com/openebs/dynamic-localpv-provisioner/pkg/kubernetes/api/storage/v1/storageclass"
2217
)
2318

24-
// createDeploymentWhichConsumesHostpath creates a single-replica Deployment whose Pod consumes hostpath PVC.
25-
func createDeploymentWhichConsumesHostpath(namePrefix, namespace, pvcName string) (*appsv1.Deployment, error) {
26-
labelSelector := map[string]string{
27-
"app": namePrefix,
28-
}
29-
deployment, err := deploy.NewBuilder().
30-
WithGenerateName(namePrefix).
31-
WithNamespace(namespace).
32-
WithLabelsNew(labelSelector).
33-
WithSelectorMatchLabelsNew(labelSelector).
34-
WithPodTemplateSpecBuilder(
35-
pts.NewBuilder().
36-
WithLabelsNew(labelSelector).
37-
WithContainerBuildersNew(
38-
container.NewBuilder().
39-
WithName("busybox").
40-
WithImage("busybox").
41-
WithCommandNew(
42-
[]string{
43-
"sleep",
44-
"3600",
45-
},
46-
).
47-
WithVolumeMountsNew(
48-
[]corev1.VolumeMount{
49-
{
50-
Name: "demo-vol1",
51-
MountPath: "/mnt/store1",
52-
},
53-
},
54-
),
55-
).
56-
WithVolumeBuilders(
57-
k8svolume.NewBuilder().
58-
WithName("demo-vol1").
59-
WithPVCSource(pvcName),
60-
),
61-
).
62-
Build()
63-
if err != nil {
64-
return nil, err
65-
}
66-
67-
return ops.DeployClient.WithNamespace(namespaceObj.Name).Create(context.TODO(), deployment)
68-
}
69-
70-
// isLabelSelectorsEqual compares two arrays of label selector keys.
71-
func isLabelSelectorsEqual(request, result []string) bool {
72-
if len(request) != len(result) {
73-
return false
74-
}
75-
76-
ch := make(chan struct{}, 2)
77-
collectFrequency := func(labelKeys []string, freq *map[string]int) {
78-
for _, elem := range labelKeys {
79-
(*freq)[elem]++
80-
}
81-
82-
ch <- struct{}{}
83-
}
84-
85-
// Maps to hold the frequency of strings in the string slices.
86-
freqRequest := make(map[string]int)
87-
freqResult := make(map[string]int)
88-
89-
go collectFrequency(request, &freqRequest)
90-
go collectFrequency(result, &freqResult)
91-
92-
for i := 0; i < 2; i++ {
93-
select {
94-
case <-ch:
95-
continue
96-
}
97-
}
98-
99-
// Compare frequencies
100-
for key, countRequest := range freqRequest {
101-
countResult, ok := freqResult[key]
102-
if !ok || countRequest != countResult {
103-
return false
104-
}
105-
}
106-
107-
return true
108-
}
109-
11019
var _ = Describe("VOLUME PROVISIONING/DE-PROVISIONING WITH ADDITIVE CAS-CONFIGS ON PVC AND SC", func() {
11120
var (
11221
pvcNamePrefix = "pvc-additive-cas-config"
@@ -125,7 +34,7 @@ var _ = Describe("VOLUME PROVISIONING/DE-PROVISIONING WITH ADDITIVE CAS-CONFIGS
12534
It("should provision the volume", func() {
12635
By("creating the StorageClass with cas-config", func() {
12736
storageClass, err := sc.NewStorageClass(
128-
sc.WithGenerateName("sc-additive-cas-config"),
37+
sc.WithGenerateName(scNamePrefix),
12938
sc.WithLabels(map[string]string{
13039
"openebs.io/test-sc": "true",
13140
}),
@@ -185,7 +94,7 @@ var _ = Describe("VOLUME PROVISIONING/DE-PROVISIONING WITH ADDITIVE CAS-CONFIGS
18594
pvcName = pvc.Name
18695
})
18796
By("creating a bound PV", func() {
188-
deployment, err = createDeploymentWhichConsumesHostpath(deployNamePrefix, namespaceObj.Name,
97+
deployment, err = ops.createDeploymentWhichConsumesHostpath(deployNamePrefix, namespaceObj.Name,
18998
pvcName)
19099
Expect(err).To(
191100
BeNil(),
@@ -272,7 +181,7 @@ var _ = Describe("VOLUME PROVISIONING/DE-PROVISIONING WITH CONFLICTING CAS-CONFI
272181
It("should provision the volume", func() {
273182
By("creating the StorageClass with cas-config", func() {
274183
storageClass, err := sc.NewStorageClass(
275-
sc.WithGenerateName("sc-conflicting-cas-config"),
184+
sc.WithGenerateName(scNamePrefix),
276185
sc.WithLabels(map[string]string{
277186
"openebs.io/test-sc": "true",
278187
}),
@@ -332,7 +241,7 @@ var _ = Describe("VOLUME PROVISIONING/DE-PROVISIONING WITH CONFLICTING CAS-CONFI
332241
pvcName = pvc.Name
333242
})
334243
By("creating a bound PV", func() {
335-
deployment, err = createDeploymentWhichConsumesHostpath(deployNamePrefix, namespaceObj.Name,
244+
deployment, err = ops.createDeploymentWhichConsumesHostpath(deployNamePrefix, namespaceObj.Name,
336245
pvcName)
337246
Expect(err).To(
338247
BeNil(),

0 commit comments

Comments
 (0)