...

Source file src/k8s.io/kubernetes/pkg/volume/local/local_test.go

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

     1  //go:build linux || darwin || windows
     2  // +build linux darwin windows
     3  
     4  /*
     5  Copyright 2017 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 local
    21  
    22  import (
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  	"reflect"
    27  	"runtime"
    28  	"testing"
    29  
    30  	v1 "k8s.io/api/core/v1"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/types"
    33  	utiltesting "k8s.io/client-go/util/testing"
    34  	"k8s.io/kubernetes/pkg/volume"
    35  	volumetest "k8s.io/kubernetes/pkg/volume/testing"
    36  	"k8s.io/kubernetes/pkg/volume/util/hostutil"
    37  	"k8s.io/mount-utils"
    38  )
    39  
    40  const (
    41  	testPVName                        = "pvA"
    42  	testMountPath                     = "pods/poduid/volumes/kubernetes.io~local-volume/pvA"
    43  	testGlobalPath                    = "plugins/kubernetes.io~local-volume/volumeDevices/pvA"
    44  	testPodPath                       = "pods/poduid/volumeDevices/kubernetes.io~local-volume"
    45  	testBlockFormattingToFSGlobalPath = "plugins/kubernetes.io/local-volume/mounts/pvA"
    46  )
    47  
    48  func getPlugin(t *testing.T) (string, volume.VolumePlugin) {
    49  	tmpDir, err := utiltesting.MkTmpdir("localVolumeTest")
    50  	if err != nil {
    51  		t.Fatalf("can't make a temp dir: %v", err)
    52  	}
    53  
    54  	plugMgr := volume.VolumePluginMgr{}
    55  	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil))
    56  
    57  	plug, err := plugMgr.FindPluginByName(localVolumePluginName)
    58  	if err != nil {
    59  		os.RemoveAll(tmpDir)
    60  		t.Fatalf("Can't find the plugin by name")
    61  	}
    62  	if plug.GetPluginName() != localVolumePluginName {
    63  		t.Errorf("Wrong name: %s", plug.GetPluginName())
    64  	}
    65  	return tmpDir, plug
    66  }
    67  
    68  func getBlockPlugin(t *testing.T) (string, volume.BlockVolumePlugin) {
    69  	tmpDir, err := utiltesting.MkTmpdir("localVolumeTest")
    70  	if err != nil {
    71  		t.Fatalf("can't make a temp dir: %v", err)
    72  	}
    73  
    74  	plugMgr := volume.VolumePluginMgr{}
    75  	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil))
    76  	plug, err := plugMgr.FindMapperPluginByName(localVolumePluginName)
    77  	if err != nil {
    78  		os.RemoveAll(tmpDir)
    79  		t.Fatalf("Can't find the plugin by name: %q", localVolumePluginName)
    80  	}
    81  	if plug.GetPluginName() != localVolumePluginName {
    82  		t.Errorf("Wrong name: %s", plug.GetPluginName())
    83  	}
    84  	return tmpDir, plug
    85  }
    86  
    87  func getNodeExpandablePlugin(t *testing.T, isBlockDevice bool) (string, volume.NodeExpandableVolumePlugin) {
    88  	tmpDir, err := utiltesting.MkTmpdir("localVolumeTest")
    89  	if err != nil {
    90  		t.Fatalf("can't make a temp dir: %v", err)
    91  	}
    92  
    93  	plugMgr := volume.VolumePluginMgr{}
    94  	var pathToFSType map[string]hostutil.FileType
    95  	if isBlockDevice {
    96  		pathToFSType = map[string]hostutil.FileType{
    97  			tmpDir: hostutil.FileTypeBlockDev,
    98  		}
    99  	} else {
   100  		pathToFSType = map[string]hostutil.FileType{
   101  			tmpDir: hostutil.FileTypeDirectory,
   102  		}
   103  	}
   104  
   105  	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHostWithMounterFSType(t, tmpDir, nil, nil, pathToFSType))
   106  
   107  	plug, err := plugMgr.FindNodeExpandablePluginByName(localVolumePluginName)
   108  	if err != nil {
   109  		os.RemoveAll(tmpDir)
   110  		t.Fatalf("Can't find the plugin by name")
   111  	}
   112  	if plug.GetPluginName() != localVolumePluginName {
   113  		t.Errorf("Wrong name: %s", plug.GetPluginName())
   114  	}
   115  	return tmpDir, plug
   116  }
   117  
   118  func getPersistentPlugin(t *testing.T) (string, volume.PersistentVolumePlugin) {
   119  	tmpDir, err := utiltesting.MkTmpdir("localVolumeTest")
   120  	if err != nil {
   121  		t.Fatalf("can't make a temp dir: %v", err)
   122  	}
   123  
   124  	plugMgr := volume.VolumePluginMgr{}
   125  	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil))
   126  
   127  	plug, err := plugMgr.FindPersistentPluginByName(localVolumePluginName)
   128  	if err != nil {
   129  		os.RemoveAll(tmpDir)
   130  		t.Fatalf("Can't find the plugin by name")
   131  	}
   132  	if plug.GetPluginName() != localVolumePluginName {
   133  		t.Errorf("Wrong name: %s", plug.GetPluginName())
   134  	}
   135  	return tmpDir, plug
   136  }
   137  
   138  func getDeviceMountablePluginWithBlockPath(t *testing.T, isBlockDevice bool) (string, volume.DeviceMountableVolumePlugin) {
   139  	var (
   140  		source string
   141  		err    error
   142  	)
   143  
   144  	if isBlockDevice && runtime.GOOS == "windows" {
   145  		// On Windows, block devices are referenced by the disk number, which is validated by the mounter,
   146  		source = "0"
   147  	} else {
   148  		source, err = utiltesting.MkTmpdir("localVolumeTest")
   149  		if err != nil {
   150  			t.Fatalf("can't make a temp dir: %v", err)
   151  		}
   152  	}
   153  
   154  	plugMgr := volume.VolumePluginMgr{}
   155  	var pathToFSType map[string]hostutil.FileType
   156  	if isBlockDevice {
   157  		pathToFSType = map[string]hostutil.FileType{
   158  			source: hostutil.FileTypeBlockDev,
   159  		}
   160  	}
   161  
   162  	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHostWithMounterFSType(t, source, nil, nil, pathToFSType))
   163  
   164  	plug, err := plugMgr.FindDeviceMountablePluginByName(localVolumePluginName)
   165  	if err != nil {
   166  		os.RemoveAll(source)
   167  		t.Fatalf("Can't find the plugin by name")
   168  	}
   169  	if plug.GetPluginName() != localVolumePluginName {
   170  		t.Errorf("Wrong name: %s", plug.GetPluginName())
   171  	}
   172  	return source, plug
   173  }
   174  
   175  func getTestVolume(readOnly bool, path string, isBlock bool, mountOptions []string) *volume.Spec {
   176  	pv := &v1.PersistentVolume{
   177  		ObjectMeta: metav1.ObjectMeta{
   178  			Name: testPVName,
   179  		},
   180  		Spec: v1.PersistentVolumeSpec{
   181  			PersistentVolumeSource: v1.PersistentVolumeSource{
   182  				Local: &v1.LocalVolumeSource{
   183  					Path: path,
   184  				},
   185  			},
   186  			MountOptions: mountOptions,
   187  		},
   188  	}
   189  
   190  	if isBlock {
   191  		blockMode := v1.PersistentVolumeBlock
   192  		pv.Spec.VolumeMode = &blockMode
   193  	} else {
   194  		fsMode := v1.PersistentVolumeFilesystem
   195  		pv.Spec.VolumeMode = &fsMode
   196  	}
   197  	return volume.NewSpecFromPersistentVolume(pv, readOnly)
   198  }
   199  
   200  func TestCanSupport(t *testing.T) {
   201  	tmpDir, plug := getPlugin(t)
   202  	defer os.RemoveAll(tmpDir)
   203  
   204  	if !plug.CanSupport(getTestVolume(false, tmpDir, false, nil)) {
   205  		t.Errorf("Expected true")
   206  	}
   207  }
   208  
   209  func TestGetAccessModes(t *testing.T) {
   210  	tmpDir, plug := getPersistentPlugin(t)
   211  	defer os.RemoveAll(tmpDir)
   212  
   213  	modes := plug.GetAccessModes()
   214  	if !volumetest.ContainsAccessMode(modes, v1.ReadWriteOnce) {
   215  		t.Errorf("Expected AccessModeType %q", v1.ReadWriteOnce)
   216  	}
   217  
   218  	if volumetest.ContainsAccessMode(modes, v1.ReadWriteMany) {
   219  		t.Errorf("Found AccessModeType %q, expected not", v1.ReadWriteMany)
   220  	}
   221  	if volumetest.ContainsAccessMode(modes, v1.ReadOnlyMany) {
   222  		t.Errorf("Found AccessModeType %q, expected not", v1.ReadOnlyMany)
   223  	}
   224  }
   225  
   226  func TestGetVolumeName(t *testing.T) {
   227  	tmpDir, plug := getPersistentPlugin(t)
   228  	defer os.RemoveAll(tmpDir)
   229  
   230  	volName, err := plug.GetVolumeName(getTestVolume(false, tmpDir, false, nil))
   231  	if err != nil {
   232  		t.Errorf("Failed to get volume name: %v", err)
   233  	}
   234  	if volName != testPVName {
   235  		t.Errorf("Expected volume name %q, got %q", testPVName, volName)
   236  	}
   237  }
   238  
   239  func TestInvalidLocalPath(t *testing.T) {
   240  	tmpDir, plug := getPlugin(t)
   241  	defer os.RemoveAll(tmpDir)
   242  
   243  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
   244  	mounter, err := plug.NewMounter(getTestVolume(false, "/no/backsteps/allowed/..", false, nil), pod, volume.VolumeOptions{})
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  
   249  	err = mounter.SetUp(volume.MounterArgs{})
   250  	expectedMsg := "invalid path: /no/backsteps/allowed/.. must not contain '..'"
   251  	if err.Error() != expectedMsg {
   252  		t.Fatalf("expected error `%s` but got `%s`", expectedMsg, err)
   253  	}
   254  }
   255  
   256  func TestBlockDeviceGlobalPathAndMountDevice(t *testing.T) {
   257  	// Block device global mount path testing
   258  	tmpBlockDir, plug := getDeviceMountablePluginWithBlockPath(t, true)
   259  	defer os.RemoveAll(tmpBlockDir)
   260  
   261  	dm, err := plug.NewDeviceMounter()
   262  	if err != nil {
   263  		t.Errorf("Failed to make a new device mounter: %v", err)
   264  	}
   265  
   266  	pvSpec := getTestVolume(false, tmpBlockDir, false, nil)
   267  
   268  	expectedGlobalPath := filepath.Join(tmpBlockDir, testBlockFormattingToFSGlobalPath)
   269  	actualPath, err := dm.GetDeviceMountPath(pvSpec)
   270  	if err != nil {
   271  		t.Errorf("Failed to get device mount path: %v", err)
   272  	}
   273  	if expectedGlobalPath != actualPath {
   274  		t.Fatalf("Expected device mount global path:%s, got: %s", expectedGlobalPath, actualPath)
   275  	}
   276  
   277  	fmt.Println("expected global path is:", expectedGlobalPath)
   278  
   279  	err = dm.MountDevice(pvSpec, tmpBlockDir, expectedGlobalPath, volume.DeviceMounterArgs{})
   280  	if err != nil {
   281  		t.Fatal(err)
   282  	}
   283  	if _, err := os.Stat(actualPath); err != nil {
   284  		if os.IsNotExist(err) {
   285  			t.Errorf("DeviceMounter.MountDevice() failed, device mount path not created: %s", actualPath)
   286  		} else {
   287  			t.Errorf("DeviceMounter.MountDevice() failed: %v", err)
   288  		}
   289  	}
   290  
   291  	du, err := plug.NewDeviceUnmounter()
   292  	if err != nil {
   293  		t.Fatalf("Create device unmounter error: %v", err)
   294  	}
   295  
   296  	err = du.UnmountDevice(actualPath)
   297  	if err != nil {
   298  		t.Fatalf("Unmount device error: %v", err)
   299  	}
   300  }
   301  
   302  func TestFSGlobalPathAndMountDevice(t *testing.T) {
   303  	// FS global path testing
   304  	tmpFSDir, plug := getDeviceMountablePluginWithBlockPath(t, false)
   305  	defer os.RemoveAll(tmpFSDir)
   306  
   307  	dm, err := plug.NewDeviceMounter()
   308  	if err != nil {
   309  		t.Errorf("Failed to make a new device mounter: %v", err)
   310  	}
   311  
   312  	pvSpec := getTestVolume(false, tmpFSDir, false, nil)
   313  
   314  	expectedGlobalPath := tmpFSDir
   315  	actualPath, err := dm.GetDeviceMountPath(pvSpec)
   316  	if err != nil {
   317  		t.Errorf("Failed to get device mount path: %v", err)
   318  	}
   319  	if expectedGlobalPath != actualPath {
   320  		t.Fatalf("Expected device mount global path:%s, got: %s", expectedGlobalPath, actualPath)
   321  	}
   322  
   323  	// Actually, we will do nothing if the local path is FS type
   324  	err = dm.MountDevice(pvSpec, tmpFSDir, expectedGlobalPath, volume.DeviceMounterArgs{})
   325  	if err != nil {
   326  		t.Fatal(err)
   327  	}
   328  	if _, err := os.Stat(expectedGlobalPath); err != nil {
   329  		if os.IsNotExist(err) {
   330  			t.Errorf("DeviceMounter.MountDevice() failed, device mount path not created: %s", expectedGlobalPath)
   331  		} else {
   332  			t.Errorf("DeviceMounter.MountDevice() failed: %v", err)
   333  		}
   334  	}
   335  }
   336  
   337  func TestNodeExpand(t *testing.T) {
   338  	// FS global path testing
   339  	tmpFSDir, plug := getNodeExpandablePlugin(t, false)
   340  	defer os.RemoveAll(tmpFSDir)
   341  
   342  	pvSpec := getTestVolume(false, tmpFSDir, false, nil)
   343  
   344  	resizeOptions := volume.NodeResizeOptions{
   345  		VolumeSpec: pvSpec,
   346  		DevicePath: tmpFSDir,
   347  	}
   348  
   349  	// Actually, we will do no volume expansion if volume is of type dir
   350  	resizeDone, err := plug.NodeExpand(resizeOptions)
   351  	if err != nil {
   352  		t.Fatal(err)
   353  	}
   354  	if !resizeDone {
   355  		t.Errorf("expected resize to be done")
   356  	}
   357  }
   358  
   359  func TestMountUnmount(t *testing.T) {
   360  	tmpDir, plug := getPlugin(t)
   361  	defer os.RemoveAll(tmpDir)
   362  
   363  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
   364  	mounter, err := plug.NewMounter(getTestVolume(false, tmpDir, false, nil), pod, volume.VolumeOptions{})
   365  	if err != nil {
   366  		t.Errorf("Failed to make a new Mounter: %v", err)
   367  	}
   368  	if mounter == nil {
   369  		t.Fatalf("Got a nil Mounter")
   370  	}
   371  
   372  	volPath := filepath.Join(tmpDir, testMountPath)
   373  	path := mounter.GetPath()
   374  	if path != volPath {
   375  		t.Errorf("Got unexpected path: %s", path)
   376  	}
   377  
   378  	if err := mounter.SetUp(volume.MounterArgs{}); err != nil {
   379  		t.Errorf("Expected success, got: %v", err)
   380  	}
   381  
   382  	if runtime.GOOS != "windows" {
   383  		// skip this check in windows since the "bind mount" logic is implemented differently in mount_windows.go
   384  		if _, err := os.Stat(path); err != nil {
   385  			if os.IsNotExist(err) {
   386  				t.Errorf("SetUp() failed, volume path not created: %s", path)
   387  			} else {
   388  				t.Errorf("SetUp() failed: %v", err)
   389  			}
   390  		}
   391  	}
   392  
   393  	unmounter, err := plug.NewUnmounter(testPVName, pod.UID)
   394  	if err != nil {
   395  		t.Errorf("Failed to make a new Unmounter: %v", err)
   396  	}
   397  	if unmounter == nil {
   398  		t.Fatalf("Got a nil Unmounter")
   399  	}
   400  
   401  	if err := unmounter.TearDown(); err != nil {
   402  		t.Errorf("Expected success, got: %v", err)
   403  	}
   404  	if _, err := os.Stat(path); err == nil {
   405  		t.Errorf("TearDown() failed, volume path still exists: %s", path)
   406  	} else if !os.IsNotExist(err) {
   407  		t.Errorf("TearDown() failed: %v", err)
   408  	}
   409  }
   410  
   411  // TestMapUnmap tests block map and unmap interfaces.
   412  func TestMapUnmap(t *testing.T) {
   413  	tmpDir, plug := getBlockPlugin(t)
   414  	defer os.RemoveAll(tmpDir)
   415  
   416  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
   417  	volSpec := getTestVolume(false, tmpDir, true /*isBlock*/, nil)
   418  	mapper, err := plug.NewBlockVolumeMapper(volSpec, pod, volume.VolumeOptions{})
   419  	if err != nil {
   420  		t.Errorf("Failed to make a new Mounter: %v", err)
   421  	}
   422  	if mapper == nil {
   423  		t.Fatalf("Got a nil Mounter")
   424  	}
   425  
   426  	expectedGlobalPath := filepath.Join(tmpDir, testGlobalPath)
   427  	globalPath, err := mapper.GetGlobalMapPath(volSpec)
   428  	if err != nil {
   429  		t.Errorf("Failed to get global path: %v", err)
   430  	}
   431  	if globalPath != expectedGlobalPath {
   432  		t.Errorf("Got unexpected path: %s, expected %s", globalPath, expectedGlobalPath)
   433  	}
   434  	expectedPodPath := filepath.Join(tmpDir, testPodPath)
   435  	podPath, volName := mapper.GetPodDeviceMapPath()
   436  	if podPath != expectedPodPath {
   437  		t.Errorf("Got unexpected pod path: %s, expected %s", podPath, expectedPodPath)
   438  	}
   439  	if volName != testPVName {
   440  		t.Errorf("Got unexpected volNamne: %s, expected %s", volName, testPVName)
   441  	}
   442  	var devPath string
   443  
   444  	if customMapper, ok := mapper.(volume.CustomBlockVolumeMapper); ok {
   445  		_, err = customMapper.SetUpDevice()
   446  		if err != nil {
   447  			t.Errorf("Failed to SetUpDevice, err: %v", err)
   448  		}
   449  		devPath, err = customMapper.MapPodDevice()
   450  		if err != nil {
   451  			t.Errorf("Failed to MapPodDevice, err: %v", err)
   452  		}
   453  	}
   454  
   455  	if _, err := os.Stat(devPath); err != nil {
   456  		if os.IsNotExist(err) {
   457  			t.Errorf("SetUpDevice() failed, volume path not created: %s", devPath)
   458  		} else {
   459  			t.Errorf("SetUpDevice() failed: %v", err)
   460  		}
   461  	}
   462  
   463  	unmapper, err := plug.NewBlockVolumeUnmapper(testPVName, pod.UID)
   464  	if err != nil {
   465  		t.Fatalf("Failed to make a new Unmapper: %v", err)
   466  	}
   467  	if unmapper == nil {
   468  		t.Fatalf("Got a nil Unmapper")
   469  	}
   470  
   471  	if customUnmapper, ok := unmapper.(volume.CustomBlockVolumeUnmapper); ok {
   472  		if err := customUnmapper.UnmapPodDevice(); err != nil {
   473  			t.Errorf("UnmapPodDevice failed, err: %v", err)
   474  		}
   475  
   476  		if err := customUnmapper.TearDownDevice(globalPath, devPath); err != nil {
   477  			t.Errorf("TearDownDevice failed, err: %v", err)
   478  		}
   479  	}
   480  }
   481  
   482  func testFSGroupMount(plug volume.VolumePlugin, pod *v1.Pod, tmpDir string, fsGroup int64) error {
   483  	mounter, err := plug.NewMounter(getTestVolume(false, tmpDir, false, nil), pod, volume.VolumeOptions{})
   484  	if err != nil {
   485  		return err
   486  	}
   487  	if mounter == nil {
   488  		return fmt.Errorf("got a nil Mounter")
   489  	}
   490  
   491  	volPath := filepath.Join(tmpDir, testMountPath)
   492  	path := mounter.GetPath()
   493  	if path != volPath {
   494  		return fmt.Errorf("got unexpected path: %s", path)
   495  	}
   496  
   497  	var mounterArgs volume.MounterArgs
   498  	mounterArgs.FsGroup = &fsGroup
   499  	if err := mounter.SetUp(mounterArgs); err != nil {
   500  		return err
   501  	}
   502  	return nil
   503  }
   504  
   505  func TestConstructVolumeSpec(t *testing.T) {
   506  	tests := []struct {
   507  		name         string
   508  		mountPoints  []mount.MountPoint
   509  		expectedPath string
   510  	}{
   511  		{
   512  			name: "filesystem volume with directory source",
   513  			mountPoints: []mount.MountPoint{
   514  				{
   515  					Device: "/mnt/disk/ssd0",
   516  					Path:   "pods/poduid/volumes/kubernetes.io~local-volume/pvA",
   517  				},
   518  			},
   519  			expectedPath: "",
   520  		},
   521  		{
   522  			name: "filesystem volume with block source",
   523  			mountPoints: []mount.MountPoint{
   524  				{
   525  					Device: "/dev/loop0",
   526  					Path:   testMountPath,
   527  				},
   528  				{
   529  					Device: "/dev/loop0",
   530  					Path:   testBlockFormattingToFSGlobalPath,
   531  				},
   532  			},
   533  			expectedPath: "/dev/loop0",
   534  		},
   535  	}
   536  
   537  	for _, tt := range tests {
   538  		t.Run(tt.name, func(t *testing.T) {
   539  			tmpDir, err := utiltesting.MkTmpdir("localVolumeTest")
   540  			if err != nil {
   541  				t.Fatalf("can't make a temp dir: %v", err)
   542  			}
   543  			defer os.RemoveAll(tmpDir)
   544  			plug := &localVolumePlugin{
   545  				host: volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil),
   546  			}
   547  			mounter := plug.host.GetMounter(plug.GetPluginName())
   548  			fakeMountPoints := []mount.MountPoint{}
   549  			for _, mp := range tt.mountPoints {
   550  				fakeMountPoint := mp
   551  				fakeMountPoint.Path = filepath.Join(tmpDir, mp.Path)
   552  				fakeMountPoints = append(fakeMountPoints, fakeMountPoint)
   553  			}
   554  			mounter.(*mount.FakeMounter).MountPoints = fakeMountPoints
   555  			volPath := filepath.Join(tmpDir, testMountPath)
   556  			rec, err := plug.ConstructVolumeSpec(testPVName, volPath)
   557  			if err != nil {
   558  				t.Errorf("ConstructVolumeSpec() failed: %v", err)
   559  			}
   560  			if rec.Spec == nil {
   561  				t.Fatalf("ConstructVolumeSpec() returned nil")
   562  			}
   563  
   564  			volName := rec.Spec.Name()
   565  			if volName != testPVName {
   566  				t.Errorf("Expected volume name %q, got %q", testPVName, volName)
   567  			}
   568  
   569  			if rec.Spec.Volume != nil {
   570  				t.Errorf("Volume object returned, expected nil")
   571  			}
   572  
   573  			pv := rec.Spec.PersistentVolume
   574  			if pv == nil {
   575  				t.Fatalf("PersistentVolume object nil")
   576  			}
   577  
   578  			if rec.Spec.PersistentVolume.Spec.VolumeMode == nil {
   579  				t.Fatalf("Volume mode has not been set.")
   580  			}
   581  
   582  			if *rec.Spec.PersistentVolume.Spec.VolumeMode != v1.PersistentVolumeFilesystem {
   583  				t.Errorf("Unexpected volume mode %q", *rec.Spec.PersistentVolume.Spec.VolumeMode)
   584  			}
   585  
   586  			ls := pv.Spec.PersistentVolumeSource.Local
   587  			if ls == nil {
   588  				t.Fatalf("LocalVolumeSource object nil")
   589  			}
   590  
   591  			if pv.Spec.PersistentVolumeSource.Local.Path != tt.expectedPath {
   592  				t.Fatalf("Unexpected path got %q, expected %q", pv.Spec.PersistentVolumeSource.Local.Path, tt.expectedPath)
   593  			}
   594  		})
   595  	}
   596  
   597  }
   598  
   599  func TestConstructBlockVolumeSpec(t *testing.T) {
   600  	tmpDir, plug := getBlockPlugin(t)
   601  	defer os.RemoveAll(tmpDir)
   602  
   603  	podPath := filepath.Join(tmpDir, testPodPath)
   604  	spec, err := plug.ConstructBlockVolumeSpec(types.UID("poduid"), testPVName, podPath)
   605  	if err != nil {
   606  		t.Errorf("ConstructBlockVolumeSpec() failed: %v", err)
   607  	}
   608  	if spec == nil {
   609  		t.Fatalf("ConstructBlockVolumeSpec() returned nil")
   610  	}
   611  
   612  	volName := spec.Name()
   613  	if volName != testPVName {
   614  		t.Errorf("Expected volume name %q, got %q", testPVName, volName)
   615  	}
   616  
   617  	if spec.Volume != nil {
   618  		t.Errorf("Volume object returned, expected nil")
   619  	}
   620  
   621  	pv := spec.PersistentVolume
   622  	if pv == nil {
   623  		t.Fatalf("PersistentVolume object nil")
   624  	}
   625  
   626  	if spec.PersistentVolume.Spec.VolumeMode == nil {
   627  		t.Fatalf("Volume mode has not been set.")
   628  	}
   629  
   630  	if *spec.PersistentVolume.Spec.VolumeMode != v1.PersistentVolumeBlock {
   631  		t.Errorf("Unexpected volume mode %q", *spec.PersistentVolume.Spec.VolumeMode)
   632  	}
   633  
   634  	ls := pv.Spec.PersistentVolumeSource.Local
   635  	if ls == nil {
   636  		t.Fatalf("LocalVolumeSource object nil")
   637  	}
   638  }
   639  
   640  func TestMountOptions(t *testing.T) {
   641  	tmpDir, plug := getPlugin(t)
   642  	defer os.RemoveAll(tmpDir)
   643  
   644  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
   645  	mounter, err := plug.NewMounter(getTestVolume(false, tmpDir, false, []string{"test-option"}), pod, volume.VolumeOptions{})
   646  	if err != nil {
   647  		t.Errorf("Failed to make a new Mounter: %v", err)
   648  	}
   649  	if mounter == nil {
   650  		t.Fatalf("Got a nil Mounter")
   651  	}
   652  
   653  	// Wrap with FakeMounter.
   654  	fakeMounter := mount.NewFakeMounter(nil)
   655  	mounter.(*localVolumeMounter).mounter = fakeMounter
   656  
   657  	if err := mounter.SetUp(volume.MounterArgs{}); err != nil {
   658  		t.Errorf("Expected success, got: %v", err)
   659  	}
   660  	mountOptions := fakeMounter.MountPoints[0].Opts
   661  	expectedMountOptions := []string{"bind", "test-option"}
   662  	if !reflect.DeepEqual(mountOptions, expectedMountOptions) {
   663  		t.Errorf("Expected mount options to be %v got %v", expectedMountOptions, mountOptions)
   664  	}
   665  }
   666  
   667  func TestPersistentClaimReadOnlyFlag(t *testing.T) {
   668  	tmpDir, plug := getPlugin(t)
   669  	defer os.RemoveAll(tmpDir)
   670  
   671  	// Read only == true
   672  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
   673  	mounter, err := plug.NewMounter(getTestVolume(true, tmpDir, false, nil), pod, volume.VolumeOptions{})
   674  	if err != nil {
   675  		t.Errorf("Failed to make a new Mounter: %v", err)
   676  	}
   677  	if mounter == nil {
   678  		t.Fatalf("Got a nil Mounter")
   679  	}
   680  	if !mounter.GetAttributes().ReadOnly {
   681  		t.Errorf("Expected true for mounter.IsReadOnly")
   682  	}
   683  
   684  	// Read only == false
   685  	mounter, err = plug.NewMounter(getTestVolume(false, tmpDir, false, nil), pod, volume.VolumeOptions{})
   686  	if err != nil {
   687  		t.Errorf("Failed to make a new Mounter: %v", err)
   688  	}
   689  	if mounter == nil {
   690  		t.Fatalf("Got a nil Mounter")
   691  	}
   692  	if mounter.GetAttributes().ReadOnly {
   693  		t.Errorf("Expected false for mounter.IsReadOnly")
   694  	}
   695  }
   696  
   697  func TestUnsupportedPlugins(t *testing.T) {
   698  	tmpDir, err := utiltesting.MkTmpdir("localVolumeTest")
   699  	if err != nil {
   700  		t.Fatalf("can't make a temp dir: %v", err)
   701  	}
   702  	defer os.RemoveAll(tmpDir)
   703  
   704  	plugMgr := volume.VolumePluginMgr{}
   705  	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil))
   706  	spec := getTestVolume(false, tmpDir, false, nil)
   707  
   708  	recyclePlug, err := plugMgr.FindRecyclablePluginBySpec(spec)
   709  	if err == nil && recyclePlug != nil {
   710  		t.Errorf("Recyclable plugin found, expected none")
   711  	}
   712  
   713  	deletePlug, err := plugMgr.FindDeletablePluginByName(localVolumePluginName)
   714  	if err == nil && deletePlug != nil {
   715  		t.Errorf("Deletable plugin found, expected none")
   716  	}
   717  
   718  	attachPlug, err := plugMgr.FindAttachablePluginByName(localVolumePluginName)
   719  	if err == nil && attachPlug != nil {
   720  		t.Errorf("Attachable plugin found, expected none")
   721  	}
   722  
   723  	createPlug, err := plugMgr.FindCreatablePluginBySpec(spec)
   724  	if err == nil && createPlug != nil {
   725  		t.Errorf("Creatable plugin found, expected none")
   726  	}
   727  
   728  	provisionPlug, err := plugMgr.FindProvisionablePluginByName(localVolumePluginName)
   729  	if err == nil && provisionPlug != nil {
   730  		t.Errorf("Provisionable plugin found, expected none")
   731  	}
   732  }
   733  
   734  func TestFilterPodMounts(t *testing.T) {
   735  	tmpDir, plug := getPlugin(t)
   736  	defer os.RemoveAll(tmpDir)
   737  
   738  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
   739  	mounter, err := plug.NewMounter(getTestVolume(false, tmpDir, false, nil), pod, volume.VolumeOptions{})
   740  	if err != nil {
   741  		t.Fatal(err)
   742  	}
   743  	lvMounter, ok := mounter.(*localVolumeMounter)
   744  	if !ok {
   745  		t.Fatal("mounter is not localVolumeMounter")
   746  	}
   747  
   748  	host := volumetest.NewFakeKubeletVolumeHost(t, tmpDir, nil, nil)
   749  	podsDir := host.GetPodsDir()
   750  
   751  	cases := map[string]struct {
   752  		input    []string
   753  		expected []string
   754  	}{
   755  		"empty": {
   756  			[]string{},
   757  			[]string{},
   758  		},
   759  		"not-pod-mount": {
   760  			[]string{"/mnt/outside"},
   761  			[]string{},
   762  		},
   763  		"pod-mount": {
   764  			[]string{filepath.Join(podsDir, "pod-mount")},
   765  			[]string{filepath.Join(podsDir, "pod-mount")},
   766  		},
   767  		"not-directory-prefix": {
   768  			[]string{podsDir + "pod-mount"},
   769  			[]string{},
   770  		},
   771  		"mix": {
   772  			[]string{"/mnt/outside",
   773  				filepath.Join(podsDir, "pod-mount"),
   774  				"/another/outside",
   775  				filepath.Join(podsDir, "pod-mount2")},
   776  			[]string{filepath.Join(podsDir, "pod-mount"),
   777  				filepath.Join(podsDir, "pod-mount2")},
   778  		},
   779  	}
   780  	for name, test := range cases {
   781  		output := lvMounter.filterPodMounts(test.input)
   782  		if !reflect.DeepEqual(output, test.expected) {
   783  			t.Errorf("%v failed: output %+v doesn't equal expected %+v", name, output, test.expected)
   784  		}
   785  	}
   786  }
   787  

View as plain text