Skip to content
Merged
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
22 changes: 16 additions & 6 deletions cmd/limactl/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ func editAction(cmd *cobra.Command, args []string) error {
return err
}
if err := limayaml.Validate(y, true); err != nil {
rejectedYAML := "lima.REJECTED.yaml"
if writeErr := os.WriteFile(rejectedYAML, yBytes, 0o644); writeErr != nil {
return fmt.Errorf("the YAML is invalid, attempted to save the buffer as %q but failed: %w: %w", rejectedYAML, writeErr, err)
}
// TODO: may need to support editing the rejected YAML
return fmt.Errorf("the YAML is invalid, saved the buffer as %q: %w", rejectedYAML, err)
return saveRejectedYAML(yBytes, err)
}

if err := limayaml.ValidateYAMLAgainstLatestConfig(yBytes, yContent); err != nil {
return saveRejectedYAML(yBytes, err)
}

if err := os.WriteFile(filePath, yBytes, 0o644); err != nil {
return err
}
Expand Down Expand Up @@ -171,3 +171,13 @@ func askWhetherToStart() (bool, error) {
func editBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return bashCompleteInstanceNames(cmd)
}

// saveRejectedYAML writes the rejected config and returns an error.
func saveRejectedYAML(y []byte, origErr error) error {
rejectedYAML := "lima.REJECTED.yaml"
if writeErr := os.WriteFile(rejectedYAML, y, 0o644); writeErr != nil {
return fmt.Errorf("the YAML is invalid, attempted to save the buffer as %q but failed: %w", rejectedYAML, errors.Join(writeErr, origErr))
}
// TODO: may need to support editing the rejected YAML
return fmt.Errorf("the YAML is invalid, saved the buffer as %q: %w", rejectedYAML, origErr)
}
37 changes: 37 additions & 0 deletions pkg/limayaml/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,3 +610,40 @@ func warnExperimental(y *LimaYAML) {
logrus.Warn("`mountInotify` is experimental")
}
}

// ValidateYAMLAgainstLatestConfig validates the values between the latest YAML and the updated(New) YAML.
// This validates configuration rules that disallow certain changes, such as shrinking the disk.
func ValidateYAMLAgainstLatestConfig(yNew, yLatest []byte) error {
var n LimaYAML

// Load the latest YAML and fill in defaults
l, err := LoadWithWarnings(yLatest, "")
if err != nil {
return err
}
if err := Unmarshal(yNew, &n, "Unmarshal new YAML bytes"); err != nil {
return err
}

// Handle editing the template without a disk value
if n.Disk == nil || l.Disk == nil {
return nil
}

// Disk value must be provided, as it is required when creating an instance.
nDisk, err := units.RAMInBytes(*n.Disk)
if err != nil {
return err
}
lDisk, err := units.RAMInBytes(*l.Disk)
if err != nil {
return err
}

// Reject shrinking disk
if nDisk < lDisk {
return fmt.Errorf("field `disk`: shrinking the disk (%v --> %v) is not supported", *l.Disk, *n.Disk)
}

return nil
}
53 changes: 53 additions & 0 deletions pkg/limayaml/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package limayaml

import (
"errors"
"testing"

"gotest.tools/v3/assert"
Expand Down Expand Up @@ -264,3 +265,55 @@ provision:
"field `provision[0].mode` must one of \"system\", \"user\", \"boot\", \"data\", \"dependency\", or \"ansible\"\n"+
"field `provision[1].path` must not be empty when mode is \"data\"")
}

func TestValidateYAMLAgainstLatestConfig(t *testing.T) {
tests := []struct {
name string
yNew string
yLatest string
wantErr error
}{
{
name: "Valid disk size unchanged",
yNew: `disk: 100GiB`,
yLatest: `disk: 100GiB`,
},
{
name: "Valid disk size increased",
yNew: `disk: 200GiB`,
yLatest: `disk: 100GiB`,
},
{
name: "No disk field in both YAMLs",
yNew: ``,
yLatest: ``,
},
{
name: "No disk field in new YAMLs",
yNew: ``,
yLatest: `disk: 100GiB`,
},
{
name: "No disk field in latest YAMLs",
yNew: `disk: 100GiB`,
yLatest: ``,
},
{
name: "Disk size shrunk",
yNew: `disk: 50GiB`,
yLatest: `disk: 100GiB`,
wantErr: errors.New("field `disk`: shrinking the disk (100GiB --> 50GiB) is not supported"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateYAMLAgainstLatestConfig([]byte(tt.yNew), []byte(tt.yLatest))
if tt.wantErr == nil {
assert.NilError(t, err)
} else {
assert.Error(t, err, tt.wantErr.Error())
}
})
}
}
Loading