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 }