Skip to content

Commit 1958c01

Browse files
committed
Add a plugin mechanism to provide custom locator URL schemes
The template reader will attempt to call `limactl-url-$SCHEME` to rewrite a custom URL as either a local filename, or a URL with a supported URL like https. Signed-off-by: Jan Dubois <[email protected]>
1 parent 1055f75 commit 1958c01

File tree

4 files changed

+89
-2
lines changed

4 files changed

+89
-2
lines changed

cmd/limactl/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,10 @@ func addPluginCommands(rootCmd *cobra.Command) {
254254
// unreachable
255255
},
256256
}
257-
257+
// Don't show the url scheme helper in the help output.
258+
if strings.HasPrefix(plugin.Name, "url-") {
259+
pluginCmd.Hidden = true
260+
}
258261
rootCmd.AddCommand(pluginCmd)
259262
}
260263
}

cmd/limactl/template.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func newTemplateCommand() *cobra.Command {
4040
newTemplateCopyCommand(),
4141
newTemplateValidateCommand(),
4242
newTemplateYQCommand(),
43+
newTemplateURLCommand(),
4344
)
4445
return templateCommand
4546
}
@@ -281,3 +282,22 @@ func templateValidateAction(cmd *cobra.Command, args []string) error {
281282

282283
return nil
283284
}
285+
286+
func newTemplateURLCommand() *cobra.Command {
287+
templateURLCommand := &cobra.Command{
288+
Use: "url CUSTOM_URL",
289+
Short: "Transform custom template URLs to regular file or https URLs",
290+
Args: WrapArgsError(cobra.ExactArgs(1)),
291+
RunE: templateURLAction,
292+
}
293+
return templateURLCommand
294+
}
295+
296+
func templateURLAction(cmd *cobra.Command, args []string) error {
297+
url, err := limatmpl.TransformCustomURL(cmd.Context(), args[0])
298+
if err != nil {
299+
return err
300+
}
301+
_, err = fmt.Fprintln(cmd.OutOrStdout(), url)
302+
return err
303+
}

pkg/limatmpl/locator.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ package limatmpl
55

66
import (
77
"context"
8+
"errors"
89
"fmt"
910
"io"
1011
"net/http"
1112
"net/url"
1213
"os"
14+
"os/exec"
1315
"path"
1416
"path/filepath"
1517
"regexp"
@@ -22,18 +24,23 @@ import (
2224
"github.com/lima-vm/lima/v2/pkg/ioutilx"
2325
"github.com/lima-vm/lima/v2/pkg/limatype"
2426
"github.com/lima-vm/lima/v2/pkg/limayaml"
27+
"github.com/lima-vm/lima/v2/pkg/plugins"
2528
"github.com/lima-vm/lima/v2/pkg/templatestore"
2629
)
2730

2831
const yBytesLimit = 4 * 1024 * 1024 // 4MiB
2932

3033
func Read(ctx context.Context, name, locator string) (*Template, error) {
31-
var err error
3234
tmpl := &Template{
3335
Name: name,
3436
Locator: locator,
3537
}
3638

39+
locator, err := TransformCustomURL(ctx, locator)
40+
if err != nil {
41+
return nil, err
42+
}
43+
3744
if imageTemplate(tmpl, locator) {
3845
return tmpl, nil
3946
}
@@ -285,3 +292,45 @@ func InstNameFromYAMLPath(yamlPath string) (string, error) {
285292
}
286293
return s, nil
287294
}
295+
296+
func TransformCustomURL(ctx context.Context, locator string) (string, error) {
297+
u, err := url.Parse(locator)
298+
if err != nil || len(u.Scheme) <= 1 {
299+
return locator, nil
300+
}
301+
302+
plugin, err := plugins.Find("url-" + u.Scheme)
303+
if err != nil {
304+
return "", err
305+
}
306+
if plugin == nil {
307+
return locator, nil
308+
}
309+
310+
currentPath := os.Getenv("PATH")
311+
defer os.Setenv("PATH", currentPath)
312+
err = plugins.UpdatePath()
313+
if err != nil {
314+
return "", err
315+
}
316+
317+
cmd := exec.CommandContext(ctx, plugin.Path, strings.TrimPrefix(u.String(), u.Scheme+":"))
318+
cmd.Env = os.Environ()
319+
320+
stdout, err := cmd.Output()
321+
if err != nil {
322+
var exitErr *exec.ExitError
323+
if errors.As(err, &exitErr) {
324+
stderrMsg := string(exitErr.Stderr)
325+
if stderrMsg != "" {
326+
return "", fmt.Errorf("command %q failed: %s", cmd.String(), strings.TrimSpace(stderrMsg))
327+
}
328+
}
329+
return "", fmt.Errorf("command %q failed: %w", cmd.String(), err)
330+
}
331+
newLocator := strings.TrimSpace(string(stdout))
332+
if newLocator != locator {
333+
logrus.Debugf("Custom locator %q replaced with %q", locator, newLocator)
334+
}
335+
return newLocator, nil
336+
}

pkg/plugins/plugins.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,21 @@ func extractDescFromScript(path string) string {
192192
return desc
193193
}
194194

195+
// Find locates a plugin by name and returns a pointer to a copy.
196+
func Find(name string) (*Plugin, error) {
197+
allPlugins, err := Discover()
198+
if err != nil {
199+
return nil, err
200+
}
201+
for _, plugin := range allPlugins {
202+
if name == plugin.Name {
203+
pluginCopy := plugin
204+
return &pluginCopy, nil
205+
}
206+
}
207+
return nil, nil
208+
}
209+
195210
func UpdatePath() error {
196211
pluginDirs := getPluginDirectories()
197212
newPath := strings.Join(pluginDirs, string(filepath.ListSeparator))

0 commit comments

Comments
 (0)