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  }