1
1
package iscsi
2
2
3
3
import (
4
+ "context"
4
5
"fmt"
6
+ "io/ioutil"
5
7
"os"
6
8
"os/exec"
9
+ "path/filepath"
7
10
"reflect"
8
11
"strconv"
9
12
"testing"
13
+ "time"
10
14
)
11
15
12
16
var nodeDB = `
@@ -79,10 +83,16 @@ node.conn[0].iscsi.OFMarker = No
79
83
var emptyTransportName = "iface.transport_name = \n "
80
84
var emptyDbRecord = "\n \n \n "
81
85
var testCmdOutput = ""
86
+ var testCmdTimeout = false
82
87
var testCmdError error
88
+ var testExecWithTimeoutError error
83
89
var mockedExitStatus = 0
84
90
var mockedStdout string
85
91
92
+ var normalDevice = "sda"
93
+ var multipathDevice = "dm-1"
94
+ var slaves = []string {"sdb" , "sdc" }
95
+
86
96
type testCmdRunner struct {}
87
97
88
98
func fakeExecCommand (command string , args ... string ) * exec.Cmd {
@@ -96,6 +106,13 @@ func fakeExecCommand(command string, args ...string) *exec.Cmd {
96
106
return cmd
97
107
}
98
108
109
+ func fakeExecWithTimeout (command string , args []string , timeout time.Duration ) ([]byte , error ) {
110
+ if testCmdTimeout {
111
+ return nil , context .DeadlineExceeded
112
+ }
113
+ return []byte (testCmdOutput ), testExecWithTimeoutError
114
+ }
115
+
99
116
func TestExecCommandHelper (t * testing.T ) {
100
117
if os .Getenv ("GO_WANT_HELPER_PROCESS" ) != "1" {
101
118
return
@@ -111,6 +128,33 @@ func (tr testCmdRunner) execCmd(cmd string, args ...string) (string, error) {
111
128
112
129
}
113
130
131
+ func preparePaths (sysBlockPath string ) error {
132
+ for _ , d := range append (slaves , normalDevice ) {
133
+ devicePath := filepath .Join (sysBlockPath , d , "device" )
134
+ err := os .MkdirAll (devicePath , os .ModePerm )
135
+ if err != nil {
136
+ return err
137
+ }
138
+ err = ioutil .WriteFile (filepath .Join (devicePath , "delete" ), []byte ("" ), 0600 )
139
+ if err != nil {
140
+ return err
141
+ }
142
+ }
143
+ for _ , s := range slaves {
144
+ err := os .MkdirAll (filepath .Join (sysBlockPath , multipathDevice , "slaves" , s ), os .ModePerm )
145
+ if err != nil {
146
+ return err
147
+ }
148
+ }
149
+
150
+ err := os .MkdirAll (filepath .Join (sysBlockPath , "dev" , multipathDevice ), os .ModePerm )
151
+ if err != nil {
152
+ return err
153
+ }
154
+ return nil
155
+
156
+ }
157
+
114
158
func Test_parseSessions (t * testing.T ) {
115
159
var sessions []iscsiSession
116
160
output := "tcp: [2] 192.168.1.107:3260,1 iqn.2010-10.org.openstack:volume-eb393993-73d0-4e39-9ef4-b5841e244ced (non-flash)\n " +
@@ -226,3 +270,130 @@ func Test_sessionExists(t *testing.T) {
226
270
})
227
271
}
228
272
}
273
+
274
+ func Test_DisconnectNormalVolume (t * testing.T ) {
275
+
276
+ tmpDir , err := ioutil .TempDir ("" , "" )
277
+ if err != nil {
278
+ t .Errorf ("can not create temp directory: %v" , err )
279
+ return
280
+ }
281
+ sysBlockPath = tmpDir
282
+ defer os .RemoveAll (tmpDir )
283
+
284
+ err = preparePaths (tmpDir )
285
+ if err != nil {
286
+ t .Errorf ("can not create temp directories and files: %v" , err )
287
+ return
288
+ }
289
+
290
+ execWithTimeout = fakeExecWithTimeout
291
+ devicePath := normalDevice
292
+
293
+ tests := []struct {
294
+ name string
295
+ removeDevice bool
296
+ wantErr bool
297
+ }{
298
+ {"DisconnectNormalVolume" , false , false },
299
+ {"DisconnectNonexistentNormalVolume" , true , false },
300
+ }
301
+ for _ , tt := range tests {
302
+ t .Run (tt .name , func (t * testing.T ) {
303
+ if tt .removeDevice {
304
+ os .RemoveAll (filepath .Join (sysBlockPath , devicePath ))
305
+ }
306
+ c := Connector {Multipath : false , DevicePath : devicePath }
307
+ err := DisconnectVolume (c )
308
+ if (err != nil ) != tt .wantErr {
309
+ t .Errorf ("DisconnectVolume() error = %v, wantErr %v" , err , tt .wantErr )
310
+ return
311
+ }
312
+
313
+ if ! tt .removeDevice {
314
+ deleteFile := filepath .Join (sysBlockPath , devicePath , "device" , "delete" )
315
+ out , err := ioutil .ReadFile (deleteFile )
316
+ if err != nil {
317
+ t .Errorf ("can not read file %v: %v" , deleteFile , err )
318
+ return
319
+ }
320
+ if string (out ) != "1" {
321
+ t .Errorf ("file content mismatch, got = %s, want = 1" , string (out ))
322
+ return
323
+ }
324
+ }
325
+ })
326
+ }
327
+ }
328
+
329
+ func Test_DisconnectMultipathVolume (t * testing.T ) {
330
+
331
+ tmpDir , err := ioutil .TempDir ("" , "" )
332
+ if err != nil {
333
+ t .Errorf ("can not create temp directory: %v" , err )
334
+ return
335
+ }
336
+ sysBlockPath = tmpDir
337
+ devPath = filepath .Join (tmpDir , "dev" )
338
+ defer os .RemoveAll (tmpDir )
339
+
340
+ err = preparePaths (tmpDir )
341
+ if err != nil {
342
+ t .Errorf ("can not create temp directories and files: %v" , err )
343
+ return
344
+ }
345
+
346
+ execWithTimeout = fakeExecWithTimeout
347
+ devicePath := multipathDevice
348
+
349
+ tests := []struct {
350
+ name string
351
+ timeout bool
352
+ removeDevice bool
353
+ wantErr bool
354
+ cmdError error
355
+ }{
356
+ {"DisconnectMultipathVolume" , false , false , false , nil },
357
+ {"DisconnectMultipathVolumeFlushFailed" , false , false , true , fmt .Errorf ("error" )},
358
+ {"DisconnectMultipathVolumeFlushTimeout" , true , false , true , nil },
359
+ {"DisconnectNonexistentMultipathVolume" , false , false , true , fmt .Errorf ("error" )},
360
+ }
361
+ for _ , tt := range tests {
362
+ t .Run (tt .name , func (t * testing.T ) {
363
+ testExecWithTimeoutError = tt .cmdError
364
+ testCmdTimeout = tt .timeout
365
+ if tt .removeDevice {
366
+ os .RemoveAll (filepath .Join (sysBlockPath , devicePath ))
367
+ os .RemoveAll (devPath )
368
+ }
369
+ c := Connector {Multipath : true , DevicePath : devicePath }
370
+ err := DisconnectVolume (c )
371
+ if (err != nil ) != tt .wantErr {
372
+ t .Errorf ("DisconnectVolume() error = %v, wantErr %v" , err , tt .wantErr )
373
+ return
374
+ }
375
+ if tt .timeout {
376
+ if err != context .DeadlineExceeded {
377
+ t .Errorf ("DisconnectVolume() error = %v, wantErr %v" , err , context .DeadlineExceeded )
378
+ return
379
+ }
380
+ }
381
+
382
+ if ! tt .removeDevice {
383
+ for _ , s := range slaves {
384
+ deleteFile := filepath .Join (sysBlockPath , s , "device" , "delete" )
385
+ out , err := ioutil .ReadFile (deleteFile )
386
+ if err != nil {
387
+ t .Errorf ("can not read file %v: %v" , deleteFile , err )
388
+ return
389
+ }
390
+ if string (out ) != "1" {
391
+ t .Errorf ("file content mismatch, got = %s, want = 1" , string (out ))
392
+ return
393
+ }
394
+ }
395
+
396
+ }
397
+ })
398
+ }
399
+ }
0 commit comments