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
+ execWithTimeout = fakeExecWithTimeout
332
+ devicePath := multipathDevice
333
+
334
+ tests := []struct {
335
+ name string
336
+ timeout bool
337
+ removeDevice bool
338
+ wantErr bool
339
+ cmdError error
340
+ }{
341
+ {"DisconnectMultipathVolume" , false , false , false , nil },
342
+ {"DisconnectMultipathVolumeFlushFailed" , false , false , true , fmt .Errorf ("error" )},
343
+ {"DisconnectMultipathVolumeFlushTimeout" , true , false , true , nil },
344
+ {"DisconnectNonexistentMultipathVolume" , false , true , false , fmt .Errorf ("error" )},
345
+ }
346
+ for _ , tt := range tests {
347
+ t .Run (tt .name , func (t * testing.T ) {
348
+
349
+ tmpDir , err := ioutil .TempDir ("" , "" )
350
+ if err != nil {
351
+ t .Errorf ("can not create temp directory: %v" , err )
352
+ return
353
+ }
354
+ sysBlockPath = tmpDir
355
+ devPath = filepath .Join (tmpDir , "dev" )
356
+ defer os .RemoveAll (tmpDir )
357
+
358
+ err = preparePaths (tmpDir )
359
+ if err != nil {
360
+ t .Errorf ("can not create temp directories and files: %v" , err )
361
+ return
362
+ }
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 && ! tt .wantErr {
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