@@ -30,25 +30,46 @@ import (
30
30
31
31
func newStartCommand () * cobra.Command {
32
32
var startCommand = & cobra.Command {
33
- Use : "start NAME|FILE.yaml|URL" ,
33
+ Use : "start NAME|FILE.yaml|URL" ,
34
+ Example : `
35
+ Create an instance "default" (if not created yet) from the default Ubuntu template, and start it:
36
+ $ limactl start
37
+
38
+ Create an instance "default" from a template "docker":
39
+ $ limactl start --name=default template://docker
40
+
41
+ Create an instance "default" from a local file:
42
+ $ limactl start --name=default /usr/local/share/lima/examples/fedora.yaml
43
+
44
+ Create an instance "default" from a remote URL (use carefully, with a trustable source):
45
+ $ limactl start --name=default https://raw.githubusercontent.com/lima-vm/lima/master/examples/alpine.yaml
46
+ ` ,
34
47
Short : "Start an instance of Lima" ,
35
48
Args : cobra .MaximumNArgs (1 ),
36
49
ValidArgsFunction : startBashComplete ,
37
50
RunE : startAction ,
38
51
}
39
52
startCommand .Flags ().Bool ("tty" , isatty .IsTerminal (os .Stdout .Fd ()), "enable TUI interactions such as opening an editor, defaults to true when stdout is a terminal" )
53
+ startCommand .Flags ().String ("name" , "" , "override the instance name" )
40
54
return startCommand
41
55
}
42
56
43
- func readDefaultTemplate ( ) ([]byte , error ) {
57
+ func readTemplate ( name string ) ([]byte , error ) {
44
58
dir , err := usrlocalsharelima .Dir ()
45
59
if err != nil {
46
60
return nil , err
47
61
}
48
- defaultYAMLPath := filepath .Join (dir , "examples" , "default.yaml" )
62
+ if strings .Contains (name , string (os .PathSeparator )) {
63
+ return nil , fmt .Errorf ("invalid template name %q" , name )
64
+ }
65
+ defaultYAMLPath := filepath .Join (dir , "examples" , name + ".yaml" )
49
66
return os .ReadFile (defaultYAMLPath )
50
67
}
51
68
69
+ func readDefaultTemplate () ([]byte , error ) {
70
+ return readTemplate ("default" )
71
+ }
72
+
52
73
func loadOrCreateInstance (cmd * cobra.Command , args []string ) (* store.Instance , error ) {
53
74
var arg string
54
75
if len (args ) == 0 {
@@ -61,13 +82,29 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string) (*store.Instance, e
61
82
st = & creatorState {}
62
83
err error
63
84
)
85
+ st .instName , err = cmd .Flags ().GetString ("name" )
86
+ if err != nil {
87
+ return nil , err
88
+ }
64
89
const yBytesLimit = 4 * 1024 * 1024 // 4MiB
65
90
66
- if argSeemsHTTPURL (arg ) {
67
- st .instName , err = instNameFromURL (arg )
91
+ if ok , u := argSeemsTemplateURL (arg ); ok {
92
+ templateName := u .Host
93
+ logrus .Debugf ("interpreting argument %q as a template name %q" , arg , templateName )
94
+ if st .instName == "" {
95
+ st .instName = templateName
96
+ }
97
+ st .yBytes , err = readTemplate (templateName )
68
98
if err != nil {
69
99
return nil , err
70
100
}
101
+ } else if argSeemsHTTPURL (arg ) {
102
+ if st .instName == "" {
103
+ st .instName , err = instNameFromURL (arg )
104
+ if err != nil {
105
+ return nil , err
106
+ }
107
+ }
71
108
logrus .Debugf ("interpreting argument %q as a http url for instance %q" , arg , st .instName )
72
109
resp , err := http .Get (arg )
73
110
if err != nil {
@@ -79,9 +116,11 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string) (*store.Instance, e
79
116
return nil , err
80
117
}
81
118
} else if argSeemsFileURL (arg ) {
82
- st .instName , err = instNameFromURL (arg )
83
- if err != nil {
84
- return nil , err
119
+ if st .instName == "" {
120
+ st .instName , err = instNameFromURL (arg )
121
+ if err != nil {
122
+ return nil , err
123
+ }
85
124
}
86
125
logrus .Debugf ("interpreting argument %q as a file url for instance %q" , arg , st .instName )
87
126
r , err := os .Open (strings .TrimPrefix (arg , "file://" ))
@@ -94,9 +133,11 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string) (*store.Instance, e
94
133
return nil , err
95
134
}
96
135
} else if argSeemsYAMLPath (arg ) {
97
- st .instName , err = instNameFromYAMLPath (arg )
98
- if err != nil {
99
- return nil , err
136
+ if st .instName == "" {
137
+ st .instName , err = instNameFromYAMLPath (arg )
138
+ if err != nil {
139
+ return nil , err
140
+ }
100
141
}
101
142
logrus .Debugf ("interpreting argument %q as a file path for instance %q" , arg , st .instName )
102
143
r , err := os .Open (arg )
@@ -109,10 +150,13 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string) (*store.Instance, e
109
150
return nil , err
110
151
}
111
152
} else {
153
+ logrus .Debugf ("interpreting argument %q as an instance name" , arg )
154
+ if st .instName != "" && st .instName != arg {
155
+ return nil , fmt .Errorf ("instance name %q and CLI flag --name=%q cannot be specified together" , arg , st .instName )
156
+ }
112
157
st .instName = arg
113
- logrus .Debugf ("interpreting argument %q as an instance name %q" , arg , st .instName )
114
158
if err := identifiers .Validate (st .instName ); err != nil {
115
- return nil , fmt .Errorf ("argument must be either an instance name or a YAML file path, got %q: %w" , st .instName , err )
159
+ return nil , fmt .Errorf ("argument must be either an instance name, a YAML file path, or a URL , got %q: %w" , st .instName , err )
116
160
}
117
161
if inst , err := store .Inspect (st .instName ); err == nil {
118
162
logrus .Infof ("Using the existing instance %q" , st .instName )
@@ -121,6 +165,10 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string) (*store.Instance, e
121
165
if ! errors .Is (err , os .ErrNotExist ) {
122
166
return nil , err
123
167
}
168
+ if st .instName != DefaultInstanceName {
169
+ logrus .Infof ("Creating an instance %q from template://default (Not from template://%s)" , st .instName , st .instName )
170
+ logrus .Warnf ("This form is deprecated. Use `limactl start --name=%s template://default` instead" , st .instName )
171
+ }
124
172
// Read the default template for creating a new instance
125
173
st .yBytes , err = readDefaultTemplate ()
126
174
if err != nil {
@@ -402,6 +450,14 @@ func startAction(cmd *cobra.Command, args []string) error {
402
450
return start .Start (ctx , inst )
403
451
}
404
452
453
+ func argSeemsTemplateURL (arg string ) (bool , * url.URL ) {
454
+ u , err := url .Parse (arg )
455
+ if err != nil {
456
+ return false , u
457
+ }
458
+ return u .Scheme == "template" , u
459
+ }
460
+
405
461
func argSeemsHTTPURL (arg string ) bool {
406
462
u , err := url .Parse (arg )
407
463
if err != nil {
@@ -448,8 +504,13 @@ func instNameFromYAMLPath(yamlPath string) (string, error) {
448
504
}
449
505
450
506
func startBashComplete (cmd * cobra.Command , args []string , toComplete string ) ([]string , cobra.ShellCompDirective ) {
451
- instances , _ := bashCompleteInstanceNames (cmd )
452
- return instances , cobra .ShellCompDirectiveDefault
507
+ comp , _ := bashCompleteInstanceNames (cmd )
508
+ if templates , err := listTemplateYAMLs (); err == nil {
509
+ for _ , f := range templates {
510
+ comp = append (comp , "template://" + f .Name )
511
+ }
512
+ }
513
+ return comp , cobra .ShellCompDirectiveDefault
453
514
}
454
515
455
516
func readAtMaximum (r io.Reader , n int64 ) ([]byte , error ) {
0 commit comments