github.com/openshift/installer@v1.4.17/pkg/destroy/vsphere/vsphere_test.go (about) 1 package vsphere 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 "testing" 9 10 "github.com/golang/mock/gomock" 11 "github.com/sirupsen/logrus" 12 "github.com/stretchr/testify/assert" 13 "github.com/vmware/govmomi/vim25/mo" 14 vspheretypes "github.com/vmware/govmomi/vim25/types" 15 16 "github.com/openshift/installer/pkg/destroy/vsphere/mock" 17 "github.com/openshift/installer/pkg/types" 18 "github.com/openshift/installer/pkg/types/vsphere" 19 ) 20 21 type editMetadataFuncs []func(m *types.ClusterMetadata) 22 23 type testCase struct { 24 name string 25 editFuncs editMetadataFuncs 26 errorMsg string 27 } 28 29 const ( 30 infraID = "infra-id" 31 listFailsID = "list-fails-infra-id" 32 deleteFailsID = "delete-fails-infra-id" 33 stopFailsID = "stop-VM-fails-infra-id" 34 ) 35 36 var ( 37 nullLogger = func() logrus.FieldLogger { 38 logger := logrus.StandardLogger() 39 logger.SetOutput(io.Discard) 40 return logger 41 }() 42 runningVM = func() mo.VirtualMachine { 43 vm := mo.VirtualMachine{} 44 vm.Name = "runningVM" 45 vm.Summary.Runtime.PowerState = "poweredOn" 46 return vm 47 }() 48 stoppedVM = func() mo.VirtualMachine { 49 vm := mo.VirtualMachine{} 50 vm.Name = "stoppedVM" 51 vm.Summary.Runtime.PowerState = "poweredOff" 52 return vm 53 }() 54 failVM = func() mo.VirtualMachine { 55 vm := mo.VirtualMachine{} 56 vm.Name = "failVM" 57 vm.Summary.Runtime.PowerState = "unknown" 58 return vm 59 }() 60 ) 61 62 func newDefaultMetadata() types.ClusterMetadata { 63 metadata := types.ClusterMetadata{ 64 ClusterName: "cluster-name", 65 ClusterID: "cluster-id", 66 InfraID: infraID, 67 } 68 metadata.VSphere = &vsphere.Metadata{ 69 VCenter: "vCenter", 70 Username: "username", 71 Password: "password", 72 TerraformPlatform: "vsphere", 73 } 74 return metadata 75 } 76 77 func TestVsphereDeleteFolder(t *testing.T) { 78 mockCtrl := gomock.NewController(t) 79 vsphereClient := mock.NewMockAPI(mockCtrl) 80 81 const ( 82 oneFolderID = "folder-infra-id" 83 manyFoldersID = "folders-infra-id" 84 manyChildrenID = "children-infra-id" 85 ) 86 87 validFolder := mo.Folder{} 88 validFolder.Name = "valid-folder" 89 childrenFolder := mo.Folder{} 90 childrenFolder.Name = "invalid-folder" 91 childrenFolder.ChildEntity = []vspheretypes.ManagedObjectReference{ 92 {Type: "child-type", Value: "child-value"}, 93 {Type: "child-type", Value: "child-value"}, 94 } 95 failFolder := mo.Folder{} 96 failFolder.Name = "fail-folder" 97 98 manyFoldersPerTag := func(m *types.ClusterMetadata) { 99 m.InfraID = manyFoldersID 100 } 101 oneFolderPerTag := func(m *types.ClusterMetadata) { 102 m.InfraID = oneFolderID 103 } 104 manyChildrenFolder := func(m *types.ClusterMetadata) { 105 m.InfraID = manyChildrenID 106 } 107 listFails := func(m *types.ClusterMetadata) { 108 m.InfraID = listFailsID 109 } 110 deleteFails := func(m *types.ClusterMetadata) { 111 m.InfraID = deleteFailsID 112 } 113 114 cases := []testCase{ 115 { 116 name: "Delete folder when no folder present", 117 editFuncs: editMetadataFuncs{}, 118 errorMsg: "", 119 }, 120 { 121 name: "Delete empty folder", 122 editFuncs: editMetadataFuncs{oneFolderPerTag}, 123 errorMsg: "", 124 }, 125 { 126 name: "Delete non-empty folder fails", 127 editFuncs: editMetadataFuncs{manyChildrenFolder}, 128 errorMsg: "Expected Folder .* to be empty", 129 }, 130 { 131 name: "Delete folder fails when listing", 132 editFuncs: editMetadataFuncs{listFails}, 133 errorMsg: "list attached objects", 134 }, 135 { 136 name: "Delete folders Zoning Terraform", 137 editFuncs: editMetadataFuncs{manyFoldersPerTag}, 138 errorMsg: "", 139 }, 140 { 141 name: "Delete folder fails", 142 editFuncs: editMetadataFuncs{deleteFails}, 143 errorMsg: "some vsphere error", 144 }, 145 } 146 147 vsphereClient. 148 EXPECT(). 149 ListFolders(gomock.Any(), gomock.Eq(infraID)). 150 Return([]mo.Folder{}, nil). 151 AnyTimes() 152 vsphereClient. 153 EXPECT(). 154 ListFolders(gomock.Any(), gomock.Eq(oneFolderID)). 155 Return([]mo.Folder{validFolder}, nil). 156 AnyTimes() 157 vsphereClient. 158 EXPECT(). 159 ListFolders(gomock.Any(), gomock.Eq(manyFoldersID)). 160 Return([]mo.Folder{validFolder, validFolder}, nil). 161 AnyTimes() 162 vsphereClient. 163 EXPECT(). 164 ListFolders(gomock.Any(), gomock.Eq(manyChildrenID)). 165 Return([]mo.Folder{validFolder, childrenFolder, failFolder}, nil). 166 AnyTimes() 167 vsphereClient. 168 EXPECT(). 169 ListFolders(gomock.Any(), gomock.Eq(deleteFailsID)). 170 Return([]mo.Folder{validFolder, failFolder, validFolder, childrenFolder}, nil). 171 AnyTimes() 172 vsphereClient. 173 EXPECT(). 174 ListFolders(gomock.Any(), gomock.Any()). 175 Return(nil, errors.New("list attached objects infra-id: vsphere error")). 176 AnyTimes() 177 178 vsphereClient. 179 EXPECT(). 180 DeleteFolder(gomock.Any(), gomock.Eq(validFolder)). 181 Return(nil). 182 AnyTimes() 183 vsphereClient. 184 EXPECT(). 185 DeleteFolder(gomock.Any(), gomock.Eq(childrenFolder)). 186 Times(0) // Should not delete a folder with children 187 vsphereClient. 188 EXPECT(). 189 DeleteFolder(gomock.Any(), gomock.Any()). 190 Return(errors.New("some vsphere error deleting Folder")). 191 AnyTimes() 192 193 for _, tc := range cases { 194 t.Run(tc.name, func(t *testing.T) { 195 editedMetadata := newDefaultMetadata() 196 for _, edit := range tc.editFuncs { 197 edit(&editedMetadata) 198 } 199 uninstaller := newWithClient(nullLogger, &editedMetadata, []API{vsphereClient}) 200 assert.NotNil(t, uninstaller) 201 err := uninstaller.deleteFolder(context.TODO()) 202 if tc.errorMsg != "" { 203 assert.Regexp(t, tc.errorMsg, err) 204 } else { 205 assert.NoError(t, err) 206 } 207 }) 208 } 209 } 210 211 func TestVsphereStopVirtualMachines(t *testing.T) { 212 mockCtrl := gomock.NewController(t) 213 vsphereClient := mock.NewMockAPI(mockCtrl) 214 215 const ( 216 stoppedVMsID = "stopped-VM-infra-id" 217 runningVMsID = "running-VM-infra-id" 218 mixedVMsID = "mixed-VM-infra-id" 219 ) 220 221 stoppedVMs := func(m *types.ClusterMetadata) { 222 m.InfraID = stoppedVMsID 223 } 224 runningVMs := func(m *types.ClusterMetadata) { 225 m.InfraID = runningVMsID 226 } 227 mixedVMs := func(m *types.ClusterMetadata) { 228 m.InfraID = mixedVMsID 229 } 230 listFails := func(m *types.ClusterMetadata) { 231 m.InfraID = listFailsID 232 } 233 stopFails := func(m *types.ClusterMetadata) { 234 m.InfraID = stopFailsID 235 } 236 237 cases := []testCase{ 238 { 239 name: "Stop VMs when no VM present", 240 editFuncs: editMetadataFuncs{}, 241 errorMsg: "", 242 }, 243 { 244 name: "Stop VMs when none running", 245 editFuncs: editMetadataFuncs{stoppedVMs}, 246 errorMsg: "", 247 }, 248 { 249 name: "Stop VMs when all running", 250 editFuncs: editMetadataFuncs{runningVMs}, 251 errorMsg: "", 252 }, 253 { 254 name: "Stop VMs when some running", 255 editFuncs: editMetadataFuncs{mixedVMs}, 256 errorMsg: "", 257 }, 258 { 259 name: "Stop VMs fails when listing fails", 260 editFuncs: editMetadataFuncs{listFails}, 261 errorMsg: "some vsphere error", 262 }, 263 { 264 name: "Stop VMs fails", 265 editFuncs: editMetadataFuncs{stopFails}, 266 errorMsg: "some vsphere error", 267 }, 268 } 269 270 vsphereClient. 271 EXPECT(). 272 ListVirtualMachines(gomock.Any(), gomock.Eq(infraID)). 273 Return([]mo.VirtualMachine{}, nil). 274 AnyTimes() 275 vsphereClient. 276 EXPECT(). 277 ListVirtualMachines(gomock.Any(), gomock.Eq(stoppedVMsID)). 278 Return([]mo.VirtualMachine{stoppedVM, stoppedVM}, nil). 279 AnyTimes() 280 vsphereClient. 281 EXPECT(). 282 ListVirtualMachines(gomock.Any(), gomock.Eq(runningVMsID)). 283 Return([]mo.VirtualMachine{runningVM, runningVM}, nil). 284 AnyTimes() 285 vsphereClient. 286 EXPECT(). 287 ListVirtualMachines(gomock.Any(), gomock.Eq(mixedVMsID)). 288 Return([]mo.VirtualMachine{runningVM, runningVM, stoppedVM}, nil). 289 AnyTimes() 290 vsphereClient. 291 EXPECT(). 292 ListVirtualMachines(gomock.Any(), gomock.Eq(stopFailsID)). 293 Return([]mo.VirtualMachine{runningVM, failVM, stoppedVM, failVM}, nil). 294 AnyTimes() 295 vsphereClient. 296 EXPECT(). 297 ListVirtualMachines(gomock.Any(), gomock.Eq(listFailsID)). 298 Return(nil, errors.New("some vsphere error listing VMs")). 299 AnyTimes() 300 301 vsphereClient. 302 EXPECT(). 303 StopVirtualMachine(gomock.Any(), gomock.Eq(runningVM)). 304 Return(nil). 305 AnyTimes() 306 vsphereClient. 307 EXPECT(). 308 StopVirtualMachine(gomock.Any(), gomock.Eq(stoppedVM)). 309 Times(0) // Should not try to stop a VM that is not running 310 vsphereClient. 311 EXPECT(). 312 StopVirtualMachine(gomock.Any(), gomock.Eq(failVM)). 313 Return(errors.New("some vsphere error stopping VM")). 314 AnyTimes() 315 vsphereClient. 316 EXPECT(). 317 StopVirtualMachine(gomock.Any(), gomock.Any()). 318 Times(0) 319 320 for _, tc := range cases { 321 t.Run(tc.name, func(t *testing.T) { 322 editedMetadata := newDefaultMetadata() 323 for _, edit := range tc.editFuncs { 324 edit(&editedMetadata) 325 } 326 uninstaller := newWithClient(nullLogger, &editedMetadata, []API{vsphereClient}) 327 assert.NotNil(t, uninstaller) 328 err := uninstaller.stopVirtualMachines(context.TODO()) 329 if tc.errorMsg != "" { 330 assert.Regexp(t, tc.errorMsg, err) 331 } else { 332 assert.NoError(t, err) 333 } 334 }) 335 } 336 } 337 338 func TestVsphereDeleteVirtualMachines(t *testing.T) { 339 mockCtrl := gomock.NewController(t) 340 vsphereClient := mock.NewMockAPI(mockCtrl) 341 342 const someVMsID = "some-VM-infra-id" 343 344 someVMs := func(m *types.ClusterMetadata) { 345 m.InfraID = someVMsID 346 } 347 listFails := func(m *types.ClusterMetadata) { 348 m.InfraID = listFailsID 349 } 350 deleteFails := func(m *types.ClusterMetadata) { 351 m.InfraID = deleteFailsID 352 } 353 354 cases := []testCase{ 355 { 356 name: "Delete VMs when none present", 357 editFuncs: editMetadataFuncs{}, 358 errorMsg: "", 359 }, 360 { 361 name: "Delete VMs when some present", 362 editFuncs: editMetadataFuncs{someVMs}, 363 errorMsg: "", 364 }, 365 { 366 name: "Delete VMs fails when listing fails", 367 editFuncs: editMetadataFuncs{listFails}, 368 errorMsg: "some vsphere error", 369 }, 370 { 371 name: "Delete VMs fails when some fail", 372 editFuncs: editMetadataFuncs{deleteFails}, 373 errorMsg: "some vsphere error", 374 }, 375 } 376 377 vsphereClient. 378 EXPECT(). 379 ListVirtualMachines(gomock.Any(), gomock.Eq(infraID)). 380 Return([]mo.VirtualMachine{}, nil). 381 AnyTimes() 382 vsphereClient. 383 EXPECT(). 384 ListVirtualMachines(gomock.Any(), gomock.Eq(someVMsID)). 385 Return([]mo.VirtualMachine{stoppedVM, stoppedVM}, nil). 386 AnyTimes() 387 vsphereClient. 388 EXPECT(). 389 ListVirtualMachines(gomock.Any(), gomock.Eq(deleteFailsID)). 390 Return([]mo.VirtualMachine{stoppedVM, failVM, stoppedVM, failVM}, nil). 391 AnyTimes() 392 vsphereClient. 393 EXPECT(). 394 ListVirtualMachines(gomock.Any(), gomock.Eq(listFailsID)). 395 Return(nil, errors.New("some vsphere error listing VMs")). 396 AnyTimes() 397 398 vsphereClient. 399 EXPECT(). 400 DeleteVirtualMachine(gomock.Any(), gomock.Eq(runningVM)). 401 Times(0) // Not import but should not happen 402 vsphereClient. 403 EXPECT(). 404 DeleteVirtualMachine(gomock.Any(), gomock.Eq(stoppedVM)). 405 AnyTimes() 406 vsphereClient. 407 EXPECT(). 408 DeleteVirtualMachine(gomock.Any(), gomock.Eq(failVM)). 409 Return(errors.New("some vsphere error deleting VM")). 410 AnyTimes() 411 vsphereClient. 412 EXPECT(). 413 StopVirtualMachine(gomock.Any(), gomock.Any()). 414 Times(0) 415 416 for _, tc := range cases { 417 t.Run(tc.name, func(t *testing.T) { 418 editedMetadata := newDefaultMetadata() 419 for _, edit := range tc.editFuncs { 420 edit(&editedMetadata) 421 } 422 uninstaller := newWithClient(nullLogger, &editedMetadata, []API{vsphereClient}) 423 assert.NotNil(t, uninstaller) 424 err := uninstaller.deleteVirtualMachines(context.TODO()) 425 if tc.errorMsg != "" { 426 assert.Regexp(t, tc.errorMsg, err) 427 } else { 428 assert.NoError(t, err) 429 } 430 }) 431 } 432 } 433 434 func TestDeleteStoragePolicy(t *testing.T) { 435 mockCtrl := gomock.NewController(t) 436 vsphereClient := mock.NewMockAPI(mockCtrl) 437 438 policyFails := fmt.Sprintf("openshift-storage-policy-%s", deleteFailsID) 439 440 deleteFails := func(m *types.ClusterMetadata) { 441 m.InfraID = deleteFailsID 442 } 443 444 cases := []testCase{ 445 { 446 name: "Delete Storage Policy succeeds", 447 editFuncs: editMetadataFuncs{}, 448 errorMsg: "", 449 }, 450 { 451 name: "Delete Storage Policy fails", 452 editFuncs: editMetadataFuncs{deleteFails}, 453 errorMsg: "some vsphere error", 454 }, 455 } 456 457 vsphereClient. 458 EXPECT(). 459 DeleteStoragePolicy(gomock.Any(), gomock.Eq(policyFails)). 460 Return(errors.New("some vsphere error deleting Storage Policy")). 461 AnyTimes() 462 vsphereClient. 463 EXPECT(). 464 DeleteStoragePolicy(gomock.Any(), gomock.Any()). 465 Return(nil). 466 AnyTimes() 467 468 for _, tc := range cases { 469 t.Run(tc.name, func(t *testing.T) { 470 editedMetadata := newDefaultMetadata() 471 for _, edit := range tc.editFuncs { 472 edit(&editedMetadata) 473 } 474 uninstaller := newWithClient(nullLogger, &editedMetadata, []API{vsphereClient}) 475 assert.NotNil(t, uninstaller) 476 err := uninstaller.deleteStoragePolicy(context.TODO()) 477 if tc.errorMsg != "" { 478 assert.Regexp(t, tc.errorMsg, err) 479 } else { 480 assert.NoError(t, err) 481 } 482 }) 483 } 484 } 485 486 func TestDeleteTag(t *testing.T) { 487 mockCtrl := gomock.NewController(t) 488 vsphereClient := mock.NewMockAPI(mockCtrl) 489 490 deleteFails := func(m *types.ClusterMetadata) { 491 m.InfraID = deleteFailsID 492 } 493 494 cases := []testCase{ 495 { 496 name: "Delete Tag succeeds", 497 editFuncs: editMetadataFuncs{}, 498 errorMsg: "", 499 }, 500 { 501 name: "Delete Tag fails", 502 editFuncs: editMetadataFuncs{deleteFails}, 503 errorMsg: "some vsphere error", 504 }, 505 } 506 507 vsphereClient. 508 EXPECT(). 509 DeleteTag(gomock.Any(), gomock.Eq(deleteFailsID)). 510 Return(errors.New("some vsphere error deleting Tag")). 511 AnyTimes() 512 vsphereClient. 513 EXPECT(). 514 DeleteTag(gomock.Any(), gomock.Any()). 515 Return(nil). 516 AnyTimes() 517 518 for _, tc := range cases { 519 t.Run(tc.name, func(t *testing.T) { 520 editedMetadata := newDefaultMetadata() 521 for _, edit := range tc.editFuncs { 522 edit(&editedMetadata) 523 } 524 uninstaller := newWithClient(nullLogger, &editedMetadata, []API{vsphereClient}) 525 assert.NotNil(t, uninstaller) 526 err := uninstaller.deleteTag(context.TODO()) 527 if tc.errorMsg != "" { 528 assert.Regexp(t, tc.errorMsg, err) 529 } else { 530 assert.NoError(t, err) 531 } 532 }) 533 } 534 } 535 536 func TestDeleteTagCategory(t *testing.T) { 537 mockCtrl := gomock.NewController(t) 538 vsphereClient := mock.NewMockAPI(mockCtrl) 539 540 tagFails := fmt.Sprintf("openshift-%s", deleteFailsID) 541 542 deleteFails := func(m *types.ClusterMetadata) { 543 m.InfraID = deleteFailsID 544 } 545 546 cases := []testCase{ 547 { 548 name: "Delete Tag Category succeeds", 549 editFuncs: editMetadataFuncs{}, 550 errorMsg: "", 551 }, 552 { 553 name: "Delete Tag Category fails", 554 editFuncs: editMetadataFuncs{deleteFails}, 555 errorMsg: "some vsphere error", 556 }, 557 } 558 559 vsphereClient. 560 EXPECT(). 561 DeleteTagCategory(gomock.Any(), gomock.Eq(tagFails)). 562 Return(errors.New("some vsphere error deleting Tag Category")). 563 AnyTimes() 564 vsphereClient. 565 EXPECT(). 566 DeleteTagCategory(gomock.Any(), gomock.Any()). 567 Return(nil). 568 AnyTimes() 569 570 for _, tc := range cases { 571 t.Run(tc.name, func(t *testing.T) { 572 editedMetadata := newDefaultMetadata() 573 for _, edit := range tc.editFuncs { 574 edit(&editedMetadata) 575 } 576 uninstaller := newWithClient(nullLogger, &editedMetadata, []API{vsphereClient}) 577 assert.NotNil(t, uninstaller) 578 err := uninstaller.deleteTagCategory(context.TODO()) 579 if tc.errorMsg != "" { 580 assert.Regexp(t, tc.errorMsg, err) 581 } else { 582 assert.NoError(t, err) 583 } 584 }) 585 } 586 }