Skip to content

Commit d995814

Browse files
authored
bdd(node-affinity): verifying NFS PV provisioning error if backend storageclass is having invalid topology (#90)
Signed-off-by: mayank <[email protected]>
1 parent bab5c4e commit d995814

File tree

1 file changed

+329
-0
lines changed

1 file changed

+329
-0
lines changed
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
/*
2+
Copyright 2021 The OpenEBS Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package tests
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
"time"
23+
24+
"github.com/ghodss/yaml"
25+
. "github.com/onsi/ginkgo"
26+
. "github.com/onsi/gomega"
27+
pvc "github.com/openebs/dynamic-nfs-provisioner/pkg/kubernetes/api/core/v1/persistentvolumeclaim"
28+
provisioner "github.com/openebs/dynamic-nfs-provisioner/provisioner"
29+
mayav1alpha1 "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
30+
corev1 "k8s.io/api/core/v1"
31+
storagev1 "k8s.io/api/storage/v1"
32+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
33+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34+
)
35+
36+
var _ = Describe("TEST PROVISIONING WITH DIFFERENT TOPOLOGY FOR BACKEND SC", func() {
37+
var (
38+
// PVC related options
39+
applicationNamespace = "default"
40+
pvcName = "different-node-affinity-pvc"
41+
accessModes = []corev1.PersistentVolumeAccessMode{corev1.ReadWriteMany}
42+
nodeAffinityKeys = []string{"kubernetes.io/hostname"}
43+
capacity = "2Gi"
44+
scName = "nfs-sc-different-node-affinity"
45+
46+
// NFS Provisioner related options
47+
openebsNamespace = "openebs"
48+
nfsProvisionerLabel = "openebs.io/component-name=openebs-nfs-provisioner"
49+
nfsProvisionerContainerName = "openebs-provisioner-nfs"
50+
nodeAffinityKeyValues map[string][]string
51+
backendScName = "different-node-affinity"
52+
backendScNodeAffinityLabel = "invalid.io/invalid-nfs-label"
53+
backendPVCName = ""
54+
)
55+
56+
When("node affinity environment variable is added", func() {
57+
It("should be applied", func() {
58+
nodeList, err := Client.listNodes("")
59+
Expect(err).To(BeNil(), "failed to list nodes")
60+
var nodeAffinityAsValue string
61+
62+
nodeAffinityKeyValues = make(map[string][]string, len(nodeAffinityKeys))
63+
// Form affinity rules from multiple nodes
64+
for _, node := range nodeList.Items {
65+
for _, key := range nodeAffinityKeys {
66+
if value, isExist := node.Labels[key]; isExist {
67+
nodeAffinityKeyValues[key] = append(nodeAffinityKeyValues[key], value)
68+
}
69+
}
70+
}
71+
72+
for key, values := range nodeAffinityKeyValues {
73+
nodeAffinityAsValue += key + ":["
74+
for _, value := range values {
75+
nodeAffinityAsValue += value + ","
76+
}
77+
// remove extra comma
78+
nodeAffinityAsValue = nodeAffinityAsValue[:len(nodeAffinityAsValue)-1]
79+
nodeAffinityAsValue += "],"
80+
}
81+
// remove extra comma and add key as affinity rules
82+
nodeAffinityAsValue = nodeAffinityAsValue[:len(nodeAffinityAsValue)-1] + ",kubernetes.io/arch"
83+
nodeAffinityKeyValues["kubernetes.io/arch"] = []string{}
84+
85+
deploymentList, err := Client.listDeployments(openebsNamespace, nfsProvisionerLabel)
86+
Expect(err).To(BeNil(), "failed to list NFS Provisioner deployments")
87+
88+
nfsProvisionerDeployment := deploymentList.Items[0]
89+
for index, containerDetails := range nfsProvisionerDeployment.Spec.Template.Spec.Containers {
90+
if containerDetails.Name == nfsProvisionerContainerName {
91+
nfsProvisionerDeployment.Spec.
92+
Template.
93+
Spec.
94+
Containers[index].Env = append(
95+
nfsProvisionerDeployment.Spec.
96+
Template.
97+
Spec.
98+
Containers[index].Env,
99+
corev1.EnvVar{
100+
Name: string(provisioner.NodeAffinityKey),
101+
Value: nodeAffinityAsValue,
102+
},
103+
)
104+
break
105+
}
106+
}
107+
108+
err = Client.applyDeployment(&nfsProvisionerDeployment)
109+
Expect(err).To(BeNil(), "failed to add %s env to NFS Provisioner", provisioner.NodeAffinityKey)
110+
})
111+
})
112+
113+
When(fmt.Sprintf("create backend storageclass %s with nodeAffinityLabel=%s", backendScName, backendScNodeAffinityLabel), func() {
114+
It("should create backend storageclass", func() {
115+
By("creating storageclass")
116+
casObj := []mayav1alpha1.Config{
117+
{
118+
Name: "StorageType",
119+
Value: "hostpath",
120+
},
121+
{
122+
Name: "BasePath",
123+
Value: "/tmp/openebs",
124+
},
125+
{
126+
// Ref: https://github.com/openebs/dynamic-localpv-provisioner/blob/develop/cmd/provisioner-localpv/app/config.go#L103
127+
Name: "NodeAffinityLabel",
128+
Value: backendScNodeAffinityLabel,
129+
},
130+
}
131+
132+
casObjStr, err := yaml.Marshal(casObj)
133+
Expect(err).To(BeNil(), "while marshaling cas object")
134+
135+
err = Client.createStorageClass(&storagev1.StorageClass{
136+
ObjectMeta: metav1.ObjectMeta{
137+
Name: backendScName,
138+
Annotations: map[string]string{
139+
string(mayav1alpha1.CASTypeKey): "local",
140+
string(mayav1alpha1.CASConfigKey): string(casObjStr),
141+
},
142+
},
143+
Provisioner: "openebs.io/local",
144+
})
145+
Expect(err).To(BeNil(), "while creating SC{%s}", scName)
146+
})
147+
})
148+
149+
When(fmt.Sprintf("create storageclass with backendStorageclass=%s", backendScName), func() {
150+
It("should create storageclass", func() {
151+
By("creating storageclass")
152+
casObj := []mayav1alpha1.Config{
153+
{
154+
Name: provisioner.KeyPVNFSServerType,
155+
Value: "kernel",
156+
},
157+
{
158+
Name: provisioner.KeyPVBackendStorageClass,
159+
Value: backendScName,
160+
},
161+
}
162+
163+
casObjStr, err := yaml.Marshal(casObj)
164+
Expect(err).To(BeNil(), "while marshaling cas object")
165+
166+
err = Client.createStorageClass(&storagev1.StorageClass{
167+
ObjectMeta: metav1.ObjectMeta{
168+
Name: scName,
169+
Annotations: map[string]string{
170+
string(mayav1alpha1.CASTypeKey): "nfsrwx",
171+
string(mayav1alpha1.CASConfigKey): string(casObjStr),
172+
},
173+
},
174+
Provisioner: "openebs.io/nfsrwx",
175+
})
176+
Expect(err).To(BeNil(), "while creating SC{%s}", scName)
177+
})
178+
})
179+
180+
When(fmt.Sprintf("pvc with storageclass=%s is created", scName), func() {
181+
It("should create NFS Server with affinity rules", func() {
182+
By("building a pvc")
183+
pvcObj, err := pvc.NewBuilder().
184+
WithName(pvcName).
185+
WithNamespace(applicationNamespace).
186+
WithStorageClass(scName).
187+
WithAccessModes(accessModes).
188+
WithCapacity(capacity).Build()
189+
Expect(err).ShouldNot(HaveOccurred(), "while building pvc object %s/%s", applicationNamespace, pvcName)
190+
191+
By("creating above pvc")
192+
err = Client.createPVC(pvcObj)
193+
Expect(err).To(BeNil(), "while creating pvc %s/%s", applicationNamespace, pvcName)
194+
})
195+
})
196+
197+
When("verifying events for PVC", func() {
198+
It("should have event for timeout error", func() {
199+
By("fetching a pvc")
200+
pvcObj, err := Client.getPVC(applicationNamespace, pvcName)
201+
Expect(err).ShouldNot(HaveOccurred(), "while fetching pvc %s/%s", applicationNamespace, pvcName)
202+
203+
backendPVCName = "nfs-pvc-" + string(pvcObj.UID)
204+
205+
maxRetry := 25
206+
retryPeriod := 5 * time.Second
207+
var foundProvisioningFailedEvent bool
208+
for maxRetry != 0 && !foundProvisioningFailedEvent {
209+
events, err := Client.listEvents(applicationNamespace)
210+
Expect(err).To(BeNil(), "while fetching events for namespace {%s}", applicationNamespace)
211+
212+
for _, cn := range events.Items {
213+
if strings.Contains(cn.Message, fmt.Sprintf("timed out waiting for PVC{openebs/nfs-pvc-%s", pvcObj.UID)) {
214+
foundProvisioningFailedEvent = true
215+
break
216+
}
217+
}
218+
time.Sleep(retryPeriod)
219+
maxRetry--
220+
}
221+
Expect(foundProvisioningFailedEvent).Should(BeTrue(), "while checking for ProvisioningFailed event")
222+
})
223+
It("should have event for backendPVC", func() {
224+
maxRetry := 25
225+
retryPeriod := 5 * time.Second
226+
var foundProvisioningFailedEvent bool
227+
for maxRetry != 0 && !foundProvisioningFailedEvent {
228+
events, err := Client.listEvents(openebsNamespace)
229+
Expect(err).To(BeNil(), "while fetching events for namespace {%s}", openebsNamespace)
230+
231+
for _, cn := range events.Items {
232+
if strings.Contains(cn.Message,
233+
fmt.Sprintf("failed to provision volume with StorageClass \"%s\": configuration error, no node was specified", backendScName)) {
234+
if cn.InvolvedObject.Name == backendPVCName {
235+
foundProvisioningFailedEvent = true
236+
break
237+
}
238+
}
239+
}
240+
time.Sleep(retryPeriod)
241+
maxRetry--
242+
}
243+
Expect(foundProvisioningFailedEvent).Should(BeTrue(), "while checking for ProvisioningFailed event")
244+
})
245+
It("should have event for nfs-server pod", func() {
246+
nfsServerLabelSelector := "openebs.io/nfs-server=" + backendPVCName
247+
nfsServerPodList, err := Client.listPods(openebsNamespace, nfsServerLabelSelector)
248+
Expect(err).To(BeNil(), "while listing NFS Server pods")
249+
250+
podName := nfsServerPodList.Items[0].Name
251+
252+
maxRetry := 25
253+
retryPeriod := 5 * time.Second
254+
var foundFailedSchedulingEvent bool
255+
for maxRetry != 0 && !foundFailedSchedulingEvent {
256+
events, err := Client.listEvents(openebsNamespace)
257+
Expect(err).To(BeNil(), "while fetching events for namespace {%s}", openebsNamespace)
258+
259+
for _, cn := range events.Items {
260+
if strings.Contains(cn.Message, "0/1 nodes are available: 1 pod has unbound immediate PersistentVolumeClaims.") {
261+
if cn.InvolvedObject.Name == podName {
262+
foundFailedSchedulingEvent = true
263+
break
264+
}
265+
}
266+
}
267+
time.Sleep(retryPeriod)
268+
maxRetry--
269+
}
270+
Expect(foundFailedSchedulingEvent).Should(BeTrue(), "while checking for FailedScheduling event")
271+
})
272+
})
273+
274+
When(fmt.Sprintf("pvc with storageclass=%s is deleted", scName), func() {
275+
It("should delete the pvc", func() {
276+
By(fmt.Sprintf("deleting pvc %s/%s", applicationNamespace, pvcName))
277+
err := Client.deletePVC(applicationNamespace, pvcName)
278+
Expect(err).To(BeNil(), "while deleting pvc %s/%s", applicationNamespace, pvcName)
279+
280+
for {
281+
_, err = Client.getPVC(applicationNamespace, pvcName)
282+
if err != nil && k8serrors.IsNotFound(err) {
283+
break
284+
}
285+
fmt.Printf("Waiting for PVC {%s} in namespace {%s} to get delete \n", pvcName, applicationNamespace)
286+
time.Sleep(time.Second * 2)
287+
}
288+
})
289+
})
290+
291+
When("node affinity rules are removed from env", func() {
292+
It("should remove from the NFS provisioner", func() {
293+
deploymentList, err := Client.listDeployments(openebsNamespace, nfsProvisionerLabel)
294+
Expect(err).To(BeNil(), "failed to list NFS Provisioner deployments")
295+
296+
nfsProvisionerDeployment := deploymentList.Items[0]
297+
for cIndex, containerDetails := range nfsProvisionerDeployment.Spec.Template.Spec.Containers {
298+
if containerDetails.Name == nfsProvisionerContainerName {
299+
envIndex := 0
300+
for _, envVar := range containerDetails.Env {
301+
if envVar.Name == string(provisioner.NodeAffinityKey) {
302+
break
303+
}
304+
envIndex++
305+
}
306+
nfsProvisionerDeployment.Spec.Template.Spec.Containers[cIndex].Env = append(nfsProvisionerDeployment.Spec.Template.Spec.Containers[cIndex].Env[:envIndex], nfsProvisionerDeployment.Spec.Template.Spec.Containers[cIndex].Env[envIndex+1:]...)
307+
break
308+
}
309+
}
310+
311+
err = Client.applyDeployment(&nfsProvisionerDeployment)
312+
Expect(err).To(BeNil(), "failed to add %s env to NFS Provisioner", provisioner.NodeAffinityKey)
313+
})
314+
})
315+
When(fmt.Sprintf("StorageClass %s is deleted", scName), func() {
316+
It("should delete the storageclass", func() {
317+
By("deleting storageclass")
318+
err := Client.deleteStorageClass(scName)
319+
Expect(err).To(BeNil(), "while deleting sc {%s}", scName)
320+
})
321+
})
322+
323+
When(fmt.Sprintf("backend storageclass %s is deleted", backendScName), func() {
324+
It("should delete the storageclass", func() {
325+
err := Client.deleteStorageClass(backendScName)
326+
Expect(err).To(BeNil(), "while deleting storageclass=%s", backendScName)
327+
})
328+
})
329+
})

0 commit comments

Comments
 (0)