Skip to content

Commit b685f52

Browse files
authored
Merge pull request kubernetes#120471 from ii/create-storageclass-lifecycle-test
Write e2e test for StorageClass Endpoints + 7 Endpoints
2 parents 6c39a37 + e7ee3ae commit b685f52

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed

test/e2e/storage/storageclass.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
Copyright 2023 The Kubernetes 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 storage
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"time"
23+
24+
storagev1 "k8s.io/api/storage/v1"
25+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/apimachinery/pkg/labels"
27+
types "k8s.io/apimachinery/pkg/types"
28+
"k8s.io/client-go/util/retry"
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("StorageClasses", func() {
38+
39+
f := framework.NewDefaultFramework("csi-storageclass")
40+
f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
41+
42+
ginkgo.Describe("CSI Conformance", func() {
43+
ginkgo.It("should run through the lifecycle of a StorageClass", func(ctx context.Context) {
44+
45+
scClient := f.ClientSet.StorageV1().StorageClasses()
46+
var initialSC, replacementSC *storagev1.StorageClass
47+
48+
initialSC = &storagev1.StorageClass{
49+
TypeMeta: metav1.TypeMeta{
50+
Kind: "StorageClass",
51+
},
52+
ObjectMeta: metav1.ObjectMeta{
53+
GenerateName: "e2e-",
54+
},
55+
Provisioner: "e2e-fake-provisioner",
56+
}
57+
58+
ginkgo.By("Creating a StorageClass")
59+
createdStorageClass, err := scClient.Create(ctx, initialSC, metav1.CreateOptions{})
60+
framework.ExpectNoError(err, "failed to create the requested StorageClass")
61+
62+
ginkgo.By(fmt.Sprintf("Get StorageClass %q", createdStorageClass.Name))
63+
retrievedStorageClass, err := scClient.Get(ctx, createdStorageClass.Name, metav1.GetOptions{})
64+
framework.ExpectNoError(err, "failed to get StorageClass %q", createdStorageClass.Name)
65+
66+
ginkgo.By(fmt.Sprintf("Patching the StorageClass %q", retrievedStorageClass.Name))
67+
payload := "{\"metadata\":{\"labels\":{\"" + retrievedStorageClass.Name + "\":\"patched\"}}}"
68+
patchedStorageClass, err := scClient.Patch(ctx, retrievedStorageClass.Name, types.StrategicMergePatchType, []byte(payload), metav1.PatchOptions{})
69+
framework.ExpectNoError(err, "failed to patch StorageClass %q", retrievedStorageClass.Name)
70+
gomega.Expect(patchedStorageClass.Labels).To(gomega.HaveKeyWithValue(patchedStorageClass.Name, "patched"), "checking that patched label has been applied")
71+
72+
ginkgo.By(fmt.Sprintf("Delete StorageClass %q", patchedStorageClass.Name))
73+
err = scClient.Delete(ctx, patchedStorageClass.Name, metav1.DeleteOptions{})
74+
framework.ExpectNoError(err, "failed to delete StorageClass %q", patchedStorageClass.Name)
75+
76+
ginkgo.By(fmt.Sprintf("Confirm deletion of StorageClass %q", patchedStorageClass.Name))
77+
78+
scSelector := labels.Set{patchedStorageClass.Name: "patched"}.AsSelector().String()
79+
type state struct {
80+
StorageClasses []storagev1.StorageClass
81+
}
82+
83+
err = framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*state, error) {
84+
scList, err := scClient.List(ctx, metav1.ListOptions{LabelSelector: scSelector})
85+
if err != nil {
86+
return nil, fmt.Errorf("failed to list StorageClass: %w", err)
87+
}
88+
return &state{
89+
StorageClasses: scList.Items,
90+
}, nil
91+
})).WithTimeout(30 * time.Second).Should(framework.MakeMatcher(func(s *state) (func() string, error) {
92+
if len(s.StorageClasses) == 0 {
93+
return nil, nil
94+
}
95+
return func() string {
96+
return fmt.Sprintf("expected StorageClass to be deleted, found %q", s.StorageClasses[0].Name)
97+
}, nil
98+
}))
99+
framework.ExpectNoError(err, "timeout while waiting to confirm StorageClass %q deletion", patchedStorageClass.Name)
100+
101+
ginkgo.By("Create a replacement StorageClass")
102+
103+
replacementSC = &storagev1.StorageClass{
104+
TypeMeta: metav1.TypeMeta{
105+
Kind: "StorageClass",
106+
},
107+
ObjectMeta: metav1.ObjectMeta{
108+
GenerateName: "e2e-v2-",
109+
},
110+
Provisioner: "e2e-fake-provisioner",
111+
}
112+
113+
replacementStorageClass, err := scClient.Create(ctx, replacementSC, metav1.CreateOptions{})
114+
framework.ExpectNoError(err, "failed to create replacement StorageClass")
115+
116+
ginkgo.By(fmt.Sprintf("Updating StorageClass %q", replacementStorageClass.Name))
117+
var updatedStorageClass *storagev1.StorageClass
118+
119+
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
120+
sc, err := scClient.Get(ctx, replacementStorageClass.Name, metav1.GetOptions{})
121+
framework.ExpectNoError(err, "unable to get Storage %q", replacementStorageClass.Name)
122+
sc.Labels = map[string]string{replacementStorageClass.Name: "updated"}
123+
updatedStorageClass, err = scClient.Update(ctx, sc, metav1.UpdateOptions{})
124+
125+
return err
126+
})
127+
framework.ExpectNoError(err, "failed to update StorageClass %q", replacementStorageClass.Name)
128+
gomega.Expect(updatedStorageClass.Labels).To(gomega.HaveKeyWithValue(replacementStorageClass.Name, "updated"), "checking that updated label has been applied")
129+
130+
scSelector = labels.Set{replacementStorageClass.Name: "updated"}.AsSelector().String()
131+
ginkgo.By(fmt.Sprintf("Listing all StorageClass with the labelSelector: %q", scSelector))
132+
scList, err := scClient.List(ctx, metav1.ListOptions{LabelSelector: scSelector})
133+
framework.ExpectNoError(err, "failed to list StorageClasses with the labelSelector: %q", scSelector)
134+
gomega.Expect(scList.Items).To(gomega.HaveLen(1))
135+
136+
ginkgo.By(fmt.Sprintf("Deleting StorageClass %q via DeleteCollection", updatedStorageClass.Name))
137+
err = scClient.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: scSelector})
138+
framework.ExpectNoError(err, "failed to delete StorageClass %q", updatedStorageClass.Name)
139+
140+
ginkgo.By(fmt.Sprintf("Confirm deletion of StorageClass %q", updatedStorageClass.Name))
141+
142+
err = framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*state, error) {
143+
scList, err := scClient.List(ctx, metav1.ListOptions{LabelSelector: scSelector})
144+
if err != nil {
145+
return nil, fmt.Errorf("failed to list StorageClass: %w", err)
146+
}
147+
return &state{
148+
StorageClasses: scList.Items,
149+
}, nil
150+
})).WithTimeout(30 * time.Second).Should(framework.MakeMatcher(func(s *state) (func() string, error) {
151+
if len(s.StorageClasses) == 0 {
152+
return nil, nil
153+
}
154+
return func() string {
155+
return fmt.Sprintf("expected StorageClass to be deleted, found %q", s.StorageClasses[0].Name)
156+
}, nil
157+
}))
158+
framework.ExpectNoError(err, "timeout while waiting to confirm StorageClass %q deletion", updatedStorageClass.Name)
159+
})
160+
})
161+
})

0 commit comments

Comments
 (0)