Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

limactl shell: ask whether to start the instance if not running #2832

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
35 changes: 6 additions & 29 deletions cmd/limactl/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -132,34 +129,14 @@ func editAction(cmd *cobra.Command, args []string) error {
}
if inst != nil {
logrus.Infof("Instance %q configuration edited", inst.Name)
}

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 {
tty, err := interactive(cmd)
if tty && err == nil {
err = askToStart(cmd, inst.Name, false)
}
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) {
Expand Down
28 changes: 25 additions & 3 deletions cmd/limactl/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -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] == "--" {
Expand All @@ -68,15 +69,36 @@ func shellAction(cmd *cobra.Command, args []string) error {
}
}

tty, err := interactive(cmd)
if err != nil {
return err
}
inst, err := store.Inspect(instName)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
if !errors.Is(err, os.ErrNotExist) {
return err
}
if !tty {
return fmt.Errorf("instance %q does not exist, run `limactl create %s` to create a new instance", instName, instName)
}
if err := askToStart(cmd, instName, true); err != nil {
return err
}
inst, err = store.Inspect(instName)
} else if inst.Status == store.StatusStopped {
if !tty {
return fmt.Errorf("instance %q is stopped, run `limactl start %s` to start the instance", instName, instName)
}
if err := askToStart(cmd, instName, false); err != nil {
return err
}
inst, err = store.Inspect(instName)
}
if err != nil {
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 inst.Status != store.StatusRunning {
return fmt.Errorf("instance %q status is not %q but %q", inst.Name, store.StatusRunning, inst.Status)
}

// When workDir is explicitly set, the shell MUST have workDir as the cwd, or exit with an error.
Expand Down
53 changes: 53 additions & 0 deletions cmd/limactl/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/lima-vm/lima/pkg/templatestore"
"github.com/lima-vm/lima/pkg/uiutil"
"github.com/lima-vm/lima/pkg/yqutil"
"github.com/mattn/go-isatty"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -529,3 +530,55 @@ func startBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobr
compTmpl, _ := bashCompleteTemplateNames(cmd)
return append(compInst, compTmpl...), cobra.ShellCompDirectiveDefault
}

// interactive returns true if --tty is true and both STDIN and STDOUT are terminals.
func interactive(cmd *cobra.Command) (bool, error) {
flags := cmd.Flags()
tty, err := flags.GetBool("tty")
if err != nil {
return false, err
}
if !isatty.IsTerminal(os.Stdin.Fd()) && !isatty.IsCygwinTerminal(os.Stdin.Fd()) {
tty = false
}
if !isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()) {
tty = false
}
return tty, nil
}

func askToStart(cmd *cobra.Command, instName string, create bool) error {
template := "default"
templates, err := templatestore.Templates()
if err != nil {
return err
}
for _, t := range templates {
if t.Name == instName {
template = instName
break
}
}
var message string
if create {
message = fmt.Sprintf("Do you want to create and start the instance %q using the %q template now?", instName, template)
} else {
message = fmt.Sprintf("Do you want to start the instance %q now?", instName)
}
ans, err := uiutil.Confirm(message, true)
if !ans || err != nil {
return err
}

rootCmd := cmd.Root()
if create {
// The create command shows the template chooser UI, etc.
rootCmd.SetArgs([]string{"create", "template://" + template})
if err := rootCmd.Execute(); err != nil {
return err
}
}
// The start command reconciles the networks, etc.
rootCmd.SetArgs([]string{"start", instName})
return rootCmd.Execute()
}
13 changes: 10 additions & 3 deletions pkg/instance/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading