Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/apptainer.lima
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -eu
: "${APPTAINER_BINDPATH:=}"

if [ "$(limactl ls -q "$LIMA_INSTANCE" 2>/dev/null)" != "$LIMA_INSTANCE" ]; then
echo "instance \"$LIMA_INSTANCE\" does not exist, run \`limactl create --name=$LIMA_INSTANCE template://apptainer\` to create a new instance" >&2
echo "instance \"$LIMA_INSTANCE\" does not exist, run \`limactl create --name=$LIMA_INSTANCE template:apptainer\` to create a new instance" >&2
exit 1
elif [ "$(limactl ls -f '{{ .Status }}' "$LIMA_INSTANCE" 2>/dev/null)" != "Running" ]; then
echo "instance \"$LIMA_INSTANCE\" is not running, run \`limactl start $LIMA_INSTANCE\` to start the existing instance" >&2
Expand Down
2 changes: 1 addition & 1 deletion cmd/docker.lima
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ set -eu
: "${DOCKER:=docker}"

if [ "$(limactl ls -q "$LIMA_INSTANCE" 2>/dev/null)" != "$LIMA_INSTANCE" ]; then
echo "instance \"$LIMA_INSTANCE\" does not exist, run \`limactl create --name=$LIMA_INSTANCE template://docker\` to create a new instance" >&2
echo "instance \"$LIMA_INSTANCE\" does not exist, run \`limactl create --name=$LIMA_INSTANCE template:docker\` to create a new instance" >&2
exit 1
elif [ "$(limactl ls -f '{{ .Status }}' "$LIMA_INSTANCE" 2>/dev/null)" != "Running" ]; then
echo "instance \"$LIMA_INSTANCE\" is not running, run \`limactl start $LIMA_INSTANCE\` to start the existing instance" >&2
Expand Down
4 changes: 2 additions & 2 deletions cmd/kubectl.lima
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ if [ -z "$LIMA_INSTANCE" ]; then
LIMA_INSTANCE=k8s
else
echo "No k3s or k8s existing instances found. Either create one with" >&2
echo "limactl create --name=k3s template://k3s" >&2
echo "limactl create --name=k8s template://k8s" >&2
echo "limactl create --name=k3s template:k3s" >&2
echo "limactl create --name=k8s template:k8s" >&2
echo "or set LIMA_INSTANCE to the name of your Kubernetes instance" >&2
exit 1
fi
Expand Down
2 changes: 1 addition & 1 deletion cmd/limactl/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func bashCompleteTemplateNames(_ *cobra.Command, toComplete string) ([]string, c
var comp []string
if templates, err := templatestore.Templates(); err == nil {
for _, f := range templates {
name := "template://" + f.Name
name := "template:" + f.Name
if !strings.HasPrefix(name, toComplete) {
continue
}
Expand Down
130 changes: 78 additions & 52 deletions cmd/limactl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"github.com/lima-vm/lima/v2/pkg/fsutil"
"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
"github.com/lima-vm/lima/v2/pkg/osutil"
"github.com/lima-vm/lima/v2/pkg/plugin"
"github.com/lima-vm/lima/v2/pkg/plugins"
"github.com/lima-vm/lima/v2/pkg/version"
)

Expand All @@ -47,15 +47,49 @@ func main() {
}
}
}
rootCmd := newApp()
err := executeWithPluginSupport(rootCmd, os.Args[1:])
err := newApp().Execute()
server.StopAllExternalDrivers()
osutil.HandleExitError(err)
if err != nil {
logrus.Fatal(err)
}
}

func processGlobalFlags(rootCmd *cobra.Command) error {
// --log-level will override --debug, but will not reset debugutil.Debug
if debug, _ := rootCmd.Flags().GetBool("debug"); debug {
logrus.SetLevel(logrus.DebugLevel)
debugutil.Debug = true
}

l, _ := rootCmd.Flags().GetString("log-level")
if l != "" {
lvl, err := logrus.ParseLevel(l)
if err != nil {
return err
}
logrus.SetLevel(lvl)
}

logFormat, _ := rootCmd.Flags().GetString("log-format")
switch logFormat {
case "json":
formatter := new(logrus.JSONFormatter)
logrus.StandardLogger().SetFormatter(formatter)
case "text":
// logrus use text format by default.
if runtime.GOOS == "windows" && isatty.IsCygwinTerminal(os.Stderr.Fd()) {
formatter := new(logrus.TextFormatter)
// the default setting does not recognize cygwin on windows
formatter.ForceColors = true
logrus.StandardLogger().SetFormatter(formatter)
}
default:
return fmt.Errorf("unsupported log-format: %q", logFormat)
}
return nil
}

func newApp() *cobra.Command {
templatesDir := "$PREFIX/share/lima/templates"
if exe, err := os.Executable(); err == nil {
Expand Down Expand Up @@ -92,30 +126,8 @@ func newApp() *cobra.Command {
rootCmd.PersistentFlags().Bool("tty", isatty.IsTerminal(os.Stdout.Fd()), "Enable TUI interactions such as opening an editor. Defaults to true when stdout is a terminal. Set to false for automation.")
rootCmd.PersistentFlags().BoolP("yes", "y", false, "Alias of --tty=false")
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, _ []string) error {
l, _ := cmd.Flags().GetString("log-level")
if l != "" {
lvl, err := logrus.ParseLevel(l)
if err != nil {
return err
}
logrus.SetLevel(lvl)
}

logFormat, _ := cmd.Flags().GetString("log-format")
switch logFormat {
case "json":
formatter := new(logrus.JSONFormatter)
logrus.StandardLogger().SetFormatter(formatter)
case "text":
// logrus use text format by default.
if runtime.GOOS == "windows" && isatty.IsCygwinTerminal(os.Stderr.Fd()) {
formatter := new(logrus.TextFormatter)
// the default setting does not recognize cygwin on windows
formatter.ForceColors = true
logrus.StandardLogger().SetFormatter(formatter)
}
default:
return fmt.Errorf("unsupported log-format: %q", logFormat)
if err := processGlobalFlags(rootCmd); err != nil {
return err
}

if osutil.IsBeingRosettaTranslated() && cmd.Parent().Name() != "completion" && cmd.Name() != "generate-doc" && cmd.Name() != "validate" {
Expand Down Expand Up @@ -191,47 +203,61 @@ func newApp() *cobra.Command {
newNetworkCommand(),
newCloneCommand(),
)
addPluginCommands(rootCmd)

return rootCmd
}

func executeWithPluginSupport(rootCmd *cobra.Command, args []string) error {
rootCmd.SetArgs(args)

if err := rootCmd.ParseFlags(args); err == nil {
if debug, _ := rootCmd.Flags().GetBool("debug"); debug {
logrus.SetLevel(logrus.DebugLevel)
debugutil.Debug = true
}
func addPluginCommands(rootCmd *cobra.Command) {
// The global options are only processed when rootCmd.Execute() is called.
// Let's take a sneak peek here to help debug the plugin discovery code.
if len(os.Args) > 1 && os.Args[1] == "--debug" {
logrus.SetLevel(logrus.DebugLevel)
}

addPluginCommands(rootCmd)

return rootCmd.Execute()
}

func addPluginCommands(rootCmd *cobra.Command) {
plugins, err := plugin.DiscoverPlugins()
allPlugins, err := plugins.Discover()
if err != nil {
logrus.Warnf("Failed to discover plugins: %v", err)
return
}

for _, p := range plugins {
pluginName := p.Name
for _, plugin := range allPlugins {
pluginCmd := &cobra.Command{
Use: pluginName,
Short: p.Description,
Use: plugin.Name,
Short: plugin.Description,
GroupID: "plugin",
DisableFlagParsing: true,
Run: func(cmd *cobra.Command, args []string) {
plugin.RunExternalPlugin(cmd.Context(), pluginName, args)
SilenceErrors: true,
SilenceUsage: true,
PreRunE: func(*cobra.Command, []string) error {
for i, arg := range os.Args {
if arg == plugin.Name {
// parse global options but ignore plugin options
err := rootCmd.ParseFlags(os.Args[1:i])
if err == nil {
err = processGlobalFlags(rootCmd)
}
return err
}
}
// unreachable
return nil
},
Run: func(cmd *cobra.Command, _ []string) {
for i, arg := range os.Args {
if arg == plugin.Name {
// ignore global options
plugin.Run(cmd.Context(), os.Args[i+1:])
// plugin.Run() never returns because it calls os.Exit()
}
}
// unreachable
},
}

pluginCmd.SilenceUsage = true
pluginCmd.SilenceErrors = true

// Don't show the url scheme helper in the help output.
if strings.HasPrefix(plugin.Name, "url-") {
pluginCmd.Hidden = true
}
rootCmd.AddCommand(pluginCmd)
}
}
Expand Down
22 changes: 11 additions & 11 deletions cmd/limactl/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func newCreateCommand() *cobra.Command {
$ limactl create

To create an instance "default" from a template "docker":
$ limactl create --name=default template://docker
$ limactl create --name=default template:docker

To create an instance "default" with modified parameters:
$ limactl create --cpus=2 --memory=2
Expand Down Expand Up @@ -87,7 +87,7 @@ func newStartCommand() *cobra.Command {
$ limactl start

To create an instance "default" from a template "docker", and start it:
$ limactl start --name=default template://docker
$ limactl start --name=default template:docker
`,
Short: "Start an instance of Lima",
Args: WrapArgsError(cobra.MaximumNArgs(1)),
Expand Down Expand Up @@ -234,19 +234,19 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
if isTemplateURL, templateName := limatmpl.SeemsTemplateURL(arg); isTemplateURL {
switch templateName {
case "experimental/vz":
logrus.Warn("template://experimental/vz was merged into the default template in Lima v1.0. See also <https://lima-vm.io/docs/config/vmtype/>.")
logrus.Warn("template:experimental/vz was merged into the default template in Lima v1.0. See also <https://lima-vm.io/docs/config/vmtype/>.")
case "experimental/riscv64":
logrus.Warn("template://experimental/riscv64 was merged into the default template in Lima v1.0. Use `limactl create --arch=riscv64 template://default` instead.")
logrus.Warn("template:experimental/riscv64 was merged into the default template in Lima v1.0. Use `limactl create --arch=riscv64 template:default` instead.")
case "experimental/armv7l":
logrus.Warn("template://experimental/armv7l was merged into the default template in Lima v1.0. Use `limactl create --arch=armv7l template://default` instead.")
logrus.Warn("template:experimental/armv7l was merged into the default template in Lima v1.0. Use `limactl create --arch=armv7l template:default` instead.")
case "vmnet":
logrus.Warn("template://vmnet was removed in Lima v1.0. Use `limactl create --network=lima:shared template://default` instead. See also <https://lima-vm.io/docs/config/network/>.")
logrus.Warn("template:vmnet was removed in Lima v1.0. Use `limactl create --network=lima:shared template:default` instead. See also <https://lima-vm.io/docs/config/network/>.")
case "experimental/net-user-v2":
logrus.Warn("template://experimental/net-user-v2 was removed in Lima v1.0. Use `limactl create --network=lima:user-v2 template://default` instead. See also <https://lima-vm.io/docs/config/network/>.")
logrus.Warn("template:experimental/net-user-v2 was removed in Lima v1.0. Use `limactl create --network=lima:user-v2 template:default` instead. See also <https://lima-vm.io/docs/config/network/>.")
case "experimental/9p":
logrus.Warn("template://experimental/9p was removed in Lima v1.0. Use `limactl create --vm-type=qemu --mount-type=9p template://default` instead. See also <https://lima-vm.io/docs/config/mount/>.")
logrus.Warn("template:experimental/9p was removed in Lima v1.0. Use `limactl create --vm-type=qemu --mount-type=9p template:default` instead. See also <https://lima-vm.io/docs/config/mount/>.")
case "experimental/virtiofs-linux":
logrus.Warn("template://experimental/virtiofs-linux was removed in Lima v1.0. Use `limactl create --mount-type=virtiofs template://default` instead. See also <https://lima-vm.io/docs/config/mount/>.")
logrus.Warn("template:experimental/virtiofs-linux was removed in Lima v1.0. Use `limactl create --mount-type=virtiofs template:default` instead. See also <https://lima-vm.io/docs/config/mount/>.")
}
}
if arg == "-" {
Expand Down Expand Up @@ -298,8 +298,8 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
return nil, err
}
if arg != "" && arg != DefaultInstanceName {
logrus.Infof("Creating an instance %q from template://default (Not from template://%s)", tmpl.Name, tmpl.Name)
logrus.Warnf("This form is deprecated. Use `limactl create --name=%s template://default` instead", tmpl.Name)
logrus.Infof("Creating an instance %q from template:default (Not from template:%s)", tmpl.Name, tmpl.Name)
logrus.Warnf("This form is deprecated. Use `limactl create --name=%s template:default` instead", tmpl.Name)
}
// Read the default template for creating a new instance
tmpl.Bytes, err = templatestore.Read(templatestore.Default)
Expand Down
30 changes: 25 additions & 5 deletions cmd/limactl/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func newTemplateCommand() *cobra.Command {
newTemplateCopyCommand(),
newTemplateValidateCommand(),
newTemplateYQCommand(),
newTemplateURLCommand(),
)
return templateCommand
}
Expand All @@ -51,13 +52,13 @@ func newValidateCommand() *cobra.Command {
return validateCommand
}

var templateCopyExample = ` Template locators are local files, file://, https://, or template:// URLs
var templateCopyExample = ` Template locators are local files, file://, https://, or template: URLs

# Copy default template to STDOUT
limactl template copy template://default -
limactl template copy template:default -

# Copy template from web location to local file and embed all external references
# (this does not embed template:// references)
# (this does not embed template: references)
limactl template copy --embed https://example.com/lima.yaml mighty-machine.yaml
`

Expand Down Expand Up @@ -176,10 +177,10 @@ External references are embedded and default values are filled in
before the YQ expression is evaluated.

Example:
limactl template yq template://default '.images[].location'
limactl template yq template:default '.images[].location'

The example command is equivalent to using an external yq command like this:
limactl template copy --fill template://default - | yq '.images[].location'
limactl template copy --fill template:default - | yq '.images[].location'
`

func newTemplateYQCommand() *cobra.Command {
Expand Down Expand Up @@ -281,3 +282,22 @@ func templateValidateAction(cmd *cobra.Command, args []string) error {

return nil
}

func newTemplateURLCommand() *cobra.Command {
templateURLCommand := &cobra.Command{
Use: "url CUSTOM_URL",
Short: "Transform custom template URLs to regular file or https URLs",
Args: WrapArgsError(cobra.ExactArgs(1)),
RunE: templateURLAction,
}
return templateURLCommand
}

func templateURLAction(cmd *cobra.Command, args []string) error {
url, err := limatmpl.TransformCustomURL(cmd.Context(), args[0])
if err != nil {
return err
}
_, err = fmt.Fprintln(cmd.OutOrStdout(), url)
return err
}
2 changes: 1 addition & 1 deletion cmd/podman.lima
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -eu
: "${PODMAN:=podman}"

if [ "$(limactl ls -q "$LIMA_INSTANCE" 2>/dev/null)" != "$LIMA_INSTANCE" ]; then
echo "instance \"$LIMA_INSTANCE\" does not exist, run \`limactl create --name=$LIMA_INSTANCE template://podman\` to create a new instance" >&2
echo "instance \"$LIMA_INSTANCE\" does not exist, run \`limactl create --name=$LIMA_INSTANCE template:podman\` to create a new instance" >&2
exit 1
elif [ "$(limactl ls -f '{{ .Status }}' "$LIMA_INSTANCE" 2>/dev/null)" != "Running" ]; then
echo "instance \"$LIMA_INSTANCE\" is not running, run \`limactl start $LIMA_INSTANCE\` to start the existing instance" >&2
Expand Down
2 changes: 1 addition & 1 deletion hack/bats/tests/preserve-env.bats
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ local_setup_file() {
limactl delete --force "$NAME" || :
# Make sure that the host agent doesn't inherit file handles 3 or 4.
# Otherwise bats will not finish until the host agent exits.
limactl start --yes --name "$NAME" template://default 3>&- 4>&-
limactl start --yes --name "$NAME" template:default 3>&- 4>&-
}

local_teardown_file() {
Expand Down
2 changes: 1 addition & 1 deletion hack/common.inc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ _IPERF3=iperf3
: "${IPERF3:=$_IPERF3}"

# Setup LIMA_TEMPLATES_PATH because the templates are not installed, but reference base templates
# via template://_images/* and template://_default/*.
# via template:_images/* and template:_default/*.
templates_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../templates" && pwd)"
: "${LIMA_TEMPLATES_PATH:-$templates_dir}"
export LIMA_TEMPLATES_PATH
2 changes: 1 addition & 1 deletion hack/test-templates/test-misc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# - snapshots
# - (More to come)
#
base: template://ubuntu-22.04
base: template:ubuntu-22.04

# 9p is not compatible with `limactl snapshot`
mountTypesUnsupported: ["9p"]
Expand Down
Loading
Loading