From 46b0c558dfc6f2598a9ac3a3c784210da77205b6 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Sat, 19 Oct 2024 05:01:06 +0900 Subject: [PATCH] limactl shell: ask whether to start the instance if not running Signed-off-by: Akihiro Suda --- cmd/limactl/edit.go | 35 +++++------------------------ cmd/limactl/shell.go | 19 +++++++++++++--- cmd/limactl/start.go | 52 +++++++++++++++++++++++++++++++++++++++++++ pkg/instance/start.go | 13 ++++++++--- 4 files changed, 83 insertions(+), 36 deletions(-) diff --git a/cmd/limactl/edit.go b/cmd/limactl/edit.go index 70a54a858f6..76701cd88fb 100644 --- a/cmd/limactl/edit.go +++ b/cmd/limactl/edit.go @@ -10,12 +10,9 @@ import ( "github.com/lima-vm/lima/cmd/limactl/editflags" "github.com/lima-vm/lima/cmd/limactl/guessarg" "github.com/lima-vm/lima/pkg/editutil" - "github.com/lima-vm/lima/pkg/instance" "github.com/lima-vm/lima/pkg/limayaml" - networks "github.com/lima-vm/lima/pkg/networks/reconcile" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/lima-vm/lima/pkg/uiutil" "github.com/lima-vm/lima/pkg/yqutil" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -132,34 +129,12 @@ func editAction(cmd *cobra.Command, args []string) error { } if inst != nil { logrus.Infof("Instance %q configuration edited", inst.Name) + if _, err = startInteractive(cmd, inst.Name, false, false); err != nil { + return err + } } - - if !tty { - // use "start" to start it - return nil - } - if inst == nil { - // edited a limayaml file directly - return nil - } - startNow, err := askWhetherToStart() - if err != nil { - return err - } - if !startNow { - return nil - } - ctx := cmd.Context() - err = networks.Reconcile(ctx, inst.Name) - if err != nil { - return err - } - return instance.Start(ctx, inst, "", false) -} - -func askWhetherToStart() (bool, error) { - message := "Do you want to start the instance now? " - return uiutil.Confirm(message, true) + // inst is nil if edited a limayaml file directly + return nil } func editBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { diff --git a/cmd/limactl/shell.go b/cmd/limactl/shell.go index 694413923d8..0ca9435792b 100644 --- a/cmd/limactl/shell.go +++ b/cmd/limactl/shell.go @@ -51,6 +51,7 @@ func newShellCommand() *cobra.Command { } func shellAction(cmd *cobra.Command, args []string) error { + os.Setenv("_LIMACTL_SHELL_IN_ACTION", "") // simulate the behavior of double dash newArg := []string{} if len(args) >= 2 && args[1] == "--" { @@ -71,12 +72,24 @@ func shellAction(cmd *cobra.Command, args []string) error { inst, err := store.Inspect(instName) if err != nil { if errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("instance %q does not exist, run `limactl create %s` to create a new instance", instName, instName) + _, err = startInteractive(cmd, instName, true, true) + if err != nil { + return err + } + inst, err = store.Inspect(instName) + } + if err != nil { + return err } - return err } if inst.Status == store.StatusStopped { - return fmt.Errorf("instance %q is stopped, run `limactl start %s` to start the instance", instName, instName) + if _, err = startInteractive(cmd, instName, false, true); err != nil { + return err + } + inst, err = store.Inspect(instName) + if err != nil { + return err + } } // When workDir is explicitly set, the shell MUST have workDir as the cwd, or exit with an error. diff --git a/cmd/limactl/start.go b/cmd/limactl/start.go index 90f329c59b3..eb835d8c500 100644 --- a/cmd/limactl/start.go +++ b/cmd/limactl/start.go @@ -529,3 +529,55 @@ func startBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobr compTmpl, _ := bashCompleteTemplateNames(cmd) return append(compInst, compTmpl...), cobra.ShellCompDirectiveDefault } + +// startInteractive starts the instance after a confirmation prompt. +// +// If newInst is set to true, this function creates the instance too. +// +// If mandatory is set to true, and the answer to the confirmation prompt is No, +// the function returns an error that contains an instruction to start the instance manually. +func startInteractive(cmd *cobra.Command, instName string, newInst, mandatory bool) (bool, error) { + flags := cmd.Flags() + tty, err := flags.GetBool("tty") + if err != nil { + return false, err + } + errS := fmt.Sprintf("instance %q is stopped, run `limactl start %s` to start the instance", instName, instName) + if newInst { + errS = fmt.Sprintf("instance %q does not exist, run `limactl create %s` to create a new instance", instName, instName) + } + if !tty { + if mandatory { + return false, errors.New(errS) + } + return false, nil + } + logrus.Error(errS) + + message := fmt.Sprintf("Do you want to start the instance %q now?", instName) + if newInst { + message = fmt.Sprintf("Do you want to create and start the instance %q using the default template now?", instName) + } + ans, err := uiutil.Confirm(message, true) + if err != nil { + return false, err + } + if !ans { + if mandatory { + return ans, errors.New(errS) + } + return ans, nil + } + if newInst { + rootCmd := cmd.Root() + // The create command shows the template chooser UI, etc. + rootCmd.SetArgs([]string{"create", instName}) + if err := rootCmd.Execute(); err != nil { + return ans, err + } + } + rootCmd := cmd.Root() + // The start command reconciliates the networks, etc. + rootCmd.SetArgs([]string{"start", instName}) + return ans, rootCmd.Execute() +} diff --git a/pkg/instance/start.go b/pkg/instance/start.go index f2b1c105b79..4ecbb3d614c 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -307,10 +307,17 @@ func watchHostAgentEvents(ctx context.Context, inst *store.Instance, haStdoutPat err = xerr return true } - if *inst.Config.Plain { - logrus.Infof("READY. Run `ssh -F %q %s` to open the shell.", inst.SSHConfigFile, inst.Hostname) + // _LIMACTL_SHELL_IN_ACTION is set if `limactl shell` invoked `limactl start`. + // In this case we shouldn't print "Run `lima` to open the shell", + // because the user has already executed the `lima` command. + if _, limactlShellInAction := os.LookupEnv("_LIMACTL_SHELL_IN_ACTION"); limactlShellInAction { + logrus.Infof("READY.") } else { - logrus.Infof("READY. Run `%s` to open the shell.", LimactlShellCmd(inst.Name)) + if *inst.Config.Plain { + logrus.Infof("READY. Run `ssh -F %q %s` to open the shell.", inst.SSHConfigFile, inst.Hostname) + } else { + logrus.Infof("READY. Run `%s` to open the shell.", LimactlShellCmd(inst.Name)) + } } _ = ShowMessage(inst) err = nil