Skip to content

Commit 1abadcd

Browse files
authored
Merge pull request #3596 from songponssw/reject-shrinking-disk
Reject shrinking disk during YAML validation
2 parents b668567 + c13b75d commit 1abadcd

File tree

3 files changed

+106
-6
lines changed

3 files changed

+106
-6
lines changed

cmd/limactl/edit.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,13 @@ func editAction(cmd *cobra.Command, args []string) error {
118118
return err
119119
}
120120
if err := limayaml.Validate(y, true); err != nil {
121-
rejectedYAML := "lima.REJECTED.yaml"
122-
if writeErr := os.WriteFile(rejectedYAML, yBytes, 0o644); writeErr != nil {
123-
return fmt.Errorf("the YAML is invalid, attempted to save the buffer as %q but failed: %w: %w", rejectedYAML, writeErr, err)
124-
}
125-
// TODO: may need to support editing the rejected YAML
126-
return fmt.Errorf("the YAML is invalid, saved the buffer as %q: %w", rejectedYAML, err)
121+
return saveRejectedYAML(yBytes, err)
122+
}
123+
124+
if err := limayaml.ValidateYAMLAgainstLatestConfig(yBytes, yContent); err != nil {
125+
return saveRejectedYAML(yBytes, err)
127126
}
127+
128128
if err := os.WriteFile(filePath, yBytes, 0o644); err != nil {
129129
return err
130130
}
@@ -171,3 +171,13 @@ func askWhetherToStart() (bool, error) {
171171
func editBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
172172
return bashCompleteInstanceNames(cmd)
173173
}
174+
175+
// saveRejectedYAML writes the rejected config and returns an error.
176+
func saveRejectedYAML(y []byte, origErr error) error {
177+
rejectedYAML := "lima.REJECTED.yaml"
178+
if writeErr := os.WriteFile(rejectedYAML, y, 0o644); writeErr != nil {
179+
return fmt.Errorf("the YAML is invalid, attempted to save the buffer as %q but failed: %w", rejectedYAML, errors.Join(writeErr, origErr))
180+
}
181+
// TODO: may need to support editing the rejected YAML
182+
return fmt.Errorf("the YAML is invalid, saved the buffer as %q: %w", rejectedYAML, origErr)
183+
}

pkg/limayaml/validate.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,3 +610,40 @@ func warnExperimental(y *LimaYAML) {
610610
logrus.Warn("`mountInotify` is experimental")
611611
}
612612
}
613+
614+
// ValidateYAMLAgainstLatestConfig validates the values between the latest YAML and the updated(New) YAML.
615+
// This validates configuration rules that disallow certain changes, such as shrinking the disk.
616+
func ValidateYAMLAgainstLatestConfig(yNew, yLatest []byte) error {
617+
var n LimaYAML
618+
619+
// Load the latest YAML and fill in defaults
620+
l, err := LoadWithWarnings(yLatest, "")
621+
if err != nil {
622+
return err
623+
}
624+
if err := Unmarshal(yNew, &n, "Unmarshal new YAML bytes"); err != nil {
625+
return err
626+
}
627+
628+
// Handle editing the template without a disk value
629+
if n.Disk == nil || l.Disk == nil {
630+
return nil
631+
}
632+
633+
// Disk value must be provided, as it is required when creating an instance.
634+
nDisk, err := units.RAMInBytes(*n.Disk)
635+
if err != nil {
636+
return err
637+
}
638+
lDisk, err := units.RAMInBytes(*l.Disk)
639+
if err != nil {
640+
return err
641+
}
642+
643+
// Reject shrinking disk
644+
if nDisk < lDisk {
645+
return fmt.Errorf("field `disk`: shrinking the disk (%v --> %v) is not supported", *l.Disk, *n.Disk)
646+
}
647+
648+
return nil
649+
}

pkg/limayaml/validate_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package limayaml
55

66
import (
7+
"errors"
78
"testing"
89

910
"gotest.tools/v3/assert"
@@ -264,3 +265,55 @@ provision:
264265
"field `provision[0].mode` must one of \"system\", \"user\", \"boot\", \"data\", \"dependency\", or \"ansible\"\n"+
265266
"field `provision[1].path` must not be empty when mode is \"data\"")
266267
}
268+
269+
func TestValidateYAMLAgainstLatestConfig(t *testing.T) {
270+
tests := []struct {
271+
name string
272+
yNew string
273+
yLatest string
274+
wantErr error
275+
}{
276+
{
277+
name: "Valid disk size unchanged",
278+
yNew: `disk: 100GiB`,
279+
yLatest: `disk: 100GiB`,
280+
},
281+
{
282+
name: "Valid disk size increased",
283+
yNew: `disk: 200GiB`,
284+
yLatest: `disk: 100GiB`,
285+
},
286+
{
287+
name: "No disk field in both YAMLs",
288+
yNew: ``,
289+
yLatest: ``,
290+
},
291+
{
292+
name: "No disk field in new YAMLs",
293+
yNew: ``,
294+
yLatest: `disk: 100GiB`,
295+
},
296+
{
297+
name: "No disk field in latest YAMLs",
298+
yNew: `disk: 100GiB`,
299+
yLatest: ``,
300+
},
301+
{
302+
name: "Disk size shrunk",
303+
yNew: `disk: 50GiB`,
304+
yLatest: `disk: 100GiB`,
305+
wantErr: errors.New("field `disk`: shrinking the disk (100GiB --> 50GiB) is not supported"),
306+
},
307+
}
308+
309+
for _, tt := range tests {
310+
t.Run(tt.name, func(t *testing.T) {
311+
err := ValidateYAMLAgainstLatestConfig([]byte(tt.yNew), []byte(tt.yLatest))
312+
if tt.wantErr == nil {
313+
assert.NilError(t, err)
314+
} else {
315+
assert.Error(t, err, tt.wantErr.Error())
316+
}
317+
})
318+
}
319+
}

0 commit comments

Comments
 (0)