Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cmd/finch/virtual_machine_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ import (
"github.com/runfinch/finch/pkg/path"
)

func newDiskVMCommand(creator command.NerdctlCmdCreator, logger flog.Logger) *cobra.Command {
func newDiskVMCommand(creator command.NerdctlCmdCreator, logger flog.Logger, lca config.LimaConfigApplier, fs afero.Fs) *cobra.Command {
diskCmd := &cobra.Command{
Use: "disk",
Short: "Manage virtual machine disk operations",
}

diskCmd.AddCommand(
newVMDiskResizeCommand(creator, logger),
newVMDiskResizeCommand(creator, logger, lca, fs),
newVMDiskInfoCommand(creator, logger),
)

Expand Down Expand Up @@ -57,7 +57,7 @@ func newVirtualMachineCommand(
newInitVMCommand(limaCmdCreator, logger, optionalDepGroups, lca, nca, fp.BaseYamlFilePath(), fs,
fp.LimaSSHPrivateKeyPath(), diskManager, finchConfig),
newSettingsVMCommand(logger, lca, fs, os.Stdout),
newDiskVMCommand(limaCmdCreator, logger),
newDiskVMCommand(limaCmdCreator, logger, lca, fs),
)

return virtualMachineCommand
Expand Down
53 changes: 45 additions & 8 deletions cmd/finch/virtual_machine_disk_resize.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,43 @@ package main
import (
"fmt"

"github.com/spf13/afero"
"github.com/spf13/cobra"

"github.com/runfinch/finch/pkg/command"
"github.com/runfinch/finch/pkg/config"
"github.com/runfinch/finch/pkg/flog"
)

func newVMDiskResizeCommand(limaCmdCreator command.NerdctlCmdCreator, logger flog.Logger) *cobra.Command {
func newVMDiskResizeCommand(
limaCmdCreator command.NerdctlCmdCreator, logger flog.Logger, lca config.LimaConfigApplier, fs afero.Fs,
) *cobra.Command {
var size string
cmd := &cobra.Command{
Use: "resize",
Short: "Resize the virtual machine disk",
RunE: newVMDiskResizeAction(limaCmdCreator, logger).runAdapter,
Short: "Resize the virtual machine's data disk",
RunE: newVMDiskResizeAction(limaCmdCreator, logger, lca, fs).runAdapter,
}
cmd.Flags().StringVar(&size, "size", "", "New size for the disk (required)")
_ = cmd.MarkFlagRequired("size")
return cmd
}

type diskResizeVMAction struct {
logger flog.Logger
creator command.NerdctlCmdCreator
logger flog.Logger
creator command.NerdctlCmdCreator
limaConfigApplier config.LimaConfigApplier
fs afero.Fs
}

func newVMDiskResizeAction(limaCmdCreator command.NerdctlCmdCreator, logger flog.Logger) *diskResizeVMAction {
func newVMDiskResizeAction(
limaCmdCreator command.NerdctlCmdCreator, logger flog.Logger, lca config.LimaConfigApplier, fs afero.Fs,
) *diskResizeVMAction {
return &diskResizeVMAction{
logger: logger,
creator: limaCmdCreator,
logger: logger,
creator: limaCmdCreator,
limaConfigApplier: lca,
fs: fs,
}
}

Expand All @@ -47,14 +57,41 @@ func (dva *diskResizeVMAction) runAdapter(cmd *cobra.Command, _ []string) error
}

func (dva *diskResizeVMAction) run(size string) error {
dva.logger.Warnf("Increasing disk size is irreversible without losing data.")
dva.logger.Infof("Resizing disk to %s...", size)

configPath := dva.limaConfigApplier.GetFinchConfigPath()
oldConfig, err := afero.ReadFile(dva.fs, configPath)
if err != nil {
return fmt.Errorf("failed to read finch config: %w", err)
}

isConfigUpdated, err := config.ModifyFinchConfig(
dva.fs,
dva.logger,
configPath,
config.VMConfigOpts{DataDisk: &size},
)
if err != nil {
return fmt.Errorf("failed to update finch config: %w", err)
}

limaCmd := dva.creator.CreateWithoutStdio("disk", "resize", limaInstanceName, "--size", size)
output, err := limaCmd.CombinedOutput()
if err != nil {
if isConfigUpdated {
if writeErr := afero.WriteFile(dva.fs, configPath, oldConfig, 0o644); writeErr != nil {
return fmt.Errorf("failed to rollback config (disk resize failed): %w", writeErr)
}
dva.logger.Info("Configuration changes rolled back.")
}
return fmt.Errorf("failed to resize disk: %w\n%s", err, output)
}

dva.logger.Info("Disk resized successfully.")
if isConfigUpdated {
dva.logger.Info("Configuration updated with new disk size.")
}

return nil
}
31 changes: 30 additions & 1 deletion cmd/finch/virtual_machine_settings_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ func newSettingsVMCommand(
config.DefaultMemory,
"the amount of memory to dedicate to the virtual machine (restart the vm when applying this change.)",
)
settingsVMCommand.Flags().String(
"bootdisk",
config.DefaultBootDisk,
"the amount of boot disk space (sparse) to give to the virtual machine (restart the vm when applying this change.)",
)
settingsVMCommand.Flags().String(
"datadisk",
config.DefaultDataDisk,
"the amount of data disk space (sparse) to give to the virtual machine (restart the vm when applying this change.)",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above, warning that this cannot be lowered in the future

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not make sense to add a warning here as this command just updates the config and does not perform the resize operation (the resizing happens on next vm start/init).

)

return settingsVMCommand
}
Expand Down Expand Up @@ -74,11 +84,23 @@ func (sva *settingsVMAction) runAdapter(cmd *cobra.Command, _ []string) error {
return err
}

bootDisk, err := cmd.Flags().GetString("bootdisk")
if err != nil {
return err
}

dataDisk, err := cmd.Flags().GetString("datadisk")
if err != nil {
return err
}

cpusChanged := cmd.Flags().Changed("cpus")
memoryChanged := cmd.Flags().Changed("memory")
bootDiskChanged := cmd.Flags().Changed("bootdisk")
dataDiskChanged := cmd.Flags().Changed("datadisk")

// check if any flags were provided by the user
if !cpusChanged && !memoryChanged {
if !cpusChanged && !memoryChanged && !bootDiskChanged && !dataDiskChanged {
return cmd.Help()
}

Expand All @@ -90,6 +112,13 @@ func (sva *settingsVMAction) runAdapter(cmd *cobra.Command, _ []string) error {
opts.Memory = &memory
}

if bootDiskChanged {
opts.BootDisk = &bootDisk
}
if dataDiskChanged {
opts.DataDisk = &dataDisk
}

return sva.run(opts)
}

Expand Down
126 changes: 121 additions & 5 deletions cmd/finch/virtual_machine_settings_darwin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,46 @@ func TestSettingsVMAction_runAdapter(t *testing.T) {
lca.EXPECT().GetFinchConfigPath().Return(finchConfigPath)
},
},
{
name: "should configure the instance for valid bootdisk value",
wantErr: nil,
command: &cobra.Command{
Use: "settings",
},
args: []string{
"--bootdisk=60GiB",
},
mockSvc: func(
lca *mocks.LimaConfigApplier,
fs afero.Fs,
) {
finchConfigPath := "/config.yaml"
data := "cpus: 2\nmemory: 6GiB\nbootdisk: 100GiB\ndatadisk: 50GiB"
require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600))

lca.EXPECT().GetFinchConfigPath().Return(finchConfigPath)
},
},
{
name: "should configure the instance for valid datadisk value",
wantErr: nil,
command: &cobra.Command{
Use: "settings",
},
args: []string{
"--datadisk=60GiB",
},
mockSvc: func(
lca *mocks.LimaConfigApplier,
fs afero.Fs,
) {
finchConfigPath := "/config.yaml"
data := "cpus: 2\nmemory: 6GiB\nbootdisk: 100GiB\ndatadisk: 50GiB"
require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600))

lca.EXPECT().GetFinchConfigPath().Return(finchConfigPath)
},
},
{
name: "should show settings --help when no flags are provided",
wantErr: nil,
Expand Down Expand Up @@ -155,8 +195,10 @@ func TestSettingsVMAction_run(t *testing.T) {
*mocks.LimaConfigApplier,
afero.Fs,
)
cpus *int
memory *string
cpus *int
memory *string
bootDisk *string
dataDisk *string
}{
{
name: "should update vm settings",
Expand Down Expand Up @@ -230,6 +272,44 @@ func TestSettingsVMAction_run(t *testing.T) {
cpus: intPtr(1),
memory: stringPtr("2gi"),
},
{
name: "should return an error if the configuration of bootdisk is invalid",
wantErr: "failed to validate config file: failed to parse bootdisk to uint: invalid suffix: 'gi'",
wantStatusOutput: "",
wantWarnOutput: "",
mockSvc: func(
lca *mocks.LimaConfigApplier,
fs afero.Fs,
) {
finchConfigPath := "/config.yaml"
data := "cpus: 2\nmemory: 6GiB\nbootdisk: 100GiB\ndatadisk: 50GiB"
require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600))

lca.EXPECT().GetFinchConfigPath().Return(finchConfigPath)
},
cpus: intPtr(1),
memory: stringPtr("2GiB"),
bootDisk: stringPtr("50gi"),
},
{
name: "should return an error if the configuration of datadisk is invalid",
wantErr: "failed to validate config file: failed to parse datadisk to uint: invalid suffix: 'gi'",
wantStatusOutput: "",
wantWarnOutput: "",
mockSvc: func(
lca *mocks.LimaConfigApplier,
fs afero.Fs,
) {
finchConfigPath := "/config.yaml"
data := "cpus: 2\nmemory: 6GiB\nbootdisk: 100GiB\ndatadisk: 50GiB"
require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600))

lca.EXPECT().GetFinchConfigPath().Return(finchConfigPath)
},
cpus: intPtr(1),
memory: stringPtr("2GiB"),
dataDisk: stringPtr("50gi"),
},
{
name: "should not return an error if the configuration of CPU and memory matches existing config",
wantErr: "",
Expand All @@ -240,14 +320,48 @@ func TestSettingsVMAction_run(t *testing.T) {
fs afero.Fs,
) {
finchConfigPath := "/config.yaml"
data := "cpus: 2\nmemory: 6GiB"
data := "cpus: 2\nmemory: 6GiB\nbootdisk: 100GiB\ndatadisk: 50GiB"
require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600))

lca.EXPECT().GetFinchConfigPath().Return(finchConfigPath)
},
cpus: intPtr(2),
memory: stringPtr("6GiB"),
},
{
name: "should not return an error if the configuration of bootdisk matches existing config",
wantErr: "",
wantStatusOutput: "",
wantWarnOutput: "Provided flags match existing settings, no changes made.",
mockSvc: func(
lca *mocks.LimaConfigApplier,
fs afero.Fs,
) {
finchConfigPath := "/config.yaml"
data := "cpus: 2\nmemory: 6GiB\nbootdisk: 100GiB\ndatadisk: 50GiB"
require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600))

lca.EXPECT().GetFinchConfigPath().Return(finchConfigPath)
},
bootDisk: stringPtr("100GiB"),
},
{
name: "should not return an error if the configuration of datadisk matches existing config",
wantErr: "",
wantStatusOutput: "",
wantWarnOutput: "Provided flags match existing settings, no changes made.",
mockSvc: func(
lca *mocks.LimaConfigApplier,
fs afero.Fs,
) {
finchConfigPath := "/config.yaml"
data := "cpus: 2\nmemory: 6GiB\nbootdisk: 100GiB\ndatadisk: 50GiB"
require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600))

lca.EXPECT().GetFinchConfigPath().Return(finchConfigPath)
},
dataDisk: stringPtr("50GiB"),
},
}

for _, tc := range testCases {
Expand All @@ -260,8 +374,10 @@ func TestSettingsVMAction_run(t *testing.T) {
fs := afero.NewMemMapFs()
stdout := bytes.Buffer{}
opts := config.VMConfigOpts{
CPUs: tc.cpus,
Memory: tc.memory,
CPUs: tc.cpus,
Memory: tc.memory,
BootDisk: tc.bootDisk,
DataDisk: tc.dataDisk,
}

tc.mockSvc(lca, fs)
Expand Down
Loading
Loading