Skip to content

Commit 8ed0180

Browse files
committed
Switch to RFC compliant opaque template:name URLs
The old style template://name URLs are malformed because they do not contain a valid AUTHORITY (host name), but treat the host name as part of the path. They are still fully supported, but will emit a warning. Signed-off-by: Jan Dubois <[email protected]>
1 parent 1958c01 commit 8ed0180

File tree

8 files changed

+79
-53
lines changed

8 files changed

+79
-53
lines changed

cmd/limactl/template.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ func newValidateCommand() *cobra.Command {
5252
return validateCommand
5353
}
5454

55-
var templateCopyExample = ` Template locators are local files, file://, https://, or template:// URLs
55+
var templateCopyExample = ` Template locators are local files, file://, https://, or template: URLs
5656
5757
# Copy default template to STDOUT
58-
limactl template copy template://default -
58+
limactl template copy template:default -
5959
6060
# Copy template from web location to local file and embed all external references
61-
# (this does not embed template:// references)
61+
# (this does not embed template: references)
6262
limactl template copy --embed https://example.com/lima.yaml mighty-machine.yaml
6363
`
6464

@@ -177,10 +177,10 @@ External references are embedded and default values are filled in
177177
before the YQ expression is evaluated.
178178
179179
Example:
180-
limactl template yq template://default '.images[].location'
180+
limactl template yq template:default '.images[].location'
181181
182182
The example command is equivalent to using an external yq command like this:
183-
limactl template copy --fill template://default - | yq '.images[].location'
183+
limactl template copy --fill template:default - | yq '.images[].location'
184184
`
185185

186186
func newTemplateYQCommand() *cobra.Command {

pkg/limainfo/limainfo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type GuestAgent struct {
4848
}
4949

5050
// New returns a LimaInfo object with the Lima version, a list of all Templates and their location,
51-
// the DefaultTemplate corresponding to template://default with all defaults filled in, the
51+
// the DefaultTemplate corresponding to template:default with all defaults filled in, the
5252
// LimaHome location, a list of all supported VMTypes, and a map of GuestAgents for each architecture.
5353
func New(ctx context.Context) (*LimaInfo, error) {
5454
b, err := templatestore.Read(templatestore.Default)

pkg/limatmpl/abs.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ func basePath(locator string) (string, error) {
8989
// Single-letter schemes will be drive names on Windows, e.g. "c:/foo"
9090
if err == nil && len(u.Scheme) > 1 {
9191
// path.Dir("") returns ".", which must be removed for url.JoinPath() to do the right thing later
92+
if u.Opaque != "" {
93+
return u.Scheme + ":" + strings.TrimSuffix(path.Dir(u.Opaque), "."), nil
94+
}
9295
return u.Scheme + "://" + strings.TrimSuffix(path.Dir(path.Join(u.Host, u.Path)), "."), nil
9396
}
9497
base, err := filepath.Abs(filepath.Dir(locator))
@@ -132,6 +135,10 @@ func absPath(locator, basePath string) (string, error) {
132135
return "", err
133136
}
134137
if len(u.Scheme) > 1 {
138+
// Treat empty "template:" URL as opaque
139+
if u.Opaque != "" || (u.Scheme == "template" && u.Host == "") {
140+
return u.Scheme + ":" + path.Join(u.Opaque, locator), nil
141+
}
135142
return u.JoinPath(locator).String(), nil
136143
}
137144
locator = filepath.Join(basePath, locator)

pkg/limatmpl/abs_test.go

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,67 +23,73 @@ type useAbsLocatorsTestCase struct {
2323
var useAbsLocatorsTestCases = []useAbsLocatorsTestCase{
2424
{
2525
"Template without base or script file",
26-
"template://foo",
26+
"template:foo",
2727
`arch: aarch64`,
2828
`arch: aarch64`,
2929
},
3030
{
3131
"Single string base template",
32+
"template:foo",
33+
`base: bar.yaml`,
34+
`base: template:bar.yaml`,
35+
},
36+
{
37+
"Legacy template:// base template",
3238
"template://foo",
3339
`base: bar.yaml`,
34-
`base: template://bar.yaml`,
40+
`base: template:bar.yaml`,
3541
},
3642
{
3743
"Flow style array of one base template",
38-
"template://foo",
44+
"template:foo",
3945
`base: [{url: bar.yaml, digest: deadbeef}]`,
4046
// not sure why the quotes around the URL were added; maybe because we don't copy the style from the source
41-
`base: [{url: 'template://bar.yaml', digest: deadbeef}]`,
47+
`base: [{url: 'template:bar.yaml', digest: deadbeef}]`,
4248
},
4349
{
4450
"Flow style array of sequence of two base URLs",
45-
"template://foo",
51+
"template:foo",
4652
`base: [bar.yaml, baz.yaml]`,
47-
`base: ['template://bar.yaml', 'template://baz.yaml']`,
53+
`base: ['template:bar.yaml', 'template:baz.yaml']`,
4854
},
4955
{
5056
"Flow style array of sequence of two base locator objects",
51-
"template://foo",
57+
"template:foo",
5258
`base: [{url: bar.yaml, digest: deadbeef}, {url: baz.yaml, digest: decafbad}]`,
53-
`base: [{url: 'template://bar.yaml', digest: deadbeef}, {url: 'template://baz.yaml', digest: decafbad}]`,
59+
`base: [{url: 'template:bar.yaml', digest: deadbeef}, {url: 'template:baz.yaml', digest: decafbad}]`,
5460
},
5561
{
5662
"Block style array of one base template",
57-
"template://foo",
63+
"template:foo",
5864
`
5965
base:
6066
- bar.yaml
6167
`,
6268
`
6369
base:
64-
- template://bar.yaml`,
70+
- template:bar.yaml`,
6571
},
6672
{
6773
"Block style of four base templates",
68-
"template://foo",
74+
"template:foo",
6975
`
7076
base:
7177
- bar.yaml
72-
- template://my
78+
- template:my
7379
- https://example.com/my.yaml
7480
- baz.yaml
7581
`,
7682
`
7783
base:
78-
- template://bar.yaml
79-
- template://my
84+
- template:bar.yaml
85+
- template:my
8086
- https://example.com/my.yaml
81-
- template://baz.yaml
87+
- template:baz.yaml
8288
`,
8389
},
8490
{
8591
"Provisioning and probe scripts",
86-
"template://experimental/foo",
92+
"template:experimental/foo",
8793
`
8894
provision:
8995
- mode: user
@@ -101,15 +107,15 @@ probes:
101107
`
102108
provision:
103109
- mode: user
104-
file: template://experimental/userscript.sh
110+
file: template:experimental/userscript.sh
105111
- mode: system
106112
file:
107-
url: template://experimental/systemscript.sh
113+
url: template:experimental/systemscript.sh
108114
digest: abc123
109115
probes:
110-
- file: template://experimental/probe.sh
116+
- file: template:experimental/probe.sh
111117
- file:
112-
url: template://experimental/probe.sh
118+
url: template:experimental/probe.sh
113119
digest: digest
114120
`,
115121
},
@@ -153,15 +159,15 @@ func TestBasePath(t *testing.T) {
153159
})
154160

155161
t.Run("", func(t *testing.T) {
156-
actual, err := basePath("template://foo")
162+
actual, err := basePath("template:foo")
157163
assert.NilError(t, err)
158-
assert.Equal(t, actual, "template://")
164+
assert.Equal(t, actual, "template:")
159165
})
160166

161167
t.Run("", func(t *testing.T) {
162-
actual, err := basePath("template://foo/bar")
168+
actual, err := basePath("template:foo/bar")
163169
assert.NilError(t, err)
164-
assert.Equal(t, actual, "template://foo")
170+
assert.Equal(t, actual, "template:foo")
165171
})
166172

167173
t.Run("", func(t *testing.T) {
@@ -216,9 +222,9 @@ func TestAbsPath(t *testing.T) {
216222
})
217223

218224
t.Run("", func(t *testing.T) {
219-
actual, err := absPath("template://foo", volume+"/root")
225+
actual, err := absPath("template:foo", volume+"/root")
220226
assert.NilError(t, err)
221-
assert.Equal(t, actual, "template://foo")
227+
assert.Equal(t, actual, "template:foo")
222228
})
223229

224230
t.Run("", func(t *testing.T) {
@@ -272,15 +278,15 @@ func TestAbsPath(t *testing.T) {
272278
}
273279

274280
t.Run("", func(t *testing.T) {
275-
actual, err := absPath("foo", "template://")
281+
actual, err := absPath("foo", "template:")
276282
assert.NilError(t, err)
277-
assert.Equal(t, actual, "template://foo")
283+
assert.Equal(t, actual, "template:foo")
278284
})
279285

280286
t.Run("", func(t *testing.T) {
281-
actual, err := absPath("bar", "template://foo")
287+
actual, err := absPath("bar", "template:foo")
282288
assert.NilError(t, err)
283-
assert.Equal(t, actual, "template://foo/bar")
289+
assert.Equal(t, actual, "template:foo/bar")
284290
})
285291

286292
t.Run("", func(t *testing.T) {

pkg/limatmpl/embed.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,15 @@ func (tmpl *Template) embedAllBases(ctx context.Context, embedAll, defaultBase b
7676
}
7777
isTemplate, _ := SeemsTemplateURL(baseLocator.URL)
7878
if isTemplate && !embedAll {
79-
// Once we skip a template:// URL we can no longer embed any other base template
79+
// Once we skip a template: URL we can no longer embed any other base template
8080
for i := 1; i < len(tmpl.Config.Base); i++ {
8181
isTemplate, _ = SeemsTemplateURL(tmpl.Config.Base[i].URL)
8282
if !isTemplate {
8383
return fmt.Errorf("cannot embed template %q after not embedding %q", tmpl.Config.Base[i].URL, baseLocator.URL)
8484
}
8585
}
8686
break
87-
// TODO should we track embedding of template:// URLs so we can warn if we embed a non-template:// URL afterwards?
87+
// TODO should we track embedding of template: URLs so we can warn if we embed a non-template: URL afterwards?
8888
}
8989

9090
if seen[baseLocator.URL] {

pkg/limatmpl/embed_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,24 +181,24 @@ mounts:
181181
`mounts: [{location: loc1}, {location: loc1, mountPoint: loc2, writable: true, sshfs: {followSymlinks: true}}, {location: loc1, mountPoint: loc3, writable: true}]`,
182182
},
183183
{
184-
"template:// URLs are not embedded when embedAll is false",
184+
"template: URLs are not embedded when embedAll is false",
185185
// also tests file.url format
186186
``,
187187
`
188-
base: template://default
188+
base: template:default
189189
provision:
190190
- file:
191-
url: template://provision.sh
191+
url: template:provision.sh
192192
probes:
193193
- file:
194-
url: template://probe.sh
194+
url: template:probe.sh
195195
`,
196196
`
197-
base: template://default
197+
base: template:default
198198
provision:
199-
- file: template://provision.sh
199+
- file: template:provision.sh
200200
probes:
201-
- file: template://probe.sh
201+
- file: template:probe.sh
202202
`,
203203
},
204204
{
@@ -214,18 +214,18 @@ vmType: qemu
214214
`base template loop detected`,
215215
},
216216
{
217-
"ERROR All bases following template:// bases must be template:// URLs too when embedAll is false",
217+
"ERROR All bases following template: bases must be template: URLs too when embedAll is false",
218218
``,
219-
`base: [template://default, base1.yaml]`,
219+
`base: [template:default, base1.yaml]`,
220220
"after not embedding",
221221
},
222222
{
223-
"ERROR All bases following template:// bases must be template:// URLs too when embedAll is false",
223+
"ERROR All bases following template: bases must be template: URLs too when embedAll is false",
224224
``,
225225
`
226226
base: [base1.yaml, base2.yaml]
227227
---
228-
base: template://default
228+
base: template:default
229229
---
230230
base: baseX.yaml`,
231231
"after not embedding",

pkg/limatmpl/locator.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,10 @@ func SeemsTemplateURL(arg string) (isTemplate bool, templateName string) {
248248
return false, ""
249249
}
250250
if u.Scheme == "template" {
251-
return true, path.Join(u.Host, u.Path)
251+
if u.Opaque == "" {
252+
return true, path.Join(u.Host, u.Path)
253+
}
254+
return true, u.Opaque
252255
}
253256
return false, ""
254257
}
@@ -299,6 +302,16 @@ func TransformCustomURL(ctx context.Context, locator string) (string, error) {
299302
return locator, nil
300303
}
301304

305+
if u.Scheme == "template" {
306+
if u.Opaque != "" {
307+
return locator, nil
308+
}
309+
// Fix malformed "template:" URLs.
310+
newLocator := "template:" + path.Join(u.Host, u.Path)
311+
logrus.Warnf("Template locator %q should be written %q", locator, newLocator)
312+
return newLocator, nil
313+
}
314+
302315
plugin, err := plugins.Find("url-" + u.Scheme)
303316
if err != nil {
304317
return "", err

pkg/limatmpl/locator_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func TestInstNameFromImageURL(t *testing.T) {
6464
}
6565

6666
func TestSeemsTemplateURL(t *testing.T) {
67-
arg := "template://foo/bar"
67+
arg := "template:foo/bar"
6868
t.Run(arg, func(t *testing.T) {
6969
is, name := limatmpl.SeemsTemplateURL(arg)
7070
assert.Equal(t, is, true)
@@ -96,7 +96,7 @@ func TestSeemsHTTPURL(t *testing.T) {
9696
}
9797
notHTTPURLs := []string{
9898
"file:///foo",
99-
"template://foo",
99+
"template:foo",
100100
"foo",
101101
}
102102
for _, arg := range notHTTPURLs {
@@ -114,7 +114,7 @@ func TestSeemsFileURL(t *testing.T) {
114114
notFileURLs := []string{
115115
"http://foo",
116116
"https://foo",
117-
"template://foo",
117+
"template:foo",
118118
"foo",
119119
}
120120
for _, arg := range notFileURLs {

0 commit comments

Comments
 (0)