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