1
2
3
4
19
20 package kubelet
21
22 import (
23 "fmt"
24 "os"
25 "path/filepath"
26 "testing"
27
28 v1 "k8s.io/api/core/v1"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30
31 "k8s.io/apimachinery/pkg/types"
32 _ "k8s.io/kubernetes/pkg/apis/core/install"
33 "k8s.io/mount-utils"
34 )
35
36 func validateDirExists(dir string) error {
37 _, err := os.ReadDir(dir)
38 if err != nil {
39 return err
40 }
41 return nil
42 }
43
44 func validateDirNotExists(dir string) error {
45 _, err := os.ReadDir(dir)
46 if os.IsNotExist(err) {
47 return nil
48 }
49 if err != nil {
50 return err
51 }
52 return fmt.Errorf("dir %q still exists", dir)
53 }
54
55 func TestCleanupOrphanedPodDirs(t *testing.T) {
56 if testing.Short() {
57 t.Skip("skipping test in short mode.")
58 }
59
60 testCases := map[string]struct {
61 pods []*v1.Pod
62 prepareFunc func(kubelet *Kubelet) error
63 validateFunc func(kubelet *Kubelet) error
64 expectErr bool
65 }{
66 "nothing-to-do": {},
67 "pods-dir-not-found": {
68 prepareFunc: func(kubelet *Kubelet) error {
69 return os.Remove(kubelet.getPodsDir())
70 },
71 expectErr: true,
72 },
73 "pod-doesnot-exist-novolume": {
74 prepareFunc: func(kubelet *Kubelet) error {
75 podDir := kubelet.getPodDir("pod1uid")
76 return os.MkdirAll(filepath.Join(podDir, "not/a/volume"), 0750)
77 },
78 validateFunc: func(kubelet *Kubelet) error {
79 podDir := kubelet.getPodDir("pod1uid")
80 return validateDirNotExists(filepath.Join(podDir, "not"))
81 },
82 },
83 "pod-exists-with-volume": {
84 pods: []*v1.Pod{
85 {
86 ObjectMeta: metav1.ObjectMeta{
87 Name: "pod1",
88 UID: "pod1uid",
89 },
90 },
91 },
92 prepareFunc: func(kubelet *Kubelet) error {
93 podDir := kubelet.getPodDir("pod1uid")
94 return os.MkdirAll(filepath.Join(podDir, "volumes/plugin/name"), 0750)
95 },
96 validateFunc: func(kubelet *Kubelet) error {
97 podDir := kubelet.getPodDir("pod1uid")
98 return validateDirExists(filepath.Join(podDir, "volumes/plugin/name"))
99 },
100 },
101 "pod-doesnot-exist-with-volume": {
102 prepareFunc: func(kubelet *Kubelet) error {
103 podDir := kubelet.getPodDir("pod1uid")
104 return os.MkdirAll(filepath.Join(podDir, "volumes/plugin/name"), 0750)
105 },
106 validateFunc: func(kubelet *Kubelet) error {
107 podDir := kubelet.getPodDir("pod1uid")
108 return validateDirNotExists(podDir)
109 },
110 },
111 "pod-doesnot-exist-with-volume-subdir": {
112 prepareFunc: func(kubelet *Kubelet) error {
113 podDir := kubelet.getPodDir("pod1uid")
114 return os.MkdirAll(filepath.Join(podDir, "volumes/plugin/name/subdir"), 0750)
115 },
116 validateFunc: func(kubelet *Kubelet) error {
117 podDir := kubelet.getPodDir("pod1uid")
118 return validateDirNotExists(filepath.Join(podDir, "volumes"))
119 },
120 },
121 "pod-doesnot-exist-with-subpath": {
122 prepareFunc: func(kubelet *Kubelet) error {
123 podDir := kubelet.getPodDir("pod1uid")
124 return os.MkdirAll(filepath.Join(podDir, "volume-subpaths/volume/container/index"), 0750)
125 },
126 validateFunc: func(kubelet *Kubelet) error {
127 podDir := kubelet.getPodDir("pod1uid")
128 return validateDirNotExists(podDir)
129 },
130 },
131 "pod-doesnot-exist-with-subpath-top": {
132 prepareFunc: func(kubelet *Kubelet) error {
133 podDir := kubelet.getPodDir("pod1uid")
134 return os.MkdirAll(filepath.Join(podDir, "volume-subpaths"), 0750)
135 },
136 validateFunc: func(kubelet *Kubelet) error {
137 podDir := kubelet.getPodDir("pod1uid")
138 return validateDirNotExists(podDir)
139 },
140 },
141 "pod-doesnot-exists-with-populated-volume": {
142 prepareFunc: func(kubelet *Kubelet) error {
143 podDir := kubelet.getPodDir("pod1uid")
144 volumePath := filepath.Join(podDir, "volumes/plugin/name")
145 if err := os.MkdirAll(volumePath, 0750); err != nil {
146 return err
147 }
148 return os.WriteFile(filepath.Join(volumePath, "test.txt"), []byte("test1"), 0640)
149 },
150 validateFunc: func(kubelet *Kubelet) error {
151 podDir := kubelet.getPodDir("pod1uid")
152 return validateDirExists(filepath.Join(podDir, "volumes/plugin/name"))
153 },
154 },
155 "pod-doesnot-exists-with-populated-subpath": {
156 prepareFunc: func(kubelet *Kubelet) error {
157 podDir := kubelet.getPodDir("pod1uid")
158 subPath := filepath.Join(podDir, "volume-subpaths/volume/container/index")
159 if err := os.MkdirAll(subPath, 0750); err != nil {
160 return err
161 }
162 return os.WriteFile(filepath.Join(subPath, "test.txt"), []byte("test1"), 0640)
163 },
164 validateFunc: func(kubelet *Kubelet) error {
165 podDir := kubelet.getPodDir("pod1uid")
166 return validateDirExists(filepath.Join(podDir, "volume-subpaths/volume/container/index"))
167 },
168 },
169
170 }
171
172 for name, tc := range testCases {
173 t.Run(name, func(t *testing.T) {
174 testKubelet := newTestKubelet(t, false )
175 defer testKubelet.Cleanup()
176 kubelet := testKubelet.kubelet
177
178 if tc.prepareFunc != nil {
179 if err := tc.prepareFunc(kubelet); err != nil {
180 t.Fatalf("%s failed preparation: %v", name, err)
181 }
182 }
183
184 err := kubelet.cleanupOrphanedPodDirs(tc.pods, nil)
185 if tc.expectErr && err == nil {
186 t.Errorf("%s failed: expected error, got success", name)
187 }
188 if !tc.expectErr && err != nil {
189 t.Errorf("%s failed: got error %v", name, err)
190 }
191
192 if tc.validateFunc != nil {
193 if err := tc.validateFunc(kubelet); err != nil {
194 t.Errorf("%s failed validation: %v", name, err)
195 }
196 }
197
198 })
199 }
200 }
201
202 func TestPodVolumesExistWithMount(t *testing.T) {
203 poduid := types.UID("poduid")
204 testCases := map[string]struct {
205 prepareFunc func(kubelet *Kubelet) error
206 expected bool
207 }{
208 "noncsivolume-dir-not-exist": {
209 prepareFunc: func(kubelet *Kubelet) error {
210 return nil
211 },
212 expected: false,
213 },
214 "noncsivolume-dir-exist-noplugins": {
215 prepareFunc: func(kubelet *Kubelet) error {
216 podDir := kubelet.getPodDir(poduid)
217 return os.MkdirAll(filepath.Join(podDir, "volumes/"), 0750)
218 },
219 expected: false,
220 },
221 "noncsivolume-dir-exist-nomount": {
222 prepareFunc: func(kubelet *Kubelet) error {
223 podDir := kubelet.getPodDir(poduid)
224 return os.MkdirAll(filepath.Join(podDir, "volumes/plugin/name"), 0750)
225 },
226 expected: false,
227 },
228 "noncsivolume-dir-exist-with-mount": {
229 prepareFunc: func(kubelet *Kubelet) error {
230 podDir := kubelet.getPodDir(poduid)
231 volumePath := filepath.Join(podDir, "volumes/plugin/name")
232 if err := os.MkdirAll(volumePath, 0750); err != nil {
233 return err
234 }
235 fm := mount.NewFakeMounter(
236 []mount.MountPoint{
237 {Device: "/dev/sdb", Path: volumePath},
238 })
239 kubelet.mounter = fm
240 return nil
241 },
242 expected: true,
243 },
244 "noncsivolume-dir-exist-nomount-withcsimountpath": {
245 prepareFunc: func(kubelet *Kubelet) error {
246 podDir := kubelet.getPodDir(poduid)
247 volumePath := filepath.Join(podDir, "volumes/plugin/name/mount")
248 if err := os.MkdirAll(volumePath, 0750); err != nil {
249 return err
250 }
251 fm := mount.NewFakeMounter(
252 []mount.MountPoint{
253 {Device: "/dev/sdb", Path: volumePath},
254 })
255 kubelet.mounter = fm
256 return nil
257 },
258 expected: false,
259 },
260 "csivolume-dir-exist-nomount": {
261 prepareFunc: func(kubelet *Kubelet) error {
262 podDir := kubelet.getPodDir(poduid)
263 volumePath := filepath.Join(podDir, "volumes/kubernetes.io~csi/name")
264 return os.MkdirAll(volumePath, 0750)
265 },
266 expected: false,
267 },
268 "csivolume-dir-exist-mount-nocsimountpath": {
269 prepareFunc: func(kubelet *Kubelet) error {
270 podDir := kubelet.getPodDir(poduid)
271 volumePath := filepath.Join(podDir, "volumes/kubernetes.io~csi/name/mount")
272 return os.MkdirAll(volumePath, 0750)
273 },
274 expected: false,
275 },
276 "csivolume-dir-exist-withcsimountpath": {
277 prepareFunc: func(kubelet *Kubelet) error {
278 podDir := kubelet.getPodDir(poduid)
279 volumePath := filepath.Join(podDir, "volumes/kubernetes.io~csi/name/mount")
280 if err := os.MkdirAll(volumePath, 0750); err != nil {
281 return err
282 }
283 fm := mount.NewFakeMounter(
284 []mount.MountPoint{
285 {Device: "/dev/sdb", Path: volumePath},
286 })
287 kubelet.mounter = fm
288 return nil
289 },
290 expected: true,
291 },
292 }
293
294 for name, tc := range testCases {
295 t.Run(name, func(t *testing.T) {
296 testKubelet := newTestKubelet(t, false )
297 defer testKubelet.Cleanup()
298 kubelet := testKubelet.kubelet
299
300 if tc.prepareFunc != nil {
301 if err := tc.prepareFunc(kubelet); err != nil {
302 t.Fatalf("%s failed preparation: %v", name, err)
303 }
304 }
305
306 exist := kubelet.podVolumesExist(poduid)
307 if tc.expected != exist {
308 t.Errorf("%s failed: expected %t, got %t", name, tc.expected, exist)
309 }
310 })
311 }
312 }
313
View as plain text