1
2
3
4
19
20 package iscsi
21
22 import (
23 "io/ioutil"
24 "os"
25 "path/filepath"
26 "reflect"
27 "testing"
28
29 testingexec "k8s.io/utils/exec/testing"
30
31 "k8s.io/kubernetes/pkg/kubelet/config"
32 "k8s.io/kubernetes/pkg/volume"
33 volumetest "k8s.io/kubernetes/pkg/volume/testing"
34 )
35
36 const (
37 TestIface = "192.168.1.10:pv0001"
38 )
39
40 func TestExtractDeviceAndPrefix(t *testing.T) {
41 devicePath := "127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00"
42 mountPrefix := "/var/lib/kubelet/plugins/kubernetes.io/iscsi/iface-default/" + devicePath
43 lun := "-lun-0"
44 device, prefix, err := extractDeviceAndPrefix(mountPrefix + lun)
45 if err != nil || device != (devicePath+lun) || prefix != mountPrefix {
46 t.Errorf("extractDeviceAndPrefix: expected %s and %s, got %v %s and %s", devicePath+lun, mountPrefix, err, device, prefix)
47 }
48 }
49
50 func TestExtractIface(t *testing.T) {
51 ifaceName := "default"
52 devicePath := "127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"
53 iface, found := extractIface("/var/lib/kubelet/plugins/kubernetes.io/iscsi/iface-" + ifaceName + "/" + devicePath)
54 if !found || iface != ifaceName {
55 t.Errorf("extractIface: expected %s and %t, got %s and %t", ifaceName, true, iface, found)
56 }
57 iface, found = extractIface("/var/lib/kubelet/plugins/kubernetes.io/iscsi/" + devicePath)
58 if found || iface != "" {
59 t.Errorf("extractIface: expected %s and %t, got %s and %t", "", false, iface, found)
60 }
61 }
62
63 func TestExtractPortalAndIqn(t *testing.T) {
64 devicePath := "127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"
65 portal, iqn, err := extractPortalAndIqn(devicePath)
66 if err != nil || portal != "127.0.0.1:3260" || iqn != "iqn.2014-12.com.example:test.tgt00" {
67 t.Errorf("extractPortalAndIqn: got %v %s %s", err, portal, iqn)
68 }
69 devicePath = "127.0.0.1:3260-eui.02004567A425678D-lun-0"
70 portal, iqn, err = extractPortalAndIqn(devicePath)
71 if err != nil || portal != "127.0.0.1:3260" || iqn != "eui.02004567A425678D" {
72 t.Errorf("extractPortalAndIqn: got %v %s %s", err, portal, iqn)
73 }
74 devicePath = "[2001:db8:0:f101::1]:3260-iqn.2014-12.com.example:test.tgt00-lun-0"
75 portal, iqn, err = extractPortalAndIqn(devicePath)
76 if err != nil || portal != "[2001:db8:0:f101::1]:3260" || iqn != "iqn.2014-12.com.example:test.tgt00" {
77 t.Errorf("extractPortalAndIqn: got %v %s %s", err, portal, iqn)
78 }
79 }
80
81 func TestRemoveDuplicate(t *testing.T) {
82 dupPortals := []string{"127.0.0.1:3260", "127.0.0.1:3260", "127.0.0.100:3260"}
83 portals := removeDuplicate(dupPortals)
84 want := []string{"127.0.0.1:3260", "127.0.0.100:3260"}
85 if reflect.DeepEqual(portals, want) == false {
86 t.Errorf("removeDuplicate: want: %s, got: %s", want, portals)
87 }
88 }
89
90 func fakeOsStat(devicePath string) (fi os.FileInfo, err error) {
91 var cmd os.FileInfo
92 return cmd, nil
93 }
94
95 func fakeFilepathGlob(devicePath string) (globs []string, err error) {
96 return []string{devicePath}, nil
97 }
98
99 func fakeFilepathGlob2(devicePath string) (globs []string, err error) {
100 return []string{
101 "/dev/disk/by-path/pci-0000:00:00.0-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0",
102 }, nil
103 }
104
105 func TestExtractTransportname(t *testing.T) {
106 fakeIscsiadmOutput := []string{
107 "# BEGIN RECORD 2.0-873\n" +
108 "iface.iscsi_ifacename = default\n" +
109 "iface.transport_name = tcp\n" +
110 "iface.initiatorname = <empty>\n" +
111 "# END RECORD",
112 "# BEGIN RECORD 2.0-873\n" +
113 "iface.iscsi_ifacename = default\n" +
114 "iface.transport_name = cxgb4i\n" +
115 "iface.initiatorname = <empty>\n" +
116 "# END RECORD",
117 "# BEGIN RECORD 2.0-873\n" +
118 "iface.iscsi_ifacename = default\n" +
119 "iface.transport_name = <empty>\n" +
120 "iface.initiatorname = <empty>\n" +
121 "# END RECORD",
122 "# BEGIN RECORD 2.0-873\n" +
123 "iface.iscsi_ifacename = default\n" +
124 "iface.initiatorname = <empty>\n" +
125 "# END RECORD"}
126 transportName := extractTransportname(fakeIscsiadmOutput[0])
127 if transportName != "tcp" {
128 t.Errorf("extractTransportname: Could not extract correct iface.transport_name 'tcp', got %s", transportName)
129 }
130 transportName = extractTransportname(fakeIscsiadmOutput[1])
131 if transportName != "cxgb4i" {
132 t.Errorf("extractTransportname: Could not extract correct iface.transport_name 'cxgb4i', got %s", transportName)
133 }
134 transportName = extractTransportname(fakeIscsiadmOutput[2])
135 if transportName != "tcp" {
136 t.Errorf("extractTransportname: Could not extract correct iface.transport_name 'tcp', got %s", transportName)
137 }
138 transportName = extractTransportname(fakeIscsiadmOutput[3])
139 if transportName != "" {
140 t.Errorf("extractTransportname: Could not extract correct iface.transport_name '', got %s", transportName)
141 }
142 }
143
144 func TestWaitForPathToExist(t *testing.T) {
145 devicePath := []string{"/dev/disk/by-path/ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0",
146 "/dev/disk/by-path/pci-*-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"}
147 fpath := "/dev/disk/by-path/pci-0000:00:00.0-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"
148
149 exist := waitForPathToExistInternal(&devicePath[0], 1, "tcp", fakeOsStat, filepath.Glob)
150 if exist == false {
151 t.Errorf("waitForPathToExist: could not find path %s", devicePath[0])
152 }
153 exist = waitForPathToExistInternal(&devicePath[0], 1, "fake_iface", fakeOsStat, filepath.Glob)
154 if exist != false {
155 t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[0])
156 }
157
158 exist = waitForPathToExistInternal(&devicePath[1], 1, "fake_iface", os.Stat, fakeFilepathGlob)
159 if exist == false {
160 t.Errorf("waitForPathToExist: could not find path %s", devicePath[1])
161 }
162 exist = waitForPathToExistInternal(&devicePath[1], 1, "tcp", os.Stat, fakeFilepathGlob)
163 if exist != false {
164 t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[1])
165 }
166
167 _ = waitForPathToExistInternal(&devicePath[1], 1, "fake_iface", os.Stat, fakeFilepathGlob2)
168 if devicePath[1] != fpath {
169 t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[1])
170 }
171 }
172
173 func TestParseIscsiadmShow(t *testing.T) {
174 fakeIscsiadmOutput1 := "# BEGIN RECORD 2.0-873\n" +
175 "iface.iscsi_ifacename = default\n" +
176 "iface.transport_name = tcp\n" +
177 "iface.initiatorname = <empty>\n" +
178 "iface.mtu = 0\n" +
179 "# END RECORD"
180
181 fakeIscsiadmOutput2 := "# BEGIN RECORD 2.0-873\n" +
182 "iface.iscsi_ifacename = default\n" +
183 "iface.transport_name = cxgb4i\n" +
184 "iface.initiatorname = <empty>\n" +
185 "iface.mtu = 0\n" +
186 "# END RECORD"
187
188 fakeIscsiadmOutput3 := "# BEGIN RECORD 2.0-873\n" +
189 "iface.iscsi_ifacename = custom\n" +
190 "iface.transport_name = <empty>\n" +
191 "iface.initiatorname = <empty>\n" +
192 "iface.mtu = 0\n" +
193 "# END RECORD"
194
195 fakeIscsiadmOutput4 := "iface.iscsi_ifacename=error"
196 fakeIscsiadmOutput5 := "iface.iscsi_ifacename + error"
197
198 expectedIscsiadmOutput1 := map[string]string{
199 "iface.transport_name": "tcp",
200 "iface.mtu": "0"}
201
202 expectedIscsiadmOutput2 := map[string]string{
203 "iface.transport_name": "cxgb4i",
204 "iface.mtu": "0"}
205
206 expectedIscsiadmOutput3 := map[string]string{
207 "iface.mtu": "0"}
208
209 params, _ := parseIscsiadmShow(fakeIscsiadmOutput1)
210 if !reflect.DeepEqual(params, expectedIscsiadmOutput1) {
211 t.Errorf("parseIscsiadmShow: Fail to parse iface record: %s", params)
212 }
213 params, _ = parseIscsiadmShow(fakeIscsiadmOutput2)
214 if !reflect.DeepEqual(params, expectedIscsiadmOutput2) {
215 t.Errorf("parseIscsiadmShow: Fail to parse iface record: %s", params)
216 }
217 params, _ = parseIscsiadmShow(fakeIscsiadmOutput3)
218 if !reflect.DeepEqual(params, expectedIscsiadmOutput3) {
219 t.Errorf("parseIscsiadmShow: Fail to parse iface record: %s", params)
220 }
221 _, err := parseIscsiadmShow(fakeIscsiadmOutput4)
222 if err == nil {
223 t.Errorf("parseIscsiadmShow: Fail to handle invalid record: iface %s", fakeIscsiadmOutput4)
224 }
225 _, err = parseIscsiadmShow(fakeIscsiadmOutput5)
226 if err == nil {
227 t.Errorf("parseIscsiadmShow: Fail to handle invalid record: iface %s", fakeIscsiadmOutput5)
228 }
229 }
230
231 func TestClonedIface(t *testing.T) {
232 fakeExec := &testingexec.FakeExec{}
233 scripts := []volumetest.CommandScript{
234 {
235 Cmd: "iscsiadm",
236 Args: []string{"-m", "iface", "-I", "", "-o", "show"},
237 Output: "iface.ipaddress = <empty>\niface.transport_name = tcp\niface.initiatorname = <empty>\n",
238 },
239 {
240 Cmd: "iscsiadm",
241 Args: []string{"-m", "iface", "-I", TestIface, "-o", "new"},
242 },
243 {
244 Cmd: "iscsiadm",
245 Args: []string{"-m", "iface", "-I", TestIface, "-o", "update", "-n", "iface.initiatorname", "-v", ""},
246 },
247 {
248 Cmd: "iscsiadm",
249 Args: []string{"-m", "iface", "-I", TestIface, "-o", "update", "-n", "iface.transport_name", "-v", "tcp"},
250 },
251 }
252 volumetest.ScriptCommands(fakeExec, scripts)
253 fakeExec.ExactOrder = true
254 plugins := []volume.VolumePlugin{
255 &iscsiPlugin{
256 host: nil,
257 },
258 }
259 plugin := plugins[0]
260 fakeMounter := iscsiDiskMounter{
261 iscsiDisk: &iscsiDisk{
262 Iface: TestIface,
263 plugin: plugin.(*iscsiPlugin)},
264 exec: fakeExec,
265 }
266 err := cloneIface(fakeMounter)
267 if err != nil {
268 t.Errorf("unexpected error: %v", err)
269 }
270 if fakeExec.CommandCalls != len(scripts) {
271 t.Errorf("expected 4 CombinedOutput() calls, got %d", fakeExec.CommandCalls)
272 }
273 }
274
275 func TestClonedIfaceShowError(t *testing.T) {
276 fakeExec := &testingexec.FakeExec{}
277 scripts := []volumetest.CommandScript{
278 {
279 Cmd: "iscsiadm",
280 Args: []string{"-m", "iface", "-I", "", "-o", "show"},
281 Output: "test error",
282 ReturnCode: 1,
283 },
284 }
285 volumetest.ScriptCommands(fakeExec, scripts)
286 fakeExec.ExactOrder = true
287
288 plugins := []volume.VolumePlugin{
289 &iscsiPlugin{
290 host: nil,
291 },
292 }
293 plugin := plugins[0]
294 fakeMounter := iscsiDiskMounter{
295 iscsiDisk: &iscsiDisk{
296 Iface: TestIface,
297 plugin: plugin.(*iscsiPlugin)},
298 exec: fakeExec,
299 }
300 err := cloneIface(fakeMounter)
301 if err == nil {
302 t.Errorf("expect to receive error, nil received")
303 }
304 if fakeExec.CommandCalls != len(scripts) {
305 t.Errorf("expected 1 CombinedOutput() calls, got %d", fakeExec.CommandCalls)
306 }
307
308 }
309
310 func TestClonedIfaceUpdateError(t *testing.T) {
311 fakeExec := &testingexec.FakeExec{}
312 scripts := []volumetest.CommandScript{
313 {
314 Cmd: "iscsiadm",
315 Args: []string{"-m", "iface", "-I", "", "-o", "show"},
316 Output: "iface.ipaddress = <empty>\niface.transport_name = tcp\niface.initiatorname = <empty>\n",
317 },
318 {
319 Cmd: "iscsiadm",
320 Args: []string{"-m", "iface", "-I", TestIface, "-o", "new"},
321 },
322 {
323 Cmd: "iscsiadm",
324 Args: []string{"-m", "iface", "-I", TestIface, "-o", "update", "-n", "iface.initiatorname", "-v", ""},
325 },
326 {
327 Cmd: "iscsiadm",
328 Args: []string{"-m", "iface", "-I", TestIface, "-o", "update", "-n", "iface.transport_name", "-v", "tcp"},
329 ReturnCode: 1,
330 },
331 {
332 Cmd: "iscsiadm",
333 Args: []string{"-m", "iface", "-I", TestIface, "-o", "delete"},
334 },
335 }
336 volumetest.ScriptCommands(fakeExec, scripts)
337 fakeExec.ExactOrder = true
338
339 plugins := []volume.VolumePlugin{
340 &iscsiPlugin{
341 host: nil,
342 },
343 }
344 plugin := plugins[0]
345 fakeMounter := iscsiDiskMounter{
346 iscsiDisk: &iscsiDisk{
347 Iface: TestIface,
348 plugin: plugin.(*iscsiPlugin)},
349 exec: fakeExec,
350 }
351 err := cloneIface(fakeMounter)
352 if err == nil {
353 t.Errorf("expect to receive error, nil received")
354 }
355 if fakeExec.CommandCalls != len(scripts) {
356 t.Errorf("expected 5 CombinedOutput() calls, got %d", fakeExec.CommandCalls)
357 }
358
359 }
360
361 func TestGetVolCount(t *testing.T) {
362
363
364
365
366
367
368
369
370
371
372
373 baseDir, err := createFakePluginDirs()
374 if err != nil {
375 t.Errorf("error creating fake plugin dir: %v", err)
376 }
377
378 defer os.RemoveAll(baseDir)
379
380 testCases := []struct {
381 name string
382 baseDir string
383 portal string
384 iqn string
385 count int
386 }{
387 {
388 name: "wrong portal, no volumes",
389 baseDir: baseDir,
390 portal: "192.168.0.2:3260",
391 iqn: "iqn.2003-01.io.k8s:e2e.volume-1",
392 count: 0,
393 },
394 {
395 name: "wrong iqn, no volumes",
396 baseDir: baseDir,
397 portal: "127.0.0.1:3260",
398 iqn: "iqn.2003-01.io.k8s:e2e.volume-3",
399 count: 0,
400 },
401 {
402 name: "single volume",
403 baseDir: baseDir,
404 portal: "192.168.0.1:3260",
405 iqn: "iqn.2003-01.io.k8s:e2e.volume-1",
406 count: 1,
407 },
408 {
409 name: "two volumes",
410 baseDir: baseDir,
411 portal: "127.0.0.1:3260",
412 iqn: "iqn.2003-01.io.k8s:e2e.volume-1",
413 count: 2,
414 },
415 {
416 name: "volumeDevices (block) volume",
417 baseDir: filepath.Join(baseDir, config.DefaultKubeletVolumeDevicesDirName),
418 portal: "192.168.0.2:3260",
419 iqn: "iqn.2003-01.io.k8s:e2e.volume-1-lun-4",
420 count: 1,
421 },
422 {
423 name: "nonexistent path",
424 baseDir: filepath.Join(baseDir, "this_path_should_not_exist"),
425 portal: "127.0.0.1:3260",
426 iqn: "iqn.2003-01.io.k8s:e2e.unknown",
427 count: 0,
428 },
429 }
430
431 for _, tc := range testCases {
432 t.Run(tc.name, func(t *testing.T) {
433 count, err := getVolCount(tc.baseDir, tc.portal, tc.iqn)
434 if err != nil {
435 t.Errorf("expected no error, got %v", err)
436 }
437 if count != tc.count {
438 t.Errorf("expected %d volumes, got %d", tc.count, count)
439 }
440 })
441 }
442 }
443
444 func createFakePluginDirs() (string, error) {
445 dir, err := ioutil.TempDir("", "refcounter")
446 if err != nil {
447 return "", err
448 }
449
450 subdirs := []string{
451 "iface-127.0.0.1:3260:pv1/127.0.0.1:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-3",
452 "iface-127.0.0.1:3260:pv2/127.0.0.1:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-2",
453 "iface-127.0.0.1:3260:pv2/192.168.0.1:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-1",
454 filepath.Join(config.DefaultKubeletVolumeDevicesDirName, "iface-127.0.0.1:3260/192.168.0.2:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-4"),
455 filepath.Join(config.DefaultKubeletVolumeDevicesDirName, "iface-127.0.0.1:3260/192.168.0.3:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-5"),
456 }
457
458 for _, d := range subdirs {
459 if err := os.MkdirAll(filepath.Join(dir, d), os.ModePerm); err != nil {
460 return dir, err
461 }
462 }
463
464 return dir, err
465 }
466
View as plain text