1
16
17 package storage
18
19 import (
20 "context"
21 "fmt"
22 "math/rand"
23 "time"
24
25 storagev1 "k8s.io/api/storage/v1"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/labels"
28 "k8s.io/apimachinery/pkg/types"
29 utilrand "k8s.io/apimachinery/pkg/util/rand"
30 "k8s.io/client-go/util/retry"
31 "k8s.io/kubernetes/test/e2e/framework"
32 "k8s.io/kubernetes/test/e2e/storage/utils"
33
34 "github.com/onsi/ginkgo/v2"
35 "github.com/onsi/gomega"
36 )
37
38 var _ = utils.SIGDescribe("VolumeAttachment", func() {
39
40 f := framework.NewDefaultFramework("volumeattachment")
41
42
55 ginkgo.Describe("Conformance", func() {
56
57 framework.ConformanceIt("should run through the lifecycle of a VolumeAttachment", func(ctx context.Context) {
58
59 vaClient := f.ClientSet.StorageV1().VolumeAttachments()
60
61 firstVA, vaNodeName := createVolumeAttachment(f, ctx)
62 ginkgo.By(fmt.Sprintf("Get VolumeAttachment %q on node %q", firstVA, vaNodeName))
63 retrievedVA, err := vaClient.Get(ctx, firstVA, metav1.GetOptions{})
64 framework.ExpectNoError(err, "failed to get VolumeAttachment %q", firstVA)
65 gomega.Expect(retrievedVA.Name).To(gomega.Equal(firstVA), "Checking that retrieved VolumeAttachment has the correct name")
66
67 ginkgo.By(fmt.Sprintf("Patch VolumeAttachment %q on node %q", firstVA, vaNodeName))
68 payload := "{\"metadata\":{\"labels\":{\"" + retrievedVA.Name + "\":\"patched\"}}}"
69 patchedVA, err := vaClient.Patch(ctx, retrievedVA.Name, types.MergePatchType, []byte(payload), metav1.PatchOptions{})
70 framework.ExpectNoError(err, "failed to patch PV %q", firstVA)
71 gomega.Expect(patchedVA.Labels).To(gomega.HaveKeyWithValue(patchedVA.Name, "patched"), "Checking that patched label has been applied")
72
73 patchedSelector := labels.Set{patchedVA.Name: "patched"}.AsSelector().String()
74 ginkgo.By(fmt.Sprintf("List VolumeAttachments with %q label", patchedSelector))
75 vaList, err := vaClient.List(ctx, metav1.ListOptions{LabelSelector: patchedSelector})
76 framework.ExpectNoError(err, "failed to list VolumeAttachments")
77 gomega.Expect(vaList.Items).To(gomega.HaveLen(1))
78
79 ginkgo.By(fmt.Sprintf("Delete VolumeAttachment %q on node %q", firstVA, vaNodeName))
80 err = vaClient.Delete(ctx, firstVA, metav1.DeleteOptions{})
81 framework.ExpectNoError(err, "failed to delete VolumeAttachment %q", firstVA)
82
83 ginkgo.By(fmt.Sprintf("Confirm deletion of VolumeAttachment %q on node %q", firstVA, vaNodeName))
84
85 type state struct {
86 VolumeAttachments []storagev1.VolumeAttachment
87 }
88
89 err = framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*state, error) {
90 vaList, err := vaClient.List(ctx, metav1.ListOptions{LabelSelector: patchedSelector})
91 if err != nil {
92 return nil, fmt.Errorf("failed to list VolumeAttachment: %w", err)
93 }
94 return &state{
95 VolumeAttachments: vaList.Items,
96 }, nil
97 })).WithTimeout(30 * time.Second).Should(framework.MakeMatcher(func(s *state) (func() string, error) {
98 if len(s.VolumeAttachments) == 0 {
99 return nil, nil
100 }
101 return func() string {
102 return fmt.Sprintf("Expected VolumeAttachment to be deleted, found %q", s.VolumeAttachments[0].Name)
103 }, nil
104 }))
105 framework.ExpectNoError(err, "Timeout while waiting to confirm VolumeAttachment %q deletion", firstVA)
106
107 secondVA, vaNodeName := createVolumeAttachment(f, ctx)
108 updatedLabel := map[string]string{"va-e2e": "updated"}
109 updatedSelector := labels.Set{"va-e2e": "updated"}.AsSelector().String()
110 ginkgo.By(fmt.Sprintf("Update the VolumeAttachment %q on node %q with label %q", secondVA, vaNodeName, updatedSelector))
111 var updatedVA *storagev1.VolumeAttachment
112
113 err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
114 currentVA, err := vaClient.Get(ctx, secondVA, metav1.GetOptions{})
115 framework.ExpectNoError(err, "failed to get VolumeAttachment %q", patchedVA.Name)
116 currentVA.Labels = updatedLabel
117 updatedVA, err = vaClient.Update(ctx, currentVA, metav1.UpdateOptions{})
118
119 return err
120 })
121 framework.ExpectNoError(err, "failed to update VolumeAttachment %q on node %q", secondVA, vaNodeName)
122 gomega.Expect(updatedVA.Labels).To(gomega.HaveKeyWithValue("va-e2e", "updated"), "Checking that updated label has been applied")
123
124 thirdVA, vaNodeName := createVolumeAttachment(f, ctx)
125 ginkgo.By(fmt.Sprintf("Update the VolumeAttachment %q on node %q with label %q", thirdVA, vaNodeName, updatedSelector))
126
127 err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
128 currentVA, err := vaClient.Get(ctx, thirdVA, metav1.GetOptions{})
129 framework.ExpectNoError(err, "failed to get VolumeAttachment %q", patchedVA.Name)
130 currentVA.Labels = updatedLabel
131 updatedVA, err = vaClient.Update(ctx, currentVA, metav1.UpdateOptions{})
132
133 return err
134 })
135 framework.ExpectNoError(err, "failed to update VolumeAttachment %q on node %q", thirdVA, vaNodeName)
136 gomega.Expect(updatedVA.Labels).To(gomega.HaveKeyWithValue("va-e2e", "updated"), "Checking that updated label has been applied")
137
138 ginkgo.By(fmt.Sprintf("DeleteCollection of VolumeAttachments with %q label", updatedSelector))
139 err = vaClient.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: updatedSelector})
140 framework.ExpectNoError(err, "failed to delete VolumeAttachment collection")
141
142 ginkgo.By(fmt.Sprintf("Confirm deleteCollection of VolumeAttachments with %q label", updatedSelector))
143
144 err = framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*state, error) {
145 vaList, err := vaClient.List(ctx, metav1.ListOptions{LabelSelector: updatedSelector})
146 if err != nil {
147 return nil, fmt.Errorf("failed to list VolumeAttachment: %w", err)
148 }
149 return &state{
150 VolumeAttachments: vaList.Items,
151 }, nil
152 })).WithTimeout(30 * time.Second).Should(framework.MakeMatcher(func(s *state) (func() string, error) {
153 if len(s.VolumeAttachments) == 0 {
154 return nil, nil
155 }
156 return func() string {
157 list := []string{}
158 for _, va := range s.VolumeAttachments {
159 list = append(list, va.Name)
160 }
161
162 return fmt.Sprintf("Expected VolumeAttachment(s) to be deleted, found %v", list)
163 }, nil
164 }))
165 framework.ExpectNoError(err, "Timeout while waiting to confirm deletion of all VolumeAttachments")
166 })
167 })
168 })
169
170 func NewVolumeAttachment(vaName, pvName, nodeName string, status bool) *storagev1.VolumeAttachment {
171 return &storagev1.VolumeAttachment{
172
173 ObjectMeta: metav1.ObjectMeta{
174 UID: types.UID(vaName),
175 Name: vaName,
176 },
177 Spec: storagev1.VolumeAttachmentSpec{
178 Attacher: "e2e-test.storage.k8s.io",
179 NodeName: nodeName,
180 Source: storagev1.VolumeAttachmentSource{
181 PersistentVolumeName: &pvName,
182 },
183 },
184 Status: storagev1.VolumeAttachmentStatus{
185 Attached: status,
186 },
187 }
188 }
189
190 func createVolumeAttachment(f *framework.Framework, ctx context.Context) (string, string) {
191
192 randUID := "e2e-" + utilrand.String(5)
193 vaName := "va-" + randUID
194 pvName := "pv-" + randUID
195
196 nodes, err := f.ClientSet.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
197 framework.ExpectNoError(err, "failed to list nodes")
198 randNode := rand.Intn(len(nodes.Items))
199 vaNodeName := nodes.Items[randNode].Name
200 vaAttachStatus := false
201
202 ginkgo.By(fmt.Sprintf("Create VolumeAttachment %q on node %q", vaName, vaNodeName))
203 va := NewVolumeAttachment(vaName, pvName, vaNodeName, vaAttachStatus)
204
205 createdVA, err := f.ClientSet.StorageV1().VolumeAttachments().Create(ctx, va, metav1.CreateOptions{})
206 framework.ExpectNoError(err, "failed to create VolumeAttachment %q", vaName)
207 gomega.Expect(createdVA.Name).To(gomega.Equal(vaName), "Checking that the created VolumeAttachment has the correct name")
208
209 return createdVA.Name, vaNodeName
210 }
211
View as plain text