1
16
17 package storage
18
19 import (
20 "context"
21 "time"
22
23 storagev1 "k8s.io/api/storage/v1"
24 apierrors "k8s.io/apimachinery/pkg/api/errors"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 types "k8s.io/apimachinery/pkg/types"
27 "k8s.io/apimachinery/pkg/util/wait"
28 "k8s.io/apimachinery/pkg/watch"
29 "k8s.io/kubernetes/test/e2e/framework"
30 "k8s.io/kubernetes/test/e2e/storage/utils"
31 admissionapi "k8s.io/pod-security-admission/api"
32
33 "github.com/onsi/ginkgo/v2"
34 "github.com/onsi/gomega"
35 )
36
37 var _ = utils.SIGDescribe("CSIStorageCapacity", func() {
38 f := framework.NewDefaultFramework("csistoragecapacity")
39 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
40
41
50 framework.ConformanceIt("should support CSIStorageCapacities API operations", func(ctx context.Context) {
51
52 cscVersion := "v1"
53 cscClient := f.ClientSet.StorageV1().CSIStorageCapacities(f.Namespace.Name)
54 cscClientNoNamespace := f.ClientSet.StorageV1().CSIStorageCapacities("")
55
56
57 scName := "e2e.example.com"
58
59
60 newCSIStorageCapacity := func(nameSuffix string) *storagev1.CSIStorageCapacity {
61 return &storagev1.CSIStorageCapacity{
62 ObjectMeta: metav1.ObjectMeta{
63 Name: f.UniqueName + nameSuffix,
64 Labels: map[string]string{
65 "test": f.UniqueName,
66 },
67 },
68 StorageClassName: scName,
69 }
70 }
71 csc := newCSIStorageCapacity("-csc1")
72 csc2 := newCSIStorageCapacity("-csc2")
73 csc3 := newCSIStorageCapacity("-csc3")
74
75
76
77 ginkgo.By("getting /apis")
78 {
79 discoveryGroups, err := f.ClientSet.Discovery().ServerGroups()
80 framework.ExpectNoError(err)
81 found := false
82 for _, group := range discoveryGroups.Groups {
83 if group.Name == storagev1.GroupName {
84 for _, version := range group.Versions {
85 if version.Version == cscVersion {
86 found = true
87 break
88 }
89 }
90 }
91 }
92 if !found {
93 framework.Failf("expected CSIStorageCapacity API group/version, got %#v", discoveryGroups.Groups)
94 }
95 }
96
97 ginkgo.By("getting /apis/storage.k8s.io")
98 {
99 group := &metav1.APIGroup{}
100 err := f.ClientSet.Discovery().RESTClient().Get().AbsPath("/apis/storage.k8s.io").Do(ctx).Into(group)
101 framework.ExpectNoError(err)
102 found := false
103 for _, version := range group.Versions {
104 if version.Version == cscVersion {
105 found = true
106 break
107 }
108 }
109 if !found {
110 framework.Failf("expected CSIStorageCapacity API version, got %#v", group.Versions)
111 }
112 }
113
114 ginkgo.By("getting /apis/storage.k8s.io/" + cscVersion)
115 {
116 resources, err := f.ClientSet.Discovery().ServerResourcesForGroupVersion(storagev1.SchemeGroupVersion.String())
117 framework.ExpectNoError(err)
118 found := false
119 for _, resource := range resources.APIResources {
120 switch resource.Name {
121 case "csistoragecapacities":
122 found = true
123 }
124 }
125 if !found {
126 framework.Failf("expected csistoragecapacities, got %#v", resources.APIResources)
127 }
128 }
129
130
131
132 ginkgo.By("creating")
133 createdCSC, err := cscClient.Create(ctx, csc, metav1.CreateOptions{})
134 framework.ExpectNoError(err)
135 _, err = cscClient.Create(ctx, csc, metav1.CreateOptions{})
136 if !apierrors.IsAlreadyExists(err) {
137 framework.Failf("expected 409, got %#v", err)
138 }
139 _, err = cscClient.Create(ctx, csc2, metav1.CreateOptions{})
140 framework.ExpectNoError(err)
141
142 ginkgo.By("watching")
143 framework.Logf("starting watch")
144 cscWatch, err := cscClient.Watch(ctx, metav1.ListOptions{LabelSelector: "test=" + f.UniqueName})
145 framework.ExpectNoError(err)
146 cscWatchNoNamespace, err := cscClientNoNamespace.Watch(ctx, metav1.ListOptions{LabelSelector: "test=" + f.UniqueName})
147 framework.ExpectNoError(err)
148
149
150 _, err = cscClient.Create(ctx, csc3, metav1.CreateOptions{})
151 framework.ExpectNoError(err)
152
153 ginkgo.By("getting")
154 gottenCSC, err := cscClient.Get(ctx, csc.Name, metav1.GetOptions{})
155 framework.ExpectNoError(err)
156 gomega.Expect(gottenCSC.UID).To(gomega.Equal(createdCSC.UID))
157
158 ginkgo.By("listing in namespace")
159 cscs, err := cscClient.List(ctx, metav1.ListOptions{LabelSelector: "test=" + f.UniqueName})
160 framework.ExpectNoError(err)
161 gomega.Expect(cscs.Items).To(gomega.HaveLen(3), "filtered list should have 3 items, got: %s", cscs)
162
163 ginkgo.By("listing across namespaces")
164 cscs, err = cscClientNoNamespace.List(ctx, metav1.ListOptions{LabelSelector: "test=" + f.UniqueName})
165 framework.ExpectNoError(err)
166 gomega.Expect(cscs.Items).To(gomega.HaveLen(3), "filtered list should have 3 items, got: %s", cscs)
167
168 ginkgo.By("patching")
169 patchedCSC, err := cscClient.Patch(ctx, createdCSC.Name, types.MergePatchType, []byte(`{"metadata":{"annotations":{"patched":"true"}}}`), metav1.PatchOptions{})
170 framework.ExpectNoError(err)
171 gomega.Expect(patchedCSC.Annotations).To(gomega.HaveKeyWithValue("patched", "true"), "patched object should have the applied annotation")
172
173 ginkgo.By("updating")
174 csrToUpdate := patchedCSC.DeepCopy()
175 csrToUpdate.Annotations["updated"] = "true"
176 updatedCSC, err := cscClient.Update(ctx, csrToUpdate, metav1.UpdateOptions{})
177 framework.ExpectNoError(err)
178 gomega.Expect(updatedCSC.Annotations).To(gomega.HaveKeyWithValue("updated", "true"), "updated object should have the applied annotation")
179
180 expectWatchResult := func(kind string, w watch.Interface) {
181 framework.Logf("waiting for watch events with expected annotations %s", kind)
182 for sawAdded, sawPatched, sawUpdated := false, false, false; !sawAdded && !sawPatched && !sawUpdated; {
183 select {
184 case evt, ok := <-w.ResultChan():
185 if !ok {
186 framework.Failf("%s: watch channel should not close", kind)
187 }
188 if evt.Type == watch.Modified {
189 watchedCSC, isCSC := evt.Object.(*storagev1.CSIStorageCapacity)
190 if !isCSC {
191 framework.Failf("%s: expected CSC, got %T", kind, evt.Object)
192 }
193 if watchedCSC.Annotations["patched"] == "true" {
194 framework.Logf("%s: saw patched annotations", kind)
195 sawPatched = true
196 } else if watchedCSC.Annotations["updated"] == "true" {
197 framework.Logf("%s: saw updated annotations", kind)
198 sawUpdated = true
199 } else {
200 framework.Logf("%s: missing expected annotations, waiting: %#v", kind, watchedCSC.Annotations)
201 }
202 } else if evt.Type == watch.Added {
203 _, isCSC := evt.Object.(*storagev1.CSIStorageCapacity)
204 if !isCSC {
205 framework.Failf("%s: expected CSC, got %T", kind, evt.Object)
206 }
207 sawAdded = true
208 }
209
210 case <-time.After(wait.ForeverTestTimeout):
211 framework.Failf("%s: timed out waiting for watch event", kind)
212 }
213 }
214 w.Stop()
215 }
216 expectWatchResult("in namespace", cscWatch)
217 expectWatchResult("across namespace", cscWatchNoNamespace)
218
219
220
221 ginkgo.By("deleting")
222 err = cscClient.Delete(ctx, createdCSC.Name, metav1.DeleteOptions{})
223 framework.ExpectNoError(err)
224 csc, err = cscClient.Get(ctx, createdCSC.Name, metav1.GetOptions{})
225 min := 2
226 max := min
227 switch {
228 case apierrors.IsNotFound(err):
229
230 case err != nil:
231
232 framework.Failf("expected 404, got %#v", err)
233 case csc.DeletionTimestamp != nil && len(csc.Finalizers) > 0:
234
235
236 max++
237 default:
238 framework.Failf("CSIStorageCapacitity should have been deleted or have DeletionTimestamp and Finalizers, but instead got: %s", csc)
239 }
240 cscs, err = cscClient.List(ctx, metav1.ListOptions{LabelSelector: "test=" + f.UniqueName})
241 framework.ExpectNoError(err)
242 actualLen := len(cscs.Items)
243 if actualLen < min || actualLen > max {
244 framework.Failf("expected <= %d and >= %d remaining CSIStorageCapacity objects, got %d: %v", max, min, actualLen, cscs.Items)
245 }
246
247 ginkgo.By("deleting a collection")
248 err = cscClient.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: "test=" + f.UniqueName})
249 framework.ExpectNoError(err)
250 cscs, err = cscClient.List(ctx, metav1.ListOptions{LabelSelector: "test=" + f.UniqueName})
251 framework.ExpectNoError(err)
252 for _, csc := range cscs.Items {
253
254
255 if csc.DeletionTimestamp == nil || len(csc.Finalizers) == 0 {
256 framework.Failf("CSIStorageCapacity should have been deleted or have DeletionTimestamp and Finalizers, but instead got: %s", &csc)
257 }
258 }
259 })
260 })
261
View as plain text