Skip to content

Commit

Permalink
use cobra
Browse files Browse the repository at this point in the history
Signed-off-by: luyanbo <robert.lyb@alibaba-inc.com>
  • Loading branch information
robberphex committed Sep 10, 2021
1 parent 9c4b22a commit dbc493a
Show file tree
Hide file tree
Showing 16 changed files with 399 additions and 349 deletions.
55 changes: 28 additions & 27 deletions cmd/lima-guestagent/daemon_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,32 @@ import (
"github.com/lima-vm/lima/pkg/guestagent"
"github.com/lima-vm/lima/pkg/guestagent/api/server"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
)

var daemonCommand = &cli.Command{
Name: "daemon",
Usage: "run the daemon",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "socket",
Usage: "socket",
Value: func() string {
if xrd := os.Getenv("XDG_RUNTIME_DIR"); xrd != "" {
return filepath.Join(xrd, "lima-guestagent.sock")
}
logrus.Warn("$XDG_RUNTIME_DIR is not set, cannot determine the socket name")
return ""
}(),
},
&cli.DurationFlag{
Name: "tick",
Usage: "tick for polling events",
Value: 3 * time.Second,
},
},
Action: daemonAction,
func newDaemonCommand() *cobra.Command {
daemonCommand := &cobra.Command{
Use: "daemon",
Short: "run the daemon",
RunE: daemonAction,
}
daemonCommand.Flags().String("socket", socketDefaultValue(), "the unix socket to listen on")
daemonCommand.Flags().Duration("tick", 3*time.Second, "tick for polling events")
return daemonCommand
}

func daemonAction(clicontext *cli.Context) error {
socket := clicontext.String("socket")
func daemonAction(cmd *cobra.Command, args []string) error {
socket, err := cmd.Flags().GetString("socket")
if err != nil {
return err
}
if socket == "" {
return errors.New("socket must be specified")
}
tick := clicontext.Duration("tick")
tick, err := cmd.Flags().GetDuration("tick")
if err != nil {
return err
}
if tick == 0 {
return errors.New("tick must be specified")
}
Expand All @@ -68,7 +61,7 @@ func daemonAction(clicontext *cli.Context) error {
r := mux.NewRouter()
server.AddRoutes(r, backend)
srv := &http.Server{Handler: r}
err := os.RemoveAll(socket)
err = os.RemoveAll(socket)
if err != nil {
return err
}
Expand All @@ -79,3 +72,11 @@ func daemonAction(clicontext *cli.Context) error {
logrus.Infof("serving the guest agent on %q", socket)
return srv.Serve(l)
}

func socketDefaultValue() string {
if xrd := os.Getenv("XDG_RUNTIME_DIR"); xrd != "" {
return filepath.Join(xrd, "lima-guestagent.sock")
}
logrus.Warn("$XDG_RUNTIME_DIR is not set, cannot determine the socket name")
return ""
}
15 changes: 9 additions & 6 deletions cmd/lima-guestagent/install_systemd_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ import (

"github.com/lima-vm/lima/pkg/templateutil"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
)

var installSystemdCommand = &cli.Command{
Name: "install-systemd",
Usage: "install a systemd unit (user)",
Action: installSystemdAction,
func newInstallSystemdCommand() *cobra.Command {
var installSystemdCommand = &cobra.Command{
Use: "install-systemd",
Short: "install a systemd unit (user)",
RunE: installSystemdAction,
}
return installSystemdCommand
}

func installSystemdAction(clicontext *cli.Context) error {
func installSystemdAction(cmd *cobra.Command, args []string) error {
unit, err := generateSystemdUnit()
if err != nil {
return err
Expand Down
35 changes: 16 additions & 19 deletions cmd/lima-guestagent/main_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,35 @@ import (

"github.com/lima-vm/lima/pkg/version"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
)

func main() {
if err := newApp().Run(os.Args); err != nil {
if err := newApp().Execute(); err != nil {
logrus.Fatal(err)
}
}

func newApp() *cli.App {
app := cli.NewApp()
app.Name = "lima-guestagent"
app.Usage = "Do not launch manually"
app.Version = strings.TrimPrefix(version.Version, "v")
app.Flags = []cli.Flag{
&cli.BoolFlag{
Name: "debug",
Usage: "debug mode",
},
func newApp() *cobra.Command {
var rootCmd = &cobra.Command{
Use: "lima-guestagent",
Short: "Do not launch manually",
Version: strings.TrimPrefix(version.Version, "v"),
}
app.Before = func(clicontext *cli.Context) error {
if clicontext.Bool("debug") {
rootCmd.PersistentFlags().Bool("debug", false, "debug mode")
rootCmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error {
debug, _ := cmd.Flags().GetBool("debug")
if debug {
logrus.SetLevel(logrus.DebugLevel)
}
if os.Geteuid() == 0 {
return errors.New("must not run as the root")
}
return nil
}
app.Commands = []*cli.Command{
daemonCommand,
installSystemdCommand,
}
return app
rootCmd.AddCommand(
newDaemonCommand(),
newInstallSystemdCommand(),
)
return rootCmd
}
58 changes: 4 additions & 54 deletions cmd/limactl/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,64 +19,14 @@
package main

import (
"fmt"

"github.com/lima-vm/lima/pkg/store"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
)

var completionCommand = &cli.Command{
Name: "completion",
Usage: "Show shell completion",
Subcommands: []*cli.Command{
completionBashCommand,
},
}

var completionBashCommand = &cli.Command{
Name: "bash",
Usage: "Show bash completion (use with `source <(limactl completion bash)`)",
Description: "Usage: add `source <(limactl completion bash)` to ~/.bash_profile",
Action: completionBashAction,
}

func completionBashAction(clicontext *cli.Context) error {
tmpl := `#!/bin/bash
# Autocompletion enabler for limactl.
# Usage: add 'source <(limactl completion bash)' to ~/.bash_profile
# _limactl_bash_autocomplete is forked from https://github.com/urfave/cli/blob/v2.3.0/autocomplete/bash_autocomplete (MIT License)
_limactl_bash_autocomplete() {
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
local cur opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
local args="${COMP_WORDS[@]:0:$COMP_CWORD}"
# make {"limactl", "--foo", "=", "bar"} into {"limactl", "--foo=bar"}
args="$(echo $args | sed -e 's/ = /=/g')"
if [[ "$cur" == "-"* ]]; then
opts=$( ${args} ${cur} --generate-bash-completion )
else
opts=$( ${args} --generate-bash-completion )
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
complete -o bashdefault -o default -o nospace -F _limactl_bash_autocomplete limactl
`
_, err := fmt.Fprint(clicontext.App.Writer, tmpl)
return err
}

func bashCompleteInstanceNames(clicontext *cli.Context) {
w := clicontext.App.Writer
func bashCompleteInstanceNames(cmd *cobra.Command) ([]string, cobra.ShellCompDirective) {
instances, err := store.Instances()
if err != nil {
return
}
for _, name := range instances {
fmt.Fprintln(w, name)
return nil, cobra.ShellCompDirectiveDefault
}
return instances, cobra.ShellCompDirectiveNoFileComp
}
53 changes: 32 additions & 21 deletions cmd/limactl/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,31 @@ import (
"github.com/lima-vm/lima/pkg/sshutil"
"github.com/lima-vm/lima/pkg/store"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
)

var copyCommand = &cli.Command{
Name: "copy",
Aliases: []string{"cp"},
Usage: "Copy files between host and guest",
Description: "Prefix guest filenames with the instance name and a colon.\nExample: limactl copy default:/etc/os-release .",
ArgsUsage: "SOURCE ... TARGET",
Action: copyAction,
var copyHelp = `Copy files between host and guest
Prefix guest filenames with the instance name and a colon.
Example: limactl copy default:/etc/os-release .
`

func newCopyCommand() *cobra.Command {
var copyCommand = &cobra.Command{
Use: "copy SOURCE ... TARGET",
Aliases: []string{"cp"},
Short: "Copy files between host and guest",
Long: copyHelp,
Args: cobra.MinimumNArgs(2),
RunE: copyAction,
}

return copyCommand
}

func copyAction(clicontext *cli.Context) error {
if clicontext.NArg() < 2 {
func copyAction(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return fmt.Errorf("requires at least 2 arguments: SOURCE DEST")
}
arg0, err := exec.LookPath("scp")
Expand All @@ -37,12 +48,12 @@ func copyAction(clicontext *cli.Context) error {
}

instDirs := make(map[string]string)
args := []string{"-3", "--"}
for _, arg := range clicontext.Args().Slice() {
scpArgs := []string{"-3", "--"}
for _, arg := range args {
path := strings.Split(arg, ":")
switch len(path) {
case 1:
args = append(args, arg)
scpArgs = append(scpArgs, arg)
case 2:
instName := path[0]
inst, err := store.Inspect(instName)
Expand All @@ -55,10 +66,10 @@ func copyAction(clicontext *cli.Context) error {
if inst.Status == store.StatusStopped {
return fmt.Errorf("instance %q is stopped, run `limactl start %s` to start the instance", instName, instName)
}
args = append(args, fmt.Sprintf("scp://%s@127.0.0.1:%d/%s", u.Username, inst.SSHLocalPort, path[1]))
scpArgs = append(args, fmt.Sprintf("scp://%s@127.0.0.1:%d/%s", u.Username, inst.SSHLocalPort, path[1]))
instDirs[instName] = inst.Dir
default:
return fmt.Errorf("Path %q contains multiple colons", arg)
return fmt.Errorf("path %q contains multiple colons", arg)
}
}

Expand All @@ -81,12 +92,12 @@ func copyAction(clicontext *cli.Context) error {
}
}

cmd := exec.Command(arg0, append(sshArgs, args...)...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
logrus.Debugf("executing scp (may take a long time)): %+v", cmd.Args)
sshCmd := exec.Command(arg0, append(sshArgs, scpArgs...)...)
sshCmd.Stdin = os.Stdin
sshCmd.Stdout = os.Stdout
sshCmd.Stderr = os.Stderr
logrus.Debugf("executing scp (may take a long time)): %+v", sshCmd.Args)

// TODO: use syscall.Exec directly (results in losing tty?)
return cmd.Run()
return sshCmd.Run()
}
42 changes: 21 additions & 21 deletions cmd/limactl/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,31 @@ import (

"github.com/lima-vm/lima/pkg/store"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
)

var deleteCommand = &cli.Command{
Name: "delete",
Aliases: []string{"remove", "rm"},
Usage: "Delete an instance of Lima.",
ArgsUsage: "INSTANCE [INSTANCE, ...]",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Usage: "forcibly kill the processes",
},
},
Action: deleteAction,
BashComplete: deleteBashComplete,
func newDeleteCommand() *cobra.Command {
var deleteCommand = &cobra.Command{
Use: "delete INSTANCE [INSTANCE, ...]",
Aliases: []string{"remove", "rm"},
Short: "Delete an instance of Lima.",
Args: cobra.MinimumNArgs(1),
RunE: deleteAction,
ValidArgsFunction: deleteBashComplete,
}
deleteCommand.Flags().BoolP("force", "f", false, "forcibly kill the processes")
return deleteCommand
}

func deleteAction(clicontext *cli.Context) error {
if clicontext.NArg() == 0 {
func deleteAction(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("requires at least 1 argument")
}
force := clicontext.Bool("force")
for _, instName := range clicontext.Args().Slice() {
force, err := cmd.Flags().GetBool("force")
if err != nil {
return err
}
for _, instName := range args {
inst, err := store.Inspect(instName)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
Expand Down Expand Up @@ -61,6 +61,6 @@ func deleteInstance(inst *store.Instance, force bool) error {
return nil
}

func deleteBashComplete(clicontext *cli.Context) {
bashCompleteInstanceNames(clicontext)
func deleteBashComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return bashCompleteInstanceNames(cmd)
}
Loading

0 comments on commit dbc493a

Please sign in to comment.