diff --git a/cmd/vbhostd/vbhostd.go b/cmd/vbhostd/vbhostd.go index e0e9c7b..7fb5c43 100644 --- a/cmd/vbhostd/vbhostd.go +++ b/cmd/vbhostd/vbhostd.go @@ -1,6 +1,7 @@ package main import ( + "context" "flag" "fmt" "log" @@ -38,9 +39,11 @@ func main() { logger.Print(msg + "\n") } + manager := virtualbox.NewManager() + var vms []string if *vm == "all" { - machines, err := virtualbox.ListMachines() + machines, err := manager.ListMachines(context.Background()) if err != nil { panic(err) } diff --git a/gen.go b/gen.go new file mode 100644 index 0000000..fedffa6 --- /dev/null +++ b/gen.go @@ -0,0 +1,3 @@ +package virtualbox + +//go:generate mockgen -package=virtualbox -source=vbcmd.go -destination=mockvbcmd_test.go diff --git a/machine.go b/machine.go index 9186ddc..4e9251d 100644 --- a/machine.go +++ b/machine.go @@ -260,7 +260,7 @@ func (m *Machine) Start() error { args = []string{"startvm", m.Name, "--type", "headless"} } - _, msg, err := Run(context.Background(), args...) + _, msg, err := vboxManageRun(context.Background(), args...) if err != nil { return errors.New(msg) } @@ -406,44 +406,44 @@ func CreateMachine(name, basefolder string) (*Machine, error) { return m, nil } -// Modify changes the settings of the machine. -func (m *Machine) Modify() error { - args := []string{"modifyvm", m.Name, - "--firmware", m.Firmware, +// UpdateMachine updates the machine details based on the struct fields. +func (m *Manager) UpdateMachine(ctx context.Context, vm *Machine) error { + args := []string{"modifyvm", vm.Name, + "--firmware", vm.Firmware, "--bioslogofadein", "off", "--bioslogofadeout", "off", "--bioslogodisplaytime", "0", "--biosbootmenu", "disabled", - "--ostype", m.OSType, - "--cpus", fmt.Sprintf("%d", m.CPUs), - "--memory", fmt.Sprintf("%d", m.Memory), - "--vram", fmt.Sprintf("%d", m.VRAM), - - "--acpi", m.Flag.Get(ACPI), - "--ioapic", m.Flag.Get(IOAPIC), - "--rtcuseutc", m.Flag.Get(RTCUSEUTC), - "--cpuhotplug", m.Flag.Get(CPUHOTPLUG), - "--pae", m.Flag.Get(PAE), - "--longmode", m.Flag.Get(LONGMODE), - "--hpet", m.Flag.Get(HPET), - "--hwvirtex", m.Flag.Get(HWVIRTEX), - "--triplefaultreset", m.Flag.Get(TRIPLEFAULTRESET), - "--nestedpaging", m.Flag.Get(NESTEDPAGING), - "--largepages", m.Flag.Get(LARGEPAGES), - "--vtxvpid", m.Flag.Get(VTXVPID), - "--vtxux", m.Flag.Get(VTXUX), - "--accelerate3d", m.Flag.Get(ACCELERATE3D), - } - - for i, dev := range m.BootOrder { + "--ostype", vm.OSType, + "--cpus", fmt.Sprintf("%d", vm.CPUs), + "--memory", fmt.Sprintf("%d", vm.Memory), + "--vram", fmt.Sprintf("%d", vm.VRAM), + + "--acpi", vm.Flag.Get(ACPI), + "--ioapic", vm.Flag.Get(IOAPIC), + "--rtcuseutc", vm.Flag.Get(RTCUSEUTC), + "--cpuhotplug", vm.Flag.Get(CPUHOTPLUG), + "--pae", vm.Flag.Get(PAE), + "--longmode", vm.Flag.Get(LONGMODE), + "--hpet", vm.Flag.Get(HPET), + "--hwvirtex", vm.Flag.Get(HWVIRTEX), + "--triplefaultreset", vm.Flag.Get(TRIPLEFAULTRESET), + "--nestedpaging", vm.Flag.Get(NESTEDPAGING), + "--largepages", vm.Flag.Get(LARGEPAGES), + "--vtxvpid", vm.Flag.Get(VTXVPID), + "--vtxux", vm.Flag.Get(VTXUX), + "--accelerate3d", vm.Flag.Get(ACCELERATE3D), + } + + for i, dev := range vm.BootOrder { if i > 3 { break // Only four slots `--boot{1,2,3,4}`. Ignore the rest. } args = append(args, fmt.Sprintf("--boot%d", i+1), dev) } - for i, nic := range m.NICs { + for i, nic := range vm.NICs { n := i + 1 args = append(args, fmt.Sprintf("--nic%d", n), string(nic.Network), @@ -456,10 +456,14 @@ func (m *Machine) Modify() error { } } - if err := Manage().run(args...); err != nil { + if _, _, err := m.run(ctx, args...); err != nil { return err } - return m.Refresh() + return vm.Refresh() +} + +func (m *Machine) Modify() error { + return defaultManager.UpdateMachine(context.Background(), m) } // AddNATPF adds a NAT port forarding rule to the n-th NIC with the given name. @@ -513,22 +517,27 @@ func (m *Machine) DelStorageCtl(name string) error { // AttachStorage attaches a storage medium to the named storage controller. func (m *Machine) AttachStorage(ctlName string, medium StorageMedium) error { - return Manage().run("storageattach", m.Name, "--storagectl", ctlName, + _, _, err := defaultManager.run(context.Background(), + "storageattach", m.Name, "--storagectl", ctlName, "--port", fmt.Sprintf("%d", medium.Port), "--device", fmt.Sprintf("%d", medium.Device), "--type", string(medium.DriveType), "--medium", medium.Medium, ) + return err } // SetExtraData attaches custom string to the VM. func (m *Machine) SetExtraData(key, val string) error { - return Manage().run("setextradata", m.Name, key, val) + _, _, err := defaultManager.run(context.Background(), + "setextradata", m.Name, key, val) + return err } // GetExtraData retrieves custom string from the VM. func (m *Machine) GetExtraData(key string) (*string, error) { - value, err := Manage().runOut("getextradata", m.Name, key) + value, _, err := defaultManager.run(context.Background(), + "getextradata", m.Name, key) if err != nil { return nil, err } @@ -544,13 +553,19 @@ func (m *Machine) GetExtraData(key string) (*string, error) { // DeleteExtraData removes custom string from the VM. func (m *Machine) DeleteExtraData(key string) error { - return Manage().run("setextradata", m.Name, key) + _, _, err := defaultManager.run(context.Background(), + "setextradata", m.Name, key) + return err } // CloneMachine clones the given machine name into a new one. func CloneMachine(baseImageName string, newImageName string, register bool) error { if register { - return Manage().run("clonevm", baseImageName, "--name", newImageName, "--register") + _, _, err := defaultManager.run(context.Background(), + "clonevm", baseImageName, "--name", newImageName, "--register") + return err } - return Manage().run("clonevm", baseImageName, "--name", newImageName) + _, _, err := defaultManager.run(context.Background(), + "clonevm", baseImageName, "--name", newImageName) + return err } diff --git a/mock/gen.go b/mock/gen.go new file mode 100644 index 0000000..33fac67 --- /dev/null +++ b/mock/gen.go @@ -0,0 +1,3 @@ +package mock + +//go:generate mockgen -package=mock -source=../interface.go -destination=interface_mock.go diff --git a/mock/interface_mock.go b/mock/interface_mock.go new file mode 100644 index 0000000..aa556c6 --- /dev/null +++ b/mock/interface_mock.go @@ -0,0 +1,80 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../interface.go + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + virtualbox "github.com/terra-farm/go-virtualbox" +) + +// MockManager is a mock of Manager interface. +type MockManager struct { + ctrl *gomock.Controller + recorder *MockManagerMockRecorder +} + +// MockManagerMockRecorder is the mock recorder for MockManager. +type MockManagerMockRecorder struct { + mock *MockManager +} + +// NewMockManager creates a new mock instance. +func NewMockManager(ctrl *gomock.Controller) *MockManager { + mock := &MockManager{ctrl: ctrl} + mock.recorder = &MockManagerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockManager) EXPECT() *MockManagerMockRecorder { + return m.recorder +} + +// ListMachines mocks base method. +func (m *MockManager) ListMachines(arg0 context.Context) ([]*virtualbox.Machine, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListMachines", arg0) + ret0, _ := ret[0].([]*virtualbox.Machine) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListMachines indicates an expected call of ListMachines. +func (mr *MockManagerMockRecorder) ListMachines(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListMachines", reflect.TypeOf((*MockManager)(nil).ListMachines), arg0) +} + +// Machine mocks base method. +func (m *MockManager) Machine(arg0 context.Context, arg1 string) (*virtualbox.Machine, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Machine", arg0, arg1) + ret0, _ := ret[0].(*virtualbox.Machine) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Machine indicates an expected call of Machine. +func (mr *MockManagerMockRecorder) Machine(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Machine", reflect.TypeOf((*MockManager)(nil).Machine), arg0, arg1) +} + +// UpdateMachine mocks base method. +func (m *MockManager) UpdateMachine(arg0 context.Context, arg1 *virtualbox.Machine) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateMachine", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateMachine indicates an expected call of UpdateMachine. +func (mr *MockManagerMockRecorder) UpdateMachine(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateMachine", reflect.TypeOf((*MockManager)(nil).UpdateMachine), arg0, arg1) +} diff --git a/mockvbcmd_test.go b/mockvbcmd_test.go index 82f2e5a..76980a0 100644 --- a/mockvbcmd_test.go +++ b/mockvbcmd_test.go @@ -5,75 +5,66 @@ package virtualbox import ( - gomock "github.com/golang/mock/gomock" + context "context" reflect "reflect" + + gomock "github.com/golang/mock/gomock" ) -// MockCommand is a mock of Command interface +// MockCommand is a mock of Command interface. type MockCommand struct { ctrl *gomock.Controller recorder *MockCommandMockRecorder } -// MockCommandMockRecorder is the mock recorder for MockCommand +// MockCommandMockRecorder is the mock recorder for MockCommand. type MockCommandMockRecorder struct { mock *MockCommand } -// NewMockCommand creates a new mock instance +// NewMockCommand creates a new mock instance. func NewMockCommand(ctrl *gomock.Controller) *MockCommand { mock := &MockCommand{ctrl: ctrl} mock.recorder = &MockCommandMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCommand) EXPECT() *MockCommandMockRecorder { return m.recorder } -// setOpts mocks base method -func (m *MockCommand) setOpts(opts ...option) Command { - varargs := []interface{}{} - for _, a := range opts { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "setOpts", varargs...) - ret0, _ := ret[0].(Command) - return ret0 -} - -// setOpts indicates an expected call of setOpts -func (mr *MockCommandMockRecorder) setOpts(opts ...interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "setOpts", reflect.TypeOf((*MockCommand)(nil).setOpts), opts...) -} - -// isGuest mocks base method +// isGuest mocks base method. func (m *MockCommand) isGuest() bool { + m.ctrl.T.Helper() ret := m.ctrl.Call(m, "isGuest") ret0, _ := ret[0].(bool) return ret0 } -// isGuest indicates an expected call of isGuest +// isGuest indicates an expected call of isGuest. func (mr *MockCommandMockRecorder) isGuest() *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "isGuest", reflect.TypeOf((*MockCommand)(nil).isGuest)) } -// path mocks base method +// path mocks base method. func (m *MockCommand) path() string { + m.ctrl.T.Helper() ret := m.ctrl.Call(m, "path") ret0, _ := ret[0].(string) return ret0 } -// path indicates an expected call of path +// path indicates an expected call of path. func (mr *MockCommandMockRecorder) path() *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "path", reflect.TypeOf((*MockCommand)(nil).path)) } -// run mocks base method +// run mocks base method. func (m *MockCommand) run(args ...string) error { + m.ctrl.T.Helper() varargs := []interface{}{} for _, a := range args { varargs = append(varargs, a) @@ -83,13 +74,15 @@ func (m *MockCommand) run(args ...string) error { return ret0 } -// run indicates an expected call of run +// run indicates an expected call of run. func (mr *MockCommandMockRecorder) run(args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "run", reflect.TypeOf((*MockCommand)(nil).run), args...) } -// runOut mocks base method +// runOut mocks base method. func (m *MockCommand) runOut(args ...string) (string, error) { + m.ctrl.T.Helper() varargs := []interface{}{} for _, a := range args { varargs = append(varargs, a) @@ -100,13 +93,15 @@ func (m *MockCommand) runOut(args ...string) (string, error) { return ret0, ret1 } -// runOut indicates an expected call of runOut +// runOut indicates an expected call of runOut. func (mr *MockCommandMockRecorder) runOut(args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "runOut", reflect.TypeOf((*MockCommand)(nil).runOut), args...) } -// runOutErr mocks base method +// runOutErr mocks base method. func (m *MockCommand) runOutErr(args ...string) (string, string, error) { + m.ctrl.T.Helper() varargs := []interface{}{} for _, a := range args { varargs = append(varargs, a) @@ -118,7 +113,47 @@ func (m *MockCommand) runOutErr(args ...string) (string, string, error) { return ret0, ret1, ret2 } -// runOutErr indicates an expected call of runOutErr +// runOutErr indicates an expected call of runOutErr. func (mr *MockCommandMockRecorder) runOutErr(args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "runOutErr", reflect.TypeOf((*MockCommand)(nil).runOutErr), args...) } + +// runOutErrContext mocks base method. +func (m *MockCommand) runOutErrContext(ctx context.Context, args ...string) (string, string, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx} + for _, a := range args { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "runOutErrContext", varargs...) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// runOutErrContext indicates an expected call of runOutErrContext. +func (mr *MockCommandMockRecorder) runOutErrContext(ctx interface{}, args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "runOutErrContext", reflect.TypeOf((*MockCommand)(nil).runOutErrContext), varargs...) +} + +// setOpts mocks base method. +func (m *MockCommand) setOpts(opts ...option) Command { + m.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "setOpts", varargs...) + ret0, _ := ret[0].(Command) + return ret0 +} + +// setOpts indicates an expected call of setOpts. +func (mr *MockCommandMockRecorder) setOpts(opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "setOpts", reflect.TypeOf((*MockCommand)(nil).setOpts), opts...) +} diff --git a/util.go b/util.go index 82ca5ca..8c4f7ec 100644 --- a/util.go +++ b/util.go @@ -1,7 +1,6 @@ package virtualbox import ( - "context" "net" ) @@ -14,12 +13,3 @@ func ParseIPv4Mask(s string) net.IPMask { } return net.IPv4Mask(mask[12], mask[13], mask[14], mask[15]) } - -// Run is a helper method used to execute the commands using the configured -// VBoxManage path. The command should be omitted and only the arguments -// should be passed. It will return the stdout, stderr and error if one -// occured during command execution. -func Run(_ context.Context, args ...string) (string, string, error) { - // TODO: Convert the function so you can pass in the context. - return Manage().runOutErr(args...) -} diff --git a/vbcmd.go b/vbcmd.go index f01e2d6..523906b 100644 --- a/vbcmd.go +++ b/vbcmd.go @@ -2,6 +2,7 @@ package virtualbox import ( "bytes" + "context" "errors" "os" "os/exec" @@ -19,6 +20,7 @@ type Command interface { run(args ...string) error runOut(args ...string) (string, error) runOutErr(args ...string) (string, string, error) + runOutErrContext(ctx context.Context, args ...string) (string, string, error) } var ( @@ -63,7 +65,7 @@ func (vbcmd command) path() string { return vbcmd.program } -func (vbcmd command) prepare(args []string) *exec.Cmd { +func (vbcmd command) prepare(ctx context.Context, args []string) *exec.Cmd { program := vbcmd.program argv := []string{} Debug("Command: '%+v', runtime.GOOS: '%s'", vbcmd, runtime.GOOS) @@ -73,12 +75,12 @@ func (vbcmd command) prepare(args []string) *exec.Cmd { } argv = append(argv, args...) Debug("executing: %v %v", program, argv) - return exec.Command(program, argv...) // #nosec + return exec.CommandContext(ctx, program, argv...) // #nosec } func (vbcmd command) run(args ...string) error { defer vbcmd.setOpts(sudo(false)) - cmd := vbcmd.prepare(args) + cmd := vbcmd.prepare(context.Background(), args) if Verbose { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -94,7 +96,7 @@ func (vbcmd command) run(args ...string) error { func (vbcmd command) runOut(args ...string) (string, error) { defer vbcmd.setOpts(sudo(false)) - cmd := vbcmd.prepare(args) + cmd := vbcmd.prepare(context.Background(), args) if Verbose { cmd.Stderr = os.Stderr } @@ -108,9 +110,9 @@ func (vbcmd command) runOut(args ...string) (string, error) { return string(b), err } -func (vbcmd command) runOutErr(args ...string) (string, string, error) { +func (vbcmd command) runOutErrContext(ctx context.Context, args ...string) (string, string, error) { defer vbcmd.setOpts(sudo(false)) - cmd := vbcmd.prepare(args) + cmd := vbcmd.prepare(ctx, args) var stdout bytes.Buffer var stderr bytes.Buffer cmd.Stdout = &stdout @@ -123,3 +125,7 @@ func (vbcmd command) runOutErr(args ...string) (string, string, error) { } return stdout.String(), stderr.String(), err } + +func (vbcmd command) runOutErr(args ...string) (string, string, error) { + return vbcmd.runOutErrContext(context.Background(), args...) +} diff --git a/vbmgt_test.go b/vbmgt_test.go index 9a7c203..1547fb5 100644 --- a/vbmgt_test.go +++ b/vbmgt_test.go @@ -1,7 +1,6 @@ package virtualbox import ( - "io/ioutil" "os" "path" "testing" @@ -17,7 +16,7 @@ var ( ) func ReadTestData(file string) string { - out, err := ioutil.ReadFile(path.Join("testdata", file)) + out, err := os.ReadFile(path.Join("testdata", file)) if err != nil { panic("No such file :testdata/" + file) }