diff --git a/.codecov.yml b/.codecov.yml index c06f98c05..7a5e9710a 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -13,5 +13,6 @@ coverage: project: off patch: off -ignore: # ignore hcl2spec generated code for coverage - - "**/*.hcl2spec.go" \ No newline at end of file +ignore: # ignore hcl2spec generated code for coverage and mocks + - "**/*.hcl2spec.go" + - "**/*_mock.go" \ No newline at end of file diff --git a/builder/vsphere/common/step_add_floppy.go b/builder/vsphere/common/step_add_floppy.go index ab99e2754..213f08429 100644 --- a/builder/vsphere/common/step_add_floppy.go +++ b/builder/vsphere/common/step_add_floppy.go @@ -37,8 +37,8 @@ type StepAddFloppy struct { func (s *StepAddFloppy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - vm := state.Get("vm").(*driver.VirtualMachineDriver) - d := state.Get("driver").(*driver.VCenterDriver) + vm := state.Get("vm").(driver.VirtualMachine) + d := state.Get("driver").(driver.Driver) if floppyPath, ok := state.GetOk("floppy_path"); ok { ui.Say("Uploading created floppy image") @@ -90,7 +90,7 @@ func (s *StepAddFloppy) Cleanup(state multistep.StateBag) { } ui := state.Get("ui").(packer.Ui) - d := state.Get("driver").(*driver.VCenterDriver) + d := state.Get("driver").(driver.Driver) if UploadedFloppyPath, ok := state.GetOk("uploaded_floppy_path"); ok { ui.Say("Deleting Floppy image ...") diff --git a/builder/vsphere/common/step_add_floppy_test.go b/builder/vsphere/common/step_add_floppy_test.go new file mode 100644 index 000000000..52963526a --- /dev/null +++ b/builder/vsphere/common/step_add_floppy_test.go @@ -0,0 +1,453 @@ +package common + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/packer/builder/vsphere/driver" + "github.com/hashicorp/packer/helper/multistep" +) + +func TestStepAddFloppy_Run(t *testing.T) { + tc := []struct { + name string + floppyPath string + uploadedPath string + step *StepAddFloppy + expectedAction multistep.StepAction + vmMock *driver.VirtualMachineMock + expectedVmMock *driver.VirtualMachineMock + driverMock *driver.DriverMock + expectedDriverMock *driver.DriverMock + dsMock *driver.DatastoreMock + expectedDsMock *driver.DatastoreMock + fail bool + errMessage string + }{ + { + name: "Add floppy from state floppy path", + floppyPath: "floppy/path", + uploadedPath: "vm/dir/packer-tmp-created-floppy.flp", + step: &StepAddFloppy{ + Config: new(FloppyConfig), + Datastore: "datastore", + Host: "host", + SetHostForDatastoreUploads: true, + }, + expectedAction: multistep.ActionContinue, + vmMock: &driver.VirtualMachineMock{ + GetDirResponse: "vm/dir", + }, + expectedVmMock: &driver.VirtualMachineMock{ + GetDirResponse: "vm/dir", + GetDirCalled: true, + AddFloppyCalled: true, + AddFloppyImagePath: "resolved/path", + }, + driverMock: new(driver.DriverMock), + expectedDriverMock: &driver.DriverMock{ + FindDatastoreCalled: true, + FindDatastoreName: "datastore", + FindDatastoreHost: "host", + }, + dsMock: &driver.DatastoreMock{ + ResolvePathReturn: "resolved/path", + }, + expectedDsMock: &driver.DatastoreMock{ + UploadFileCalled: true, + UploadFileSrc: "floppy/path", + UploadFileDst: "vm/dir/packer-tmp-created-floppy.flp", + UploadFileHost: "host", + UploadFileSetHost: true, + ResolvePathCalled: true, + ResolvePathReturn: "resolved/path", + }, + fail: false, + }, + { + name: "State floppy path - find datastore fail", + floppyPath: "floppy/path", + step: &StepAddFloppy{ + Config: new(FloppyConfig), + Datastore: "datastore", + Host: "host", + SetHostForDatastoreUploads: true, + }, + expectedAction: multistep.ActionHalt, + vmMock: new(driver.VirtualMachineMock), + expectedVmMock: new(driver.VirtualMachineMock), + driverMock: &driver.DriverMock{ + FindDatastoreErr: fmt.Errorf("error finding datastore"), + }, + expectedDriverMock: &driver.DriverMock{ + FindDatastoreCalled: true, + FindDatastoreName: "datastore", + FindDatastoreHost: "host", + }, + dsMock: new(driver.DatastoreMock), + expectedDsMock: new(driver.DatastoreMock), + fail: true, + errMessage: "error finding datastore", + }, + { + name: "State floppy path - vm get dir fail", + floppyPath: "floppy/path", + step: &StepAddFloppy{ + Config: new(FloppyConfig), + Datastore: "datastore", + Host: "host", + SetHostForDatastoreUploads: true, + }, + expectedAction: multistep.ActionHalt, + vmMock: &driver.VirtualMachineMock{ + GetDirErr: fmt.Errorf("fail to get vm dir"), + }, + expectedVmMock: &driver.VirtualMachineMock{ + GetDirCalled: true, + }, + driverMock: new(driver.DriverMock), + expectedDriverMock: &driver.DriverMock{ + FindDatastoreCalled: true, + FindDatastoreName: "datastore", + FindDatastoreHost: "host", + }, + dsMock: new(driver.DatastoreMock), + expectedDsMock: new(driver.DatastoreMock), + fail: true, + errMessage: "fail to get vm dir", + }, + { + name: "State floppy path - datastore upload file fail", + floppyPath: "floppy/path", + step: &StepAddFloppy{ + Config: new(FloppyConfig), + Datastore: "datastore", + Host: "host", + SetHostForDatastoreUploads: true, + }, + expectedAction: multistep.ActionHalt, + vmMock: &driver.VirtualMachineMock{ + GetDirResponse: "vm/dir", + }, + expectedVmMock: &driver.VirtualMachineMock{ + GetDirResponse: "vm/dir", + GetDirCalled: true, + }, + driverMock: new(driver.DriverMock), + expectedDriverMock: &driver.DriverMock{ + FindDatastoreCalled: true, + FindDatastoreName: "datastore", + FindDatastoreHost: "host", + }, + dsMock: &driver.DatastoreMock{ + UploadFileErr: fmt.Errorf("failed to upload file"), + }, + expectedDsMock: &driver.DatastoreMock{ + UploadFileCalled: true, + UploadFileSrc: "floppy/path", + UploadFileDst: "vm/dir/packer-tmp-created-floppy.flp", + UploadFileHost: "host", + UploadFileSetHost: true, + }, + fail: true, + errMessage: "failed to upload file", + }, + { + name: "State floppy path - vm fail to add floppy", + floppyPath: "floppy/path", + uploadedPath: "vm/dir/packer-tmp-created-floppy.flp", + step: &StepAddFloppy{ + Config: new(FloppyConfig), + Datastore: "datastore", + Host: "host", + SetHostForDatastoreUploads: true, + }, + expectedAction: multistep.ActionHalt, + vmMock: &driver.VirtualMachineMock{ + GetDirResponse: "vm/dir", + AddFloppyErr: fmt.Errorf("failed to add floppy"), + }, + expectedVmMock: &driver.VirtualMachineMock{ + GetDirResponse: "vm/dir", + GetDirCalled: true, + AddFloppyCalled: true, + AddFloppyImagePath: "resolved/path", + }, + driverMock: new(driver.DriverMock), + expectedDriverMock: &driver.DriverMock{ + FindDatastoreCalled: true, + FindDatastoreName: "datastore", + FindDatastoreHost: "host", + }, + dsMock: &driver.DatastoreMock{ + ResolvePathReturn: "resolved/path", + }, + expectedDsMock: &driver.DatastoreMock{ + UploadFileCalled: true, + UploadFileSrc: "floppy/path", + UploadFileDst: "vm/dir/packer-tmp-created-floppy.flp", + UploadFileHost: "host", + UploadFileSetHost: true, + ResolvePathCalled: true, + ResolvePathReturn: "resolved/path", + }, + fail: true, + errMessage: "failed to add floppy", + }, + { + name: "Add floppy from FloppyIMGPath config", + step: &StepAddFloppy{ + Config: &FloppyConfig{ + FloppyIMGPath: "floppy/image/path", + }, + }, + expectedAction: multistep.ActionContinue, + vmMock: new(driver.VirtualMachineMock), + expectedVmMock: &driver.VirtualMachineMock{ + AddFloppyCalled: true, + AddFloppyImagePath: "floppy/image/path", + }, + driverMock: new(driver.DriverMock), + expectedDriverMock: new(driver.DriverMock), + dsMock: new(driver.DatastoreMock), + expectedDsMock: new(driver.DatastoreMock), + fail: false, + }, + { + name: "Fail to add floppy from FloppyIMGPath config", + step: &StepAddFloppy{ + Config: &FloppyConfig{ + FloppyIMGPath: "floppy/image/path", + }, + }, + expectedAction: multistep.ActionHalt, + vmMock: &driver.VirtualMachineMock{ + AddFloppyErr: fmt.Errorf("fail to add floppy"), + }, + expectedVmMock: &driver.VirtualMachineMock{ + AddFloppyCalled: true, + AddFloppyImagePath: "floppy/image/path", + }, + driverMock: new(driver.DriverMock), + expectedDriverMock: new(driver.DriverMock), + dsMock: new(driver.DatastoreMock), + expectedDsMock: new(driver.DatastoreMock), + fail: true, + errMessage: "fail to add floppy", + }, + } + + for _, c := range tc { + t.Run(c.name, func(t *testing.T) { + state := basicStateBag(nil) + state.Put("vm", c.vmMock) + c.driverMock.DatastoreMock = c.dsMock + state.Put("driver", c.driverMock) + + if c.floppyPath != "" { + state.Put("floppy_path", c.floppyPath) + } + + if action := c.step.Run(context.TODO(), state); action != c.expectedAction { + t.Fatalf("unexpected action %v", action) + } + err, ok := state.Get("error").(error) + if ok { + if err.Error() != c.errMessage { + t.Fatalf("unexpected error %s", err.Error()) + } + } else { + if c.fail { + t.Fatalf("expected to fail but it didn't") + } + } + + uploadedPath, _ := state.Get("uploaded_floppy_path").(string) + if uploadedPath != c.uploadedPath { + t.Fatalf("Unexpected uploaded path state %s", uploadedPath) + } + + if diff := cmp.Diff(c.vmMock, c.expectedVmMock, + cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" { + t.Fatalf("unexpected VirtualMachine calls: %s", diff) + } + c.expectedDriverMock.DatastoreMock = c.expectedDsMock + if diff := cmp.Diff(c.driverMock, c.expectedDriverMock, + cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" { + t.Fatalf("unexpected Driver calls: %s", diff) + } + if diff := cmp.Diff(c.dsMock, c.expectedDsMock, + cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" { + t.Fatalf("unexpected Datastore calls: %s", diff) + } + }) + } +} + +func TestStepAddFloppy_Cleanup(t *testing.T) { + tc := []struct { + name string + uploadedPath string + multistepState string + step *StepAddFloppy + driverMock *driver.DriverMock + expectedDriverMock *driver.DriverMock + dsMock *driver.DatastoreMock + expectedDsMock *driver.DatastoreMock + fail bool + errMessage string + }{ + { + name: "State cancelled clean up", + uploadedPath: "uploaded/path", + multistepState: multistep.StateCancelled, + step: &StepAddFloppy{ + Datastore: "datastore", + Host: "host", + }, + driverMock: new(driver.DriverMock), + expectedDriverMock: &driver.DriverMock{ + FindDatastoreCalled: true, + FindDatastoreName: "datastore", + FindDatastoreHost: "host", + }, + dsMock: &driver.DatastoreMock{ + DeleteCalled: true, + }, + expectedDsMock: &driver.DatastoreMock{ + DeleteCalled: true, + DeletePath: "uploaded/path", + }, + fail: false, + }, + { + name: "State halted clean up", + uploadedPath: "uploaded/path", + multistepState: multistep.StateHalted, + step: &StepAddFloppy{ + Datastore: "datastore", + Host: "host", + }, + driverMock: new(driver.DriverMock), + expectedDriverMock: &driver.DriverMock{ + FindDatastoreCalled: true, + FindDatastoreName: "datastore", + FindDatastoreHost: "host", + }, + dsMock: &driver.DatastoreMock{ + DeleteCalled: true, + }, + expectedDsMock: &driver.DatastoreMock{ + DeleteCalled: true, + DeletePath: "uploaded/path", + }, + fail: false, + }, + { + name: "Don't clean up without uploaded path", + multistepState: multistep.StateHalted, + step: new(StepAddFloppy), + driverMock: new(driver.DriverMock), + expectedDriverMock: new(driver.DriverMock), + dsMock: new(driver.DatastoreMock), + expectedDsMock: new(driver.DatastoreMock), + fail: false, + }, + { + name: "Don't clean up if state is not halted or canceled", + multistepState: "", + step: new(StepAddFloppy), + driverMock: new(driver.DriverMock), + expectedDriverMock: new(driver.DriverMock), + dsMock: new(driver.DatastoreMock), + expectedDsMock: new(driver.DatastoreMock), + fail: false, + }, + { + name: "Fail because datastore is not found", + uploadedPath: "uploaded/path", + multistepState: multistep.StateHalted, + step: &StepAddFloppy{ + Datastore: "datastore", + Host: "host", + }, + driverMock: &driver.DriverMock{ + FindDatastoreErr: fmt.Errorf("fail to find datastore"), + }, + expectedDriverMock: &driver.DriverMock{ + FindDatastoreCalled: true, + FindDatastoreName: "datastore", + FindDatastoreHost: "host", + }, + dsMock: new(driver.DatastoreMock), + expectedDsMock: new(driver.DatastoreMock), + fail: true, + errMessage: "fail to find datastore", + }, + { + name: "Fail to delete floppy", + uploadedPath: "uploaded/path", + multistepState: multistep.StateHalted, + step: &StepAddFloppy{ + Datastore: "datastore", + Host: "host", + }, + driverMock: new(driver.DriverMock), + expectedDriverMock: &driver.DriverMock{ + FindDatastoreCalled: true, + FindDatastoreName: "datastore", + FindDatastoreHost: "host", + }, + dsMock: &driver.DatastoreMock{ + DeleteCalled: true, + DeleteErr: fmt.Errorf("failed to delete floppy"), + }, + expectedDsMock: &driver.DatastoreMock{ + DeleteCalled: true, + DeletePath: "uploaded/path", + }, + fail: true, + errMessage: "failed to delete floppy", + }, + } + + for _, c := range tc { + t.Run(c.name, func(t *testing.T) { + state := basicStateBag(nil) + c.driverMock.DatastoreMock = c.dsMock + state.Put("driver", c.driverMock) + if c.uploadedPath != "" { + state.Put("uploaded_floppy_path", c.uploadedPath) + } + + if c.multistepState != "" { + state.Put(c.multistepState, true) + } + + c.step.Cleanup(state) + err, ok := state.Get("error").(error) + if ok { + if err.Error() != c.errMessage { + t.Fatalf("unexpected error %s", err.Error()) + } + } else { + if c.fail { + t.Fatalf("expected to fail but it didn't") + } + } + + c.expectedDriverMock.DatastoreMock = c.expectedDsMock + if diff := cmp.Diff(c.driverMock, c.expectedDriverMock, + cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" { + t.Fatalf("unexpected Driver calls: %s", diff) + } + if diff := cmp.Diff(c.dsMock, c.expectedDsMock, + cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" { + t.Fatalf("unexpected Datastore calls: %s", diff) + } + }) + } +} diff --git a/builder/vsphere/driver/datastore_mock.go b/builder/vsphere/driver/datastore_mock.go index 52fbc7c6b..b85c95f5b 100644 --- a/builder/vsphere/driver/datastore_mock.go +++ b/builder/vsphere/driver/datastore_mock.go @@ -8,7 +8,20 @@ import ( type DatastoreMock struct { FileExistsCalled bool MakeDirectoryCalled bool - UploadFileCalled bool + + ResolvePathCalled bool + ResolvePathReturn string + + DeleteCalled bool + DeletePath string + DeleteErr error + + UploadFileCalled bool + UploadFileSrc string + UploadFileDst string + UploadFileHost string + UploadFileSetHost bool + UploadFileErr error } func (ds *DatastoreMock) Info(params ...string) (*mo.Datastore, error) { @@ -29,16 +42,23 @@ func (ds *DatastoreMock) Reference() types.ManagedObjectReference { } func (ds *DatastoreMock) ResolvePath(path string) string { - return "" + ds.ResolvePathCalled = true + return ds.ResolvePathReturn } func (ds *DatastoreMock) UploadFile(src, dst, host string, setHost bool) error { ds.UploadFileCalled = true - return nil + ds.UploadFileSrc = src + ds.UploadFileDst = dst + ds.UploadFileHost = host + ds.UploadFileSetHost = setHost + return ds.UploadFileErr } func (ds *DatastoreMock) Delete(path string) error { - return nil + ds.DeleteCalled = true + ds.DeletePath = path + return ds.DeleteErr } func (ds *DatastoreMock) MakeDirectory(path string) error { diff --git a/builder/vsphere/driver/driver_mock.go b/builder/vsphere/driver/driver_mock.go index e1bbe1348..7f8748606 100644 --- a/builder/vsphere/driver/driver_mock.go +++ b/builder/vsphere/driver/driver_mock.go @@ -11,6 +11,9 @@ import ( type DriverMock struct { FindDatastoreCalled bool DatastoreMock *DatastoreMock + FindDatastoreName string + FindDatastoreHost string + FindDatastoreErr error PreCleanShouldFail bool PreCleanVMCalled bool @@ -32,7 +35,9 @@ func (d *DriverMock) FindDatastore(name string, host string) (Datastore, error) if d.DatastoreMock == nil { d.DatastoreMock = new(DatastoreMock) } - return d.DatastoreMock, nil + d.FindDatastoreName = name + d.FindDatastoreHost = host + return d.DatastoreMock, d.FindDatastoreErr } func (d *DriverMock) NewVM(ref *types.ManagedObjectReference) VirtualMachine { diff --git a/builder/vsphere/driver/vm_mock.go b/builder/vsphere/driver/vm_mock.go index db63168bf..a60a9b705 100644 --- a/builder/vsphere/driver/vm_mock.go +++ b/builder/vsphere/driver/vm_mock.go @@ -32,6 +32,14 @@ type VirtualMachineMock struct { AddCdromErr error AddCdromTypes []string AddCdromPaths []string + + GetDirCalled bool + GetDirResponse string + GetDirErr error + + AddFloppyCalled bool + AddFloppyImagePath string + AddFloppyErr error } func (vm *VirtualMachineMock) Info(params ...string) (*mo.VirtualMachine, error) { @@ -124,7 +132,8 @@ func (vm *VirtualMachineMock) ImportToContentLibrary(template vcenter.Template) } func (vm *VirtualMachineMock) GetDir() (string, error) { - return "", nil + vm.GetDirCalled = true + return vm.GetDirResponse, vm.GetDirErr } func (vm *VirtualMachineMock) AddCdrom(cdromType string, isoPath string) error { @@ -136,7 +145,9 @@ func (vm *VirtualMachineMock) AddCdrom(cdromType string, isoPath string) error { } func (vm *VirtualMachineMock) AddFloppy(imgPath string) error { - return nil + vm.AddFloppyCalled = true + vm.AddFloppyImagePath = imgPath + return vm.AddFloppyErr } func (vm *VirtualMachineMock) SetBootOrder(order []string) error {