...

Source file src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_util_test.go

Documentation: k8s.io/kubernetes/pkg/volume/iscsi

     1  //go:build !windows
     2  // +build !windows
     3  
     4  /*
     5  Copyright 2015 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    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  	// This will create a dir structure like this:
   363  	// /tmp/refcounter555814673
   364  	// +-- iface-127.0.0.1:3260:pv1
   365  	// |   +-- 127.0.0.1:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-3
   366  	// +-- iface-127.0.0.1:3260:pv2
   367  	// |   +-- 127.0.0.1:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-2
   368  	// |   +-- 192.168.0.1:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-1
   369  	// +-- volumeDevices
   370  	//     +-- 192.168.0.2:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-4
   371  	//     +-- 192.168.0.3:3260-iqn.2003-01.io.k8s:e2e.volume-1-lun-5
   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", // incorrect IP address
   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", // incorrect volume
   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