@@ -12,11 +12,14 @@ import (
12
12
"os"
13
13
"path"
14
14
"path/filepath"
15
+ "regexp"
16
+ "runtime"
15
17
"strings"
16
18
"unicode"
17
19
18
20
"github.com/containerd/containerd/identifiers"
19
21
"github.com/lima-vm/lima/pkg/ioutilx"
22
+ "github.com/lima-vm/lima/pkg/limayaml"
20
23
"github.com/lima-vm/lima/pkg/templatestore"
21
24
"github.com/sirupsen/logrus"
22
25
)
@@ -30,6 +33,10 @@ func Read(ctx context.Context, name, locator string) (*Template, error) {
30
33
Locator : locator ,
31
34
}
32
35
36
+ if imageTemplate (tmpl , locator ) {
37
+ return tmpl , nil
38
+ }
39
+
33
40
isTemplateURL , templateURL := SeemsTemplateURL (locator )
34
41
switch {
35
42
case isTemplateURL :
@@ -121,6 +128,97 @@ func Read(ctx context.Context, name, locator string) (*Template, error) {
121
128
return tmpl , nil
122
129
}
123
130
131
+ // Locators with an image file format extension, optionally followed by a compression method.
132
+ // This regex is also used to remove the file format suffix from the instance name.
133
+ var imageURLRegex = regexp .MustCompile (`\.(img|qcow2|raw|iso)(\.(gz|xz|bz2|zstd))?$` )
134
+
135
+ // Image architecture will be guessed based on the presence of arch keywords.
136
+ var archKeywords = map [string ]limayaml.Arch {
137
+ "aarch64" : limayaml .AARCH64 ,
138
+ "amd64" : limayaml .X8664 ,
139
+ "arm64" : limayaml .AARCH64 ,
140
+ "armhf" : limayaml .ARMV7L ,
141
+ "armv7l" : limayaml .ARMV7L ,
142
+ "riscv64" : limayaml .RISCV64 ,
143
+ "x86_64" : limayaml .X8664 ,
144
+ }
145
+
146
+ // These generic tags will be stripped from an image name before turning it into an instance name.
147
+ var genericTags = []string {
148
+ "base" , // Fedora, Rocky
149
+ "cloud" , // Fedora, openSUSE
150
+ "cloudimg" , // Ubuntu, Arch
151
+ "cloudinit" , // Alpine
152
+ "daily" , // Debian
153
+ "default" , // Gentoo
154
+ "generic" , // Fedora
155
+ "genericcloud" , // CentOS, Debian, Rocky, Alma
156
+ "kvm" , // Oracle
157
+ "latest" , // Gentoo, CentOS, Rocky, Alma
158
+ "linux" , // Arch
159
+ "minimal" , // openSUSE
160
+ "openstack" , // Gentoo
161
+ "server" , // Ubuntu
162
+ "std" , // Alpine-Lima
163
+ "stream" , // CentOS
164
+ "uefi" , // Alpine
165
+ "vm" , // openSUSE
166
+ }
167
+
168
+ // imageTemplate checks if the locator specifies an image URL.
169
+ // It will create a minimal template with the image URL and arch derived from the image name
170
+ // and also set the default instance name to the image name, but stripped of generic tags.
171
+ func imageTemplate (tmpl * Template , locator string ) bool {
172
+ if ! imageURLRegex .MatchString (locator ) {
173
+ return false
174
+ }
175
+
176
+ var imageArch limayaml.Arch
177
+ for keyword , arch := range archKeywords {
178
+ pattern := fmt .Sprintf (`\b%s\b` , keyword )
179
+ if regexp .MustCompile (pattern ).MatchString (locator ) {
180
+ imageArch = arch
181
+ break
182
+ }
183
+ }
184
+ if imageArch == "" {
185
+ imageArch = limayaml .NewArch (runtime .GOARCH )
186
+ logrus .Warnf ("cannot determine image arch from URL %q; assuming %q" , locator , imageArch )
187
+ }
188
+ template := `arch: %q
189
+ images:
190
+ - location: %q
191
+ arch: %q
192
+ `
193
+ tmpl .Bytes = []byte (fmt .Sprintf (template , imageArch , locator , imageArch ))
194
+ tmpl .Name = InstNameFromImageURL (locator , imageArch )
195
+ return true
196
+ }
197
+
198
+ func InstNameFromImageURL (locator , imageArch string ) string {
199
+ // We intentionally call both path.Base and filepath.Base in case we are running on Windows.
200
+ name := strings .ToLower (filepath .Base (path .Base (locator )))
201
+ // Remove file format and compression file types
202
+ name = imageURLRegex .ReplaceAllString (name , "" )
203
+ // The Alpine "nocloud_" prefix does not fit the genericTags pattern
204
+ name = strings .TrimPrefix (name , "nocloud_" )
205
+ for _ , tag := range genericTags {
206
+ re := regexp .MustCompile (fmt .Sprintf (`[-_.]%s\b` , tag ))
207
+ name = re .ReplaceAllString (name , "" )
208
+ }
209
+ // Remove imageArch as well if it is the native arch
210
+ if limayaml .IsNativeArch (imageArch ) {
211
+ re := regexp .MustCompile (fmt .Sprintf (`[-_.]%s\b` , imageArch ))
212
+ name = re .ReplaceAllString (name , "" )
213
+ }
214
+ // Remove timestamps from name: 8 digit date, optionally followed by
215
+ // a delimiter and one or more digits before a word boundary
216
+ name = regexp .MustCompile (`[-_.]20\d{6}([-_.]\d+)?\b` ).ReplaceAllString (name , "" )
217
+ // Normalize archlinux name
218
+ name = regexp .MustCompile (`^arch\b` ).ReplaceAllString (name , "archlinux" )
219
+ return name
220
+ }
221
+
124
222
func SeemsTemplateURL (arg string ) (bool , * url.URL ) {
125
223
u , err := url .Parse (arg )
126
224
if err != nil {
0 commit comments