Skip to content

Commit 0deea45

Browse files
authored
Merge pull request #1709 from AkihiroSuda/fix-1702
limactl start: apply edit flags for existing instances
2 parents edf5d59 + 08012d1 commit 0deea45

File tree

4 files changed

+77
-32
lines changed

4 files changed

+77
-32
lines changed

cmd/limactl/edit.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"os"
88
"path/filepath"
9-
"strings"
109

1110
"github.com/AlecAivazis/survey/v2"
1211
"github.com/lima-vm/lima/cmd/limactl/editflags"
@@ -64,13 +63,13 @@ func editAction(cmd *cobra.Command, args []string) error {
6463
if err != nil {
6564
return err
6665
}
67-
yqExprs, err := editflags.YQExpressions(flags)
66+
yqExprs, err := editflags.YQExpressions(flags, false)
6867
if err != nil {
6968
return err
7069
}
7170
var yBytes []byte
7271
if len(yqExprs) > 0 {
73-
yq := strings.Join(yqExprs, " | ")
72+
yq := yqutil.Join(yqExprs)
7473
yBytes, err = yqutil.EvaluateExpression(yq, yContent)
7574
if err != nil {
7675
return err

cmd/limactl/editflags/editflags.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,16 @@ func defaultExprFunc(expr string) func(v *flag.Flag) (string, error) {
9797
}
9898

9999
// YQExpressions returns YQ expressions.
100-
func YQExpressions(flags *flag.FlagSet) ([]string, error) {
100+
func YQExpressions(flags *flag.FlagSet, newInstance bool) ([]string, error) {
101101
type def struct {
102-
flagName string
103-
exprFunc func(*flag.Flag) (string, error)
104-
experimental bool
102+
flagName string
103+
exprFunc func(*flag.Flag) (string, error)
104+
onlyValidForNewInstances bool
105+
experimental bool
105106
}
106107
d := defaultExprFunc
107108
defs := []def{
108-
{"cpus", d(".cpus = %s"), false},
109+
{"cpus", d(".cpus = %s"), false, false},
109110
{"dns",
110111
func(_ *flag.Flag) (string, error) {
111112
ipSlice, err := flags.GetIPSlice("dns")
@@ -123,8 +124,9 @@ func YQExpressions(flags *flag.FlagSet) ([]string, error) {
123124
logrus.Warnf("Disabling HostResolver, as custom DNS addresses (%v) are specified", ipSlice)
124125
return expr, nil
125126
},
127+
false,
126128
false},
127-
{"memory", d(".memory = \"%sGiB\""), false},
129+
{"memory", d(".memory = \"%sGiB\""), false, false},
128130
{"mount",
129131
func(_ *flag.Flag) (string, error) {
130132
ss, err := flags.GetStringSlice("mount")
@@ -143,9 +145,10 @@ func YQExpressions(flags *flag.FlagSet) ([]string, error) {
143145
expr += `] | .mounts |= unique_by(.location)`
144146
return expr, nil
145147
},
148+
false,
146149
false},
147-
{"mount-type", d(".mountType = %q"), false},
148-
{"mount-writable", d(".mounts[].writable = %s"), false},
150+
{"mount-type", d(".mountType = %q"), false, false},
151+
{"mount-writable", d(".mounts[].writable = %s"), false, false},
149152
{"network",
150153
func(_ *flag.Flag) (string, error) {
151154
ss, err := flags.GetStringSlice("network")
@@ -171,6 +174,7 @@ func YQExpressions(flags *flag.FlagSet) ([]string, error) {
171174
expr += `] | .networks |= unique_by(.lima)`
172175
return expr, nil
173176
},
177+
false,
174178
true},
175179
{"rosetta",
176180
func(_ *flag.Flag) (string, error) {
@@ -180,8 +184,9 @@ func YQExpressions(flags *flag.FlagSet) ([]string, error) {
180184
}
181185
return fmt.Sprintf(".rosetta.enabled = %v | .rosetta.binfmt = %v", b, b), nil
182186
},
187+
false,
183188
true},
184-
{"set", d("%s"), true},
189+
{"set", d("%s"), false, true},
185190
{"video",
186191
func(_ *flag.Flag) (string, error) {
187192
b, err := flags.GetBool("video")
@@ -193,8 +198,9 @@ func YQExpressions(flags *flag.FlagSet) ([]string, error) {
193198
}
194199
return ".video.display = \"none\"", nil
195200
},
201+
false,
196202
true},
197-
{"arch", d(".arch = %q"), false},
203+
{"arch", d(".arch = %q"), true, false},
198204
{"containerd",
199205
func(_ *flag.Flag) (string, error) {
200206
s, err := flags.GetString("containerd")
@@ -214,10 +220,11 @@ func YQExpressions(flags *flag.FlagSet) ([]string, error) {
214220
return "", fmt.Errorf(`expected one of ["user", "system", "user+system", "none"], got %q`, s)
215221
}
216222
},
223+
true,
217224
false},
218225

219-
{"disk", d(".disk= \"%sGiB\""), false},
220-
{"vm-type", d(".vmType = %q"), false},
226+
{"disk", d(".disk= \"%sGiB\""), true, false},
227+
{"vm-type", d(".vmType = %q"), true, false},
221228
}
222229
var exprs []string
223230
for _, def := range defs {
@@ -226,6 +233,11 @@ func YQExpressions(flags *flag.FlagSet) ([]string, error) {
226233
if def.experimental {
227234
logrus.Warnf("`--%s` is experimental", def.flagName)
228235
}
236+
if def.onlyValidForNewInstances && !newInstance {
237+
logrus.Warnf("`--%s` is not applicable to an existing instance (Hint: create a new instance with `limactl create --%s=%s --name=NAME`)",
238+
def.flagName, def.flagName, v.Value.String())
239+
continue
240+
}
229241
expr, err := def.exprFunc(v)
230242
if err != nil {
231243
return exprs, fmt.Errorf("error while processing flag %q: %w", def.flagName, err)

cmd/limactl/start.go

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,6 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
121121
return nil, err
122122
}
123123

124-
yqExprs, err := editflags.YQExpressions(flags)
125-
if err != nil {
126-
return nil, err
127-
}
128-
if len(yqExprs) > 0 {
129-
st.yq = strings.Join(yqExprs, " | ")
130-
}
131124
const yBytesLimit = 4 * 1024 * 1024 // 4MiB
132125

133126
if ok, u := guessarg.SeemsTemplateURL(arg); ok {
@@ -231,6 +224,17 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
231224
if arg == "" {
232225
logrus.Infof("Hint: To create another instance, run the following command: limactl create --name=NAME template://default")
233226
}
227+
yqExprs, err := editflags.YQExpressions(flags, false)
228+
if err != nil {
229+
return nil, err
230+
}
231+
if len(yqExprs) > 0 {
232+
yq := yqutil.Join(yqExprs)
233+
inst, err = applyYQExpressionToExistingInstance(inst, yq)
234+
if err != nil {
235+
return nil, fmt.Errorf("failed to apply yq expression %q to instance %q: %w", yq, st.instName, err)
236+
}
237+
}
234238
return inst, nil
235239
}
236240
if !errors.Is(err, os.ErrNotExist) {
@@ -247,22 +251,48 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
247251
}
248252
}
249253

254+
yqExprs, err := editflags.YQExpressions(flags, true)
255+
if err != nil {
256+
return nil, err
257+
}
258+
yq := yqutil.Join(yqExprs)
250259
if tty {
251260
var err error
252-
st, err = chooseNextCreatorState(st)
261+
st, err = chooseNextCreatorState(st, yq)
253262
if err != nil {
254263
return nil, err
255264
}
256265
} else {
257266
logrus.Info("Terminal is not available, proceeding without opening an editor")
258-
if err := modifyInPlace(st); err != nil {
267+
if err := modifyInPlace(st, yq); err != nil {
259268
return nil, err
260269
}
261270
}
262271
saveBrokenEditorBuffer := tty
263272
return createInstance(st, saveBrokenEditorBuffer)
264273
}
265274

275+
func applyYQExpressionToExistingInstance(inst *store.Instance, yq string) (*store.Instance, error) {
276+
if strings.TrimSpace(yq) == "" {
277+
return inst, nil
278+
}
279+
filePath := filepath.Join(inst.Dir, filenames.LimaYAML)
280+
yContent, err := os.ReadFile(filePath)
281+
if err != nil {
282+
return nil, err
283+
}
284+
logrus.Debugf("Applying yq expression %q to an existing instance %q", yq, inst.Name)
285+
yBytes, err := yqutil.EvaluateExpression(yq, yContent)
286+
if err != nil {
287+
return nil, err
288+
}
289+
if err := os.WriteFile(filePath, yBytes, 0644); err != nil {
290+
return nil, err
291+
}
292+
// Reload
293+
return store.Inspect(inst.Name)
294+
}
295+
266296
func createInstance(st *creatorState, saveBrokenEditorBuffer bool) (*store.Instance, error) {
267297
if st.instName == "" {
268298
return nil, errors.New("got empty st.instName")
@@ -313,24 +343,20 @@ func createInstance(st *creatorState, saveBrokenEditorBuffer bool) (*store.Insta
313343
type creatorState struct {
314344
instName string // instance name
315345
yBytes []byte // yaml bytes
316-
yq string // yq expression
317346
}
318347

319-
func modifyInPlace(st *creatorState) error {
320-
if st.yq == "" {
321-
return nil
322-
}
323-
out, err := yqutil.EvaluateExpression(st.yq, st.yBytes)
348+
func modifyInPlace(st *creatorState, yq string) error {
349+
out, err := yqutil.EvaluateExpression(yq, st.yBytes)
324350
if err != nil {
325351
return err
326352
}
327353
st.yBytes = out
328354
return nil
329355
}
330356

331-
func chooseNextCreatorState(st *creatorState) (*creatorState, error) {
357+
func chooseNextCreatorState(st *creatorState, yq string) (*creatorState, error) {
332358
for {
333-
if err := modifyInPlace(st); err != nil {
359+
if err := modifyInPlace(st, yq); err != nil {
334360
logrus.WithError(err).Warn("Failed to evaluate yq expression")
335361
return st, err
336362
}

pkg/yqutil/yqutil.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"fmt"
66
"os"
7+
"strings"
78

89
"github.com/mikefarah/yq/v4/pkg/yqlib"
910
"github.com/sirupsen/logrus"
@@ -65,3 +66,10 @@ func EvaluateExpression(expression string, content []byte) ([]byte, error) {
6566
return out.Bytes(), nil
6667

6768
}
69+
70+
func Join(yqExprs []string) string {
71+
if len(yqExprs) == 0 {
72+
return ""
73+
}
74+
return strings.Join(yqExprs, " | ")
75+
}

0 commit comments

Comments
 (0)