1
16
17 package testsuites
18
19 import (
20 "context"
21
22 "github.com/onsi/ginkgo/v2"
23 v1 "k8s.io/api/core/v1"
24 "k8s.io/apimachinery/pkg/util/errors"
25 clientset "k8s.io/client-go/kubernetes"
26 "k8s.io/kubernetes/test/e2e/feature"
27 "k8s.io/kubernetes/test/e2e/framework"
28 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
29 e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
30 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
31 storageframework "k8s.io/kubernetes/test/e2e/storage/framework"
32 storageutils "k8s.io/kubernetes/test/e2e/storage/utils"
33 admissionapi "k8s.io/pod-security-admission/api"
34 )
35
36 type disruptiveTestSuite struct {
37 tsInfo storageframework.TestSuiteInfo
38 }
39
40
41
42 func InitCustomDisruptiveTestSuite(patterns []storageframework.TestPattern) storageframework.TestSuite {
43 return &disruptiveTestSuite{
44 tsInfo: storageframework.TestSuiteInfo{
45 Name: "disruptive",
46 TestTags: []interface{}{framework.WithDisruptive(), framework.WithLabel("LinuxOnly")},
47 TestPatterns: patterns,
48 },
49 }
50 }
51
52
53
54 func InitDisruptiveTestSuite() storageframework.TestSuite {
55 testPatterns := []storageframework.TestPattern{
56
57 storageframework.DefaultFsInlineVolume,
58 storageframework.FsVolModePreprovisionedPV,
59 storageframework.FsVolModeDynamicPV,
60 storageframework.BlockVolModePreprovisionedPV,
61 storageframework.BlockVolModeDynamicPV,
62 }
63 return InitCustomDisruptiveTestSuite(testPatterns)
64 }
65
66 func (s *disruptiveTestSuite) GetTestSuiteInfo() storageframework.TestSuiteInfo {
67 return s.tsInfo
68 }
69
70 func (s *disruptiveTestSuite) SkipUnsupportedTests(driver storageframework.TestDriver, pattern storageframework.TestPattern) {
71 skipVolTypePatterns(pattern, driver, storageframework.NewVolTypeMap(storageframework.PreprovisionedPV))
72 if pattern.VolMode == v1.PersistentVolumeBlock && !driver.GetDriverInfo().Capabilities[storageframework.CapBlock] {
73 e2eskipper.Skipf("Driver %s doesn't support %v -- skipping", driver.GetDriverInfo().Name, pattern.VolMode)
74 }
75 e2eskipper.SkipUnlessSSHKeyPresent()
76 }
77
78 func (s *disruptiveTestSuite) DefineTests(driver storageframework.TestDriver, pattern storageframework.TestPattern) {
79 type local struct {
80 config *storageframework.PerTestConfig
81
82 cs clientset.Interface
83 ns *v1.Namespace
84
85
86 resource *storageframework.VolumeResource
87 pod *v1.Pod
88 }
89 var l local
90
91
92
93 f := framework.NewFrameworkWithCustomTimeouts("disruptive", storageframework.GetDriverTimeouts(driver))
94 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
95
96 init := func(ctx context.Context, accessModes []v1.PersistentVolumeAccessMode) {
97 l = local{}
98 l.ns = f.Namespace
99 l.cs = f.ClientSet
100
101
102 l.config = driver.PrepareTest(ctx, f)
103
104 testVolumeSizeRange := s.GetTestSuiteInfo().SupportedSizeRange
105 if accessModes == nil {
106 l.resource = storageframework.CreateVolumeResource(
107 ctx,
108 driver,
109 l.config,
110 pattern,
111 testVolumeSizeRange)
112 } else {
113 l.resource = storageframework.CreateVolumeResourceWithAccessModes(
114 ctx,
115 driver,
116 l.config,
117 pattern,
118 testVolumeSizeRange,
119 accessModes)
120 }
121 }
122
123 cleanup := func(ctx context.Context) {
124 var errs []error
125 if l.pod != nil {
126 ginkgo.By("Deleting pod")
127 err := e2epod.DeletePodWithWait(ctx, f.ClientSet, l.pod)
128 errs = append(errs, err)
129 l.pod = nil
130 }
131
132 if l.resource != nil {
133 err := l.resource.CleanupResource(ctx)
134 errs = append(errs, err)
135 l.resource = nil
136 }
137
138 framework.ExpectNoError(errors.NewAggregate(errs), "while cleaning up resource")
139 }
140
141 type singlePodTestBody func(ctx context.Context, c clientset.Interface, f *framework.Framework, clientPod *v1.Pod, mountPath string)
142 type singlePodTest struct {
143 testItStmt string
144 runTestFile singlePodTestBody
145 runTestBlock singlePodTestBody
146 }
147 singlePodTests := []singlePodTest{
148 {
149 testItStmt: "Should test that pv written before kubelet restart is readable after restart.",
150 runTestFile: storageutils.TestKubeletRestartsAndRestoresMount,
151 runTestBlock: storageutils.TestKubeletRestartsAndRestoresMap,
152 },
153 {
154 testItStmt: "Should test that pv used in a pod that is deleted while the kubelet is down cleans up when the kubelet returns.",
155
156 runTestBlock: storageutils.TestVolumeUnmapsFromDeletedPod,
157 },
158 {
159 testItStmt: "Should test that pv used in a pod that is force deleted while the kubelet is down cleans up when the kubelet returns.",
160
161 runTestBlock: storageutils.TestVolumeUnmapsFromForceDeletedPod,
162 },
163 }
164
165 for _, test := range singlePodTests {
166 func(t singlePodTest) {
167 if (pattern.VolMode == v1.PersistentVolumeBlock && t.runTestBlock != nil) ||
168 (pattern.VolMode == v1.PersistentVolumeFilesystem && t.runTestFile != nil) {
169 ginkgo.It(t.testItStmt, func(ctx context.Context) {
170 init(ctx, nil)
171 ginkgo.DeferCleanup(cleanup)
172
173 var err error
174 var pvcs []*v1.PersistentVolumeClaim
175 var inlineSources []*v1.VolumeSource
176 if pattern.VolType == storageframework.InlineVolume {
177 inlineSources = append(inlineSources, l.resource.VolSource)
178 } else {
179 pvcs = append(pvcs, l.resource.Pvc)
180 }
181 ginkgo.By("Creating a pod with pvc")
182 podConfig := e2epod.Config{
183 NS: l.ns.Name,
184 PVCs: pvcs,
185 InlineVolumeSources: inlineSources,
186 SeLinuxLabel: e2epv.SELinuxLabel,
187 NodeSelection: l.config.ClientNodeSelection,
188 ImageID: e2epod.GetDefaultTestImageID(),
189 }
190 l.pod, err = e2epod.CreateSecPodWithNodeSelection(ctx, l.cs, &podConfig, f.Timeouts.PodStart)
191 framework.ExpectNoError(err, "While creating pods for kubelet restart test")
192
193 if pattern.VolMode == v1.PersistentVolumeBlock && t.runTestBlock != nil {
194 t.runTestBlock(ctx, l.cs, l.config.Framework, l.pod, e2epod.VolumeMountPath1)
195 }
196 if pattern.VolMode == v1.PersistentVolumeFilesystem && t.runTestFile != nil {
197 t.runTestFile(ctx, l.cs, l.config.Framework, l.pod, e2epod.VolumeMountPath1)
198 }
199 })
200 }
201 }(test)
202 }
203 type multiplePodTestBody func(ctx context.Context, c clientset.Interface, f *framework.Framework, pod1, pod2 *v1.Pod)
204 type multiplePodTest struct {
205 testItStmt string
206 changeSELinuxContexts bool
207 runTestFile multiplePodTestBody
208 }
209 multiplePodTests := []multiplePodTest{
210 {
211 testItStmt: "Should test that pv used in a pod that is deleted while the kubelet is down is usable by a new pod when kubelet returns",
212 runTestFile: func(ctx context.Context, c clientset.Interface, f *framework.Framework, pod1, pod2 *v1.Pod) {
213 storageutils.TestVolumeUnmountsFromDeletedPodWithForceOption(ctx, c, f, pod1, false, false, pod2, e2epod.VolumeMountPath1)
214 },
215 },
216 {
217 testItStmt: "Should test that pv used in a pod that is force deleted while the kubelet is down is usable by a new pod when kubelet returns",
218 runTestFile: func(ctx context.Context, c clientset.Interface, f *framework.Framework, pod1, pod2 *v1.Pod) {
219 storageutils.TestVolumeUnmountsFromDeletedPodWithForceOption(ctx, c, f, pod1, true, false, pod2, e2epod.VolumeMountPath1)
220 },
221 },
222 {
223 testItStmt: "Should test that pv used in a pod that is deleted while the kubelet is down is usable by a new pod with a different SELinux context when kubelet returns",
224 changeSELinuxContexts: true,
225 runTestFile: func(ctx context.Context, c clientset.Interface, f *framework.Framework, pod1, pod2 *v1.Pod) {
226 storageutils.TestVolumeUnmountsFromDeletedPodWithForceOption(ctx, c, f, pod1, false, false, pod2, e2epod.VolumeMountPath1)
227 },
228 },
229 {
230 testItStmt: "Should test that pv used in a pod that is force deleted while the kubelet is down is usable by a new pod with a different SELinux context when kubelet returns",
231 changeSELinuxContexts: true,
232 runTestFile: func(ctx context.Context, c clientset.Interface, f *framework.Framework, pod1, pod2 *v1.Pod) {
233 storageutils.TestVolumeUnmountsFromDeletedPodWithForceOption(ctx, c, f, pod1, true, false, pod2, e2epod.VolumeMountPath1)
234 },
235 },
236 }
237
238 for _, test := range multiplePodTests {
239 func(t multiplePodTest) {
240 if pattern.VolMode == v1.PersistentVolumeFilesystem && t.runTestFile != nil {
241 f.It(t.testItStmt, feature.SELinux, func(ctx context.Context) {
242 init(ctx, []v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod})
243 ginkgo.DeferCleanup(cleanup)
244
245 var err error
246 var pvcs []*v1.PersistentVolumeClaim
247 var inlineSources []*v1.VolumeSource
248 if pattern.VolType == storageframework.InlineVolume {
249 inlineSources = append(inlineSources, l.resource.VolSource)
250 } else {
251 pvcs = append(pvcs, l.resource.Pvc)
252 }
253 ginkgo.By("Creating a pod with pvc")
254 podConfig := e2epod.Config{
255 NS: l.ns.Name,
256 PVCs: pvcs,
257 InlineVolumeSources: inlineSources,
258 SeLinuxLabel: e2epv.SELinuxLabel,
259 NodeSelection: l.config.ClientNodeSelection,
260 ImageID: e2epod.GetDefaultTestImageID(),
261 }
262 l.pod, err = e2epod.CreateSecPodWithNodeSelection(ctx, l.cs, &podConfig, f.Timeouts.PodStart)
263 framework.ExpectNoError(err, "While creating pods for kubelet restart test")
264 if t.changeSELinuxContexts {
265
266 podConfig.SeLinuxLabel = &v1.SELinuxOptions{Level: "s0:c98,c99"}
267 }
268 pod2, err := e2epod.MakeSecPod(&podConfig)
269
270 pod2.Spec.NodeName = l.pod.Spec.NodeName
271 framework.ExpectNoError(err, "While creating second pod for kubelet restart test")
272 if pattern.VolMode == v1.PersistentVolumeFilesystem && t.runTestFile != nil {
273 t.runTestFile(ctx, l.cs, l.config.Framework, l.pod, pod2)
274 }
275 })
276 }
277 }(test)
278 }
279 }
280
View as plain text