k8s.io/kubernetes@v1.29.3/pkg/volume/vsphere_volume/attacher_test.go (about)

     1  //go:build !providerless
     2  // +build !providerless
     3  
     4  /*
     5  Copyright 2016 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 vsphere_volume
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"os"
    26  	"path/filepath"
    27  	"testing"
    28  
    29  	v1 "k8s.io/api/core/v1"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	"k8s.io/kubernetes/pkg/volume"
    32  	volumetest "k8s.io/kubernetes/pkg/volume/testing"
    33  	"k8s.io/legacy-cloud-providers/vsphere/vclib"
    34  
    35  	"k8s.io/klog/v2"
    36  )
    37  
    38  var (
    39  	// diskNameErr is the error when disk name is wrong.
    40  	diskNameErr = errors.New("wrong diskName")
    41  	// nodeNameErr is the error when node name is wrong.
    42  	nodeNameErr = errors.New("wrong nodeName")
    43  )
    44  
    45  func TestGetDeviceName_Volume(t *testing.T) {
    46  	plugin := newPlugin(t)
    47  	volPath := "[local] volumes/test"
    48  	spec := createVolSpec(volPath)
    49  
    50  	deviceName, err := plugin.GetVolumeName(spec)
    51  	if err != nil {
    52  		t.Errorf("GetDeviceName error: %v", err)
    53  	}
    54  	if deviceName != volPath {
    55  		t.Errorf("GetDeviceName error: expected %s, got %s", volPath, deviceName)
    56  	}
    57  }
    58  
    59  func TestGetDeviceName_PersistentVolume(t *testing.T) {
    60  	plugin := newPlugin(t)
    61  	volPath := "[local] volumes/test"
    62  	spec := createPVSpec(volPath)
    63  
    64  	deviceName, err := plugin.GetVolumeName(spec)
    65  	if err != nil {
    66  		t.Errorf("GetDeviceName error: %v", err)
    67  	}
    68  	if deviceName != volPath {
    69  		t.Errorf("GetDeviceName error: expected %s, got %s", volPath, deviceName)
    70  	}
    71  }
    72  
    73  // One testcase for TestAttachDetach table test below
    74  type testcase struct {
    75  	name string
    76  	// For fake vSphere:
    77  	attach         attachCall
    78  	detach         detachCall
    79  	diskIsAttached diskIsAttachedCall
    80  	t              *testing.T
    81  
    82  	// Actual test to run
    83  	test func(test *testcase) (string, error)
    84  	// Expected return of the test
    85  	expectedDevice string
    86  	expectedError  error
    87  }
    88  
    89  func TestAttachDetach(t *testing.T) {
    90  	uuid := "00000000000000"
    91  	diskName := "[local] volumes/test"
    92  	nodeName := types.NodeName("host")
    93  	spec := createVolSpec(diskName)
    94  	expectedDevice := filepath.FromSlash("/dev/disk/by-id/wwn-0x" + uuid)
    95  	attachError := errors.New("fake attach error")
    96  	detachError := errors.New("fake detach error")
    97  	diskCheckError := errors.New("fake DiskIsAttached error")
    98  	tests := []testcase{
    99  		// Successful Attach call
   100  		{
   101  			name:   "Attach_Positive",
   102  			attach: attachCall{diskName, nodeName, uuid, nil},
   103  			test: func(testcase *testcase) (string, error) {
   104  				attacher := newAttacher(testcase)
   105  				return attacher.Attach(spec, nodeName)
   106  			},
   107  			expectedDevice: expectedDevice,
   108  		},
   109  
   110  		// Attach call fails
   111  		{
   112  			name:   "Attach_Negative",
   113  			attach: attachCall{diskName, nodeName, "", attachError},
   114  			test: func(testcase *testcase) (string, error) {
   115  				attacher := newAttacher(testcase)
   116  				return attacher.Attach(spec, nodeName)
   117  			},
   118  			expectedError: attachError,
   119  		},
   120  
   121  		// Detach succeeds
   122  		{
   123  			name:           "Detach_Positive",
   124  			diskIsAttached: diskIsAttachedCall{diskName, nodeName, true, nil},
   125  			detach:         detachCall{diskName, nodeName, nil},
   126  			test: func(testcase *testcase) (string, error) {
   127  				detacher := newDetacher(testcase)
   128  				return "", detacher.Detach(diskName, nodeName)
   129  			},
   130  		},
   131  
   132  		// Disk is already detached
   133  		{
   134  			name:           "Detach_Positive_AlreadyDetached",
   135  			diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, nil},
   136  			test: func(testcase *testcase) (string, error) {
   137  				detacher := newDetacher(testcase)
   138  				return "", detacher.Detach(diskName, nodeName)
   139  			},
   140  		},
   141  
   142  		// Detach succeeds when DiskIsAttached fails
   143  		{
   144  			name:           "Detach_Positive_CheckFails",
   145  			diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
   146  			detach:         detachCall{diskName, nodeName, nil},
   147  			test: func(testcase *testcase) (string, error) {
   148  				detacher := newDetacher(testcase)
   149  				return "", detacher.Detach(diskName, nodeName)
   150  			},
   151  		},
   152  
   153  		// Detach fails
   154  		{
   155  			name:           "Detach_Negative",
   156  			diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
   157  			detach:         detachCall{diskName, nodeName, detachError},
   158  			test: func(testcase *testcase) (string, error) {
   159  				detacher := newDetacher(testcase)
   160  				return "", detacher.Detach(diskName, nodeName)
   161  			},
   162  			expectedError: detachError,
   163  		},
   164  	}
   165  
   166  	for _, testcase := range tests {
   167  		testcase.t = t
   168  		device, err := testcase.test(&testcase)
   169  		if err != testcase.expectedError {
   170  			t.Errorf("%s failed: expected err=%q, got %q", testcase.name, testcase.expectedError.Error(), err.Error())
   171  		}
   172  		if device != testcase.expectedDevice {
   173  			t.Errorf("%s failed: expected device=%q, got %q", testcase.name, testcase.expectedDevice, device)
   174  		}
   175  		t.Logf("Test %q succeeded", testcase.name)
   176  	}
   177  }
   178  
   179  // newPlugin creates a new vsphereVolumePlugin with fake cloud, NewAttacher
   180  // and NewDetacher won't work.
   181  func newPlugin(t *testing.T) *vsphereVolumePlugin {
   182  	host := volumetest.NewFakeVolumeHost(t, os.TempDir(), nil, nil)
   183  	plugins := ProbeVolumePlugins()
   184  	plugin := plugins[0]
   185  	plugin.Init(host)
   186  	return plugin.(*vsphereVolumePlugin)
   187  }
   188  
   189  func newAttacher(testcase *testcase) *vsphereVMDKAttacher {
   190  	return &vsphereVMDKAttacher{
   191  		host:           nil,
   192  		vsphereVolumes: testcase,
   193  	}
   194  }
   195  
   196  func newDetacher(testcase *testcase) *vsphereVMDKDetacher {
   197  	return &vsphereVMDKDetacher{
   198  		vsphereVolumes: testcase,
   199  	}
   200  }
   201  
   202  func createVolSpec(name string) *volume.Spec {
   203  	return &volume.Spec{
   204  		Volume: &v1.Volume{
   205  			VolumeSource: v1.VolumeSource{
   206  				VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
   207  					VolumePath: name,
   208  				},
   209  			},
   210  		},
   211  	}
   212  }
   213  
   214  func createPVSpec(name string) *volume.Spec {
   215  	return &volume.Spec{
   216  		PersistentVolume: &v1.PersistentVolume{
   217  			Spec: v1.PersistentVolumeSpec{
   218  				PersistentVolumeSource: v1.PersistentVolumeSource{
   219  					VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
   220  						VolumePath: name,
   221  					},
   222  				},
   223  			},
   224  		},
   225  	}
   226  }
   227  
   228  // Fake vSphere implementation
   229  
   230  type attachCall struct {
   231  	diskName      string
   232  	nodeName      types.NodeName
   233  	retDeviceUUID string
   234  	ret           error
   235  }
   236  
   237  type detachCall struct {
   238  	diskName string
   239  	nodeName types.NodeName
   240  	ret      error
   241  }
   242  
   243  type diskIsAttachedCall struct {
   244  	diskName   string
   245  	nodeName   types.NodeName
   246  	isAttached bool
   247  	ret        error
   248  }
   249  
   250  func (testcase *testcase) AttachDisk(diskName string, storagePolicyName string, nodeName types.NodeName) (string, error) {
   251  	expected := &testcase.attach
   252  
   253  	if expected.diskName == "" && expected.nodeName == "" {
   254  		// testcase.attach looks uninitialized, test did not expect to call
   255  		// AttachDisk
   256  		testcase.t.Errorf("Unexpected AttachDisk call!")
   257  		return "", errors.New("unexpected AttachDisk call")
   258  	}
   259  
   260  	if expected.diskName != diskName {
   261  		testcase.t.Errorf("Unexpected AttachDisk call: expected diskName %s, got %s", expected.diskName, diskName)
   262  		return "", fmt.Errorf(`unexpected AttachDisk call: %w`, diskNameErr)
   263  	}
   264  
   265  	if expected.nodeName != nodeName {
   266  		testcase.t.Errorf("Unexpected AttachDisk call: expected nodeName %s, got %s", expected.nodeName, nodeName)
   267  		return "", fmt.Errorf(`unexpected AttachDisk call: %w`, nodeNameErr)
   268  	}
   269  
   270  	klog.V(4).Infof("AttachDisk call: %s, %s, returning %q, %v", diskName, nodeName, expected.retDeviceUUID, expected.ret)
   271  
   272  	return expected.retDeviceUUID, expected.ret
   273  }
   274  
   275  func (testcase *testcase) DetachDisk(diskName string, nodeName types.NodeName) error {
   276  	expected := &testcase.detach
   277  
   278  	if expected.diskName == "" && expected.nodeName == "" {
   279  		// testcase.detach looks uninitialized, test did not expect to call
   280  		// DetachDisk
   281  		testcase.t.Errorf("Unexpected DetachDisk call!")
   282  		return errors.New("unexpected DetachDisk call")
   283  	}
   284  
   285  	if expected.diskName != diskName {
   286  		testcase.t.Errorf("Unexpected DetachDisk call: expected diskName %s, got %s", expected.diskName, diskName)
   287  		return fmt.Errorf(`unexpected DetachDisk call: %w`, diskNameErr)
   288  	}
   289  
   290  	if expected.nodeName != nodeName {
   291  		testcase.t.Errorf("Unexpected DetachDisk call: expected nodeName %s, got %s", expected.nodeName, nodeName)
   292  		return fmt.Errorf(`unexpected DetachDisk call: %w`, nodeNameErr)
   293  	}
   294  
   295  	klog.V(4).Infof("DetachDisk call: %s, %s, returning %v", diskName, nodeName, expected.ret)
   296  
   297  	return expected.ret
   298  }
   299  
   300  func (testcase *testcase) DiskIsAttached(diskName string, nodeName types.NodeName) (bool, string, error) {
   301  	expected := &testcase.diskIsAttached
   302  
   303  	if expected.diskName == "" && expected.nodeName == "" {
   304  		// testcase.diskIsAttached looks uninitialized, test did not expect to
   305  		// call DiskIsAttached
   306  		testcase.t.Errorf("Unexpected DiskIsAttached call!")
   307  		return false, diskName, errors.New("unexpected DiskIsAttached call")
   308  	}
   309  
   310  	if expected.diskName != diskName {
   311  		testcase.t.Errorf("Unexpected DiskIsAttached call: expected diskName %s, got %s", expected.diskName, diskName)
   312  		return false, diskName, fmt.Errorf(`unexpected DiskIsAttached call: %w`, diskNameErr)
   313  	}
   314  
   315  	if expected.nodeName != nodeName {
   316  		testcase.t.Errorf("Unexpected DiskIsAttached call: expected nodeName %s, got %s", expected.nodeName, nodeName)
   317  		return false, diskName, fmt.Errorf(`unexpected DiskIsAttached call: %w`, nodeNameErr)
   318  	}
   319  
   320  	klog.V(4).Infof("DiskIsAttached call: %s, %s, returning %v, %v", diskName, nodeName, expected.isAttached, expected.ret)
   321  
   322  	return expected.isAttached, diskName, expected.ret
   323  }
   324  
   325  func (testcase *testcase) DisksAreAttached(nodeVolumes map[types.NodeName][]string) (map[types.NodeName]map[string]bool, error) {
   326  	return nil, errors.New("not implemented")
   327  }
   328  
   329  func (testcase *testcase) CreateVolume(volumeOptions *vclib.VolumeOptions) (volumePath string, err error) {
   330  	return "", errors.New("not implemented")
   331  }
   332  
   333  func (testcase *testcase) DeleteVolume(vmDiskPath string) error {
   334  	return errors.New("not implemented")
   335  }