1
16
17 package testsuites
18
19 import (
20 "context"
21 "fmt"
22 "strconv"
23
24 "github.com/onsi/ginkgo/v2"
25 v1 "k8s.io/api/core/v1"
26 errors "k8s.io/apimachinery/pkg/util/errors"
27 "k8s.io/kubernetes/test/e2e/framework"
28 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
29 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
30 e2evolume "k8s.io/kubernetes/test/e2e/framework/volume"
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 utilpointer "k8s.io/utils/pointer"
35 )
36
37 const (
38 rootDir = "/mnt/volume1"
39 rootDirFile = "file1"
40 rootDirFilePath = rootDir + "/" + rootDirFile
41 subdir = "/mnt/volume1/subdir"
42 subDirFile = "file2"
43 subDirFilePath = subdir + "/" + subDirFile
44 )
45
46 type fsGroupChangePolicyTestSuite struct {
47 tsInfo storageframework.TestSuiteInfo
48 }
49
50 var _ storageframework.TestSuite = &fsGroupChangePolicyTestSuite{}
51
52
53 func InitCustomFsGroupChangePolicyTestSuite(patterns []storageframework.TestPattern) storageframework.TestSuite {
54 return &fsGroupChangePolicyTestSuite{
55 tsInfo: storageframework.TestSuiteInfo{
56 Name: "fsgroupchangepolicy",
57 TestPatterns: patterns,
58 SupportedSizeRange: e2evolume.SizeRange{
59 Min: "1Mi",
60 },
61 },
62 }
63 }
64
65
66 func InitFsGroupChangePolicyTestSuite() storageframework.TestSuite {
67 patterns := []storageframework.TestPattern{
68 storageframework.DefaultFsDynamicPV,
69 }
70 return InitCustomFsGroupChangePolicyTestSuite(patterns)
71 }
72
73 func (s *fsGroupChangePolicyTestSuite) GetTestSuiteInfo() storageframework.TestSuiteInfo {
74 return s.tsInfo
75 }
76
77 func (s *fsGroupChangePolicyTestSuite) SkipUnsupportedTests(driver storageframework.TestDriver, pattern storageframework.TestPattern) {
78 skipVolTypePatterns(pattern, driver, storageframework.NewVolTypeMap(storageframework.CSIInlineVolume, storageframework.GenericEphemeralVolume))
79 dInfo := driver.GetDriverInfo()
80 if !dInfo.Capabilities[storageframework.CapFsGroup] {
81 e2eskipper.Skipf("Driver %q does not support FsGroup - skipping", dInfo.Name)
82 }
83
84 if pattern.VolMode == v1.PersistentVolumeBlock {
85 e2eskipper.Skipf("Test does not support non-filesystem volume mode - skipping")
86 }
87
88 if pattern.VolType != storageframework.DynamicPV {
89 e2eskipper.Skipf("Suite %q does not support %v", s.tsInfo.Name, pattern.VolType)
90 }
91
92 _, ok := driver.(storageframework.DynamicPVTestDriver)
93 if !ok {
94 e2eskipper.Skipf("Driver %s doesn't support %v -- skipping", dInfo.Name, pattern.VolType)
95 }
96 }
97
98 func (s *fsGroupChangePolicyTestSuite) DefineTests(driver storageframework.TestDriver, pattern storageframework.TestPattern) {
99 type local struct {
100 config *storageframework.PerTestConfig
101 driver storageframework.TestDriver
102 resource *storageframework.VolumeResource
103 }
104 var l local
105
106
107
108 f := framework.NewFrameworkWithCustomTimeouts("fsgroupchangepolicy", storageframework.GetDriverTimeouts(driver))
109 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
110
111 init := func(ctx context.Context) {
112 e2eskipper.SkipIfNodeOSDistroIs("windows")
113 l = local{}
114 l.driver = driver
115 l.config = driver.PrepareTest(ctx, f)
116 testVolumeSizeRange := s.GetTestSuiteInfo().SupportedSizeRange
117 l.resource = storageframework.CreateVolumeResource(ctx, l.driver, l.config, pattern, testVolumeSizeRange)
118 }
119
120 cleanup := func(ctx context.Context) {
121 var errs []error
122 if l.resource != nil {
123 if err := l.resource.CleanupResource(ctx); err != nil {
124 errs = append(errs, err)
125 }
126 l.resource = nil
127 }
128
129 framework.ExpectNoError(errors.NewAggregate(errs), "while cleanup resource")
130 }
131
132 tests := []struct {
133 name string
134 podfsGroupChangePolicy string
135 initialPodFsGroup int
136 changedRootDirFileOwnership int
137 changedSubDirFileOwnership int
138 secondPodFsGroup int
139 finalExpectedRootDirFileOwnership int
140 finalExpectedSubDirFileOwnership int
141
142
143
144
145 supportsVolumeMountGroup bool
146 }{
147
148 {
149 name: "pod created with an initial fsgroup, new pod fsgroup applied to volume contents",
150 podfsGroupChangePolicy: "Always",
151 initialPodFsGroup: 1000,
152 secondPodFsGroup: 2000,
153 finalExpectedRootDirFileOwnership: 2000,
154 finalExpectedSubDirFileOwnership: 2000,
155 supportsVolumeMountGroup: true,
156 },
157 {
158 name: "pod created with an initial fsgroup, volume contents ownership changed via chgrp in first pod, new pod with same fsgroup applied to the volume contents",
159 podfsGroupChangePolicy: "Always",
160 initialPodFsGroup: 1000,
161 changedRootDirFileOwnership: 2000,
162 changedSubDirFileOwnership: 3000,
163 secondPodFsGroup: 1000,
164 finalExpectedRootDirFileOwnership: 1000,
165 finalExpectedSubDirFileOwnership: 1000,
166 },
167 {
168 name: "pod created with an initial fsgroup, volume contents ownership changed via chgrp in first pod, new pod with different fsgroup applied to the volume contents",
169 podfsGroupChangePolicy: "Always",
170 initialPodFsGroup: 1000,
171 changedRootDirFileOwnership: 2000,
172 changedSubDirFileOwnership: 3000,
173 secondPodFsGroup: 4000,
174 finalExpectedRootDirFileOwnership: 4000,
175 finalExpectedSubDirFileOwnership: 4000,
176 },
177
178 {
179 name: "pod created with an initial fsgroup, new pod fsgroup applied to volume contents",
180 podfsGroupChangePolicy: "OnRootMismatch",
181 initialPodFsGroup: 1000,
182 secondPodFsGroup: 2000,
183 finalExpectedRootDirFileOwnership: 2000,
184 finalExpectedSubDirFileOwnership: 2000,
185 },
186 {
187 name: "pod created with an initial fsgroup, volume contents ownership changed via chgrp in first pod, new pod with same fsgroup skips ownership changes to the volume contents",
188 podfsGroupChangePolicy: "OnRootMismatch",
189 initialPodFsGroup: 1000,
190 changedRootDirFileOwnership: 2000,
191 changedSubDirFileOwnership: 3000,
192 secondPodFsGroup: 1000,
193 finalExpectedRootDirFileOwnership: 2000,
194 finalExpectedSubDirFileOwnership: 3000,
195 },
196 {
197 name: "pod created with an initial fsgroup, volume contents ownership changed via chgrp in first pod, new pod with different fsgroup applied to the volume contents",
198 podfsGroupChangePolicy: "OnRootMismatch",
199 initialPodFsGroup: 1000,
200 changedRootDirFileOwnership: 2000,
201 changedSubDirFileOwnership: 3000,
202 secondPodFsGroup: 4000,
203 finalExpectedRootDirFileOwnership: 4000,
204 finalExpectedSubDirFileOwnership: 4000,
205 },
206 }
207
208 for _, t := range tests {
209 test := t
210 testCaseName := fmt.Sprintf("(%s)[LinuxOnly], %s", test.podfsGroupChangePolicy, test.name)
211 ginkgo.It(testCaseName, func(ctx context.Context) {
212 dInfo := driver.GetDriverInfo()
213 policy := v1.PodFSGroupChangePolicy(test.podfsGroupChangePolicy)
214
215 if dInfo.Capabilities[storageframework.CapVolumeMountGroup] &&
216 !test.supportsVolumeMountGroup {
217 e2eskipper.Skipf("Driver %q supports VolumeMountGroup, which is incompatible with this test - skipping", dInfo.Name)
218 }
219
220 init(ctx)
221 ginkgo.DeferCleanup(cleanup)
222 podConfig := e2epod.Config{
223 NS: f.Namespace.Name,
224 NodeSelection: l.config.ClientNodeSelection,
225 PVCs: []*v1.PersistentVolumeClaim{l.resource.Pvc},
226 FsGroup: utilpointer.Int64Ptr(int64(test.initialPodFsGroup)),
227 PodFSGroupChangePolicy: &policy,
228 }
229
230 pod := createPodAndVerifyContentGid(ctx, l.config.Framework, &podConfig, true , "" , "" )
231
232
233 if test.changedRootDirFileOwnership != 0 {
234 ginkgo.By(fmt.Sprintf("Changing the root directory file ownership to %s", strconv.Itoa(test.changedRootDirFileOwnership)))
235 storageutils.ChangeFilePathGidInPod(f, rootDirFilePath, strconv.Itoa(test.changedRootDirFileOwnership), pod)
236 }
237
238 if test.changedSubDirFileOwnership != 0 {
239 ginkgo.By(fmt.Sprintf("Changing the sub-directory file ownership to %s", strconv.Itoa(test.changedSubDirFileOwnership)))
240 storageutils.ChangeFilePathGidInPod(f, subDirFilePath, strconv.Itoa(test.changedSubDirFileOwnership), pod)
241 }
242
243 ginkgo.By(fmt.Sprintf("Deleting Pod %s/%s", pod.Namespace, pod.Name))
244 framework.ExpectNoError(e2epod.DeletePodWithWait(ctx, f.ClientSet, pod))
245
246
247 podConfig.FsGroup = utilpointer.Int64Ptr(int64(test.secondPodFsGroup))
248 pod = createPodAndVerifyContentGid(ctx, l.config.Framework, &podConfig, false , strconv.Itoa(test.finalExpectedRootDirFileOwnership), strconv.Itoa(test.finalExpectedSubDirFileOwnership))
249 ginkgo.By(fmt.Sprintf("Deleting Pod %s/%s", pod.Namespace, pod.Name))
250 framework.ExpectNoError(e2epod.DeletePodWithWait(ctx, f.ClientSet, pod))
251 })
252 }
253 }
254
255 func createPodAndVerifyContentGid(ctx context.Context, f *framework.Framework, podConfig *e2epod.Config, createInitialFiles bool, expectedRootDirFileOwnership, expectedSubDirFileOwnership string) *v1.Pod {
256 podFsGroup := strconv.FormatInt(*podConfig.FsGroup, 10)
257 ginkgo.By(fmt.Sprintf("Creating Pod in namespace %s with fsgroup %s", podConfig.NS, podFsGroup))
258 pod, err := e2epod.CreateSecPodWithNodeSelection(ctx, f.ClientSet, podConfig, f.Timeouts.PodStart)
259 framework.ExpectNoError(err)
260 framework.Logf("Pod %s/%s started successfully", pod.Namespace, pod.Name)
261
262 if createInitialFiles {
263 ginkgo.By(fmt.Sprintf("Creating a sub-directory and file, and verifying their ownership is %s", podFsGroup))
264 cmd := fmt.Sprintf("touch %s", rootDirFilePath)
265 var err error
266 _, _, err = e2evolume.PodExec(f, pod, cmd)
267 framework.ExpectNoError(err)
268 storageutils.VerifyFilePathGidInPod(f, rootDirFilePath, podFsGroup, pod)
269
270 cmd = fmt.Sprintf("mkdir %s", subdir)
271 _, _, err = e2evolume.PodExec(f, pod, cmd)
272 framework.ExpectNoError(err)
273 cmd = fmt.Sprintf("touch %s", subDirFilePath)
274 _, _, err = e2evolume.PodExec(f, pod, cmd)
275 framework.ExpectNoError(err)
276 storageutils.VerifyFilePathGidInPod(f, subDirFilePath, podFsGroup, pod)
277 return pod
278 }
279
280
281 ginkgo.By(fmt.Sprintf("Verifying the ownership of root directory file is %s", expectedRootDirFileOwnership))
282 storageutils.VerifyFilePathGidInPod(f, rootDirFilePath, expectedRootDirFileOwnership, pod)
283 ginkgo.By(fmt.Sprintf("Verifying the ownership of sub directory file is %s", expectedSubDirFileOwnership))
284 storageutils.VerifyFilePathGidInPod(f, subDirFilePath, expectedSubDirFileOwnership, pod)
285 return pod
286 }
287
View as plain text