pkg/utils: Move CLI utility functions to cmd

pkg/utils has been in Go Toolbox since its birth. Along the way it
accumulated a number of functions where a few of them are purely CLI
related. Since the majority of functions in the package are related to
some "deeper" functionality in Toolbox, it makes more sense to move the
selected few to package cmd. This will make pkg/utils a bit leaner and
create a dedicated space for cmd utility functions to live in.

In the process the error creation functions no longer require the
executableBase argument to be passed to them.

https://github.com/containers/toolbox/pull/819
This commit is contained in:
Ondřej Míchal 2021-06-29 20:47:16 +02:00
parent 3aeb7cf288
commit be4d3974ef
12 changed files with 109 additions and 90 deletions

View file

@ -143,7 +143,7 @@ func create(cmd *cobra.Command, args []string) error {
var err error var err error
release, err = utils.ParseRelease(createFlags.distro, createFlags.release) release, err = utils.ParseRelease(createFlags.distro, createFlags.release)
if err != nil { if err != nil {
err := utils.CreateErrorInvalidRelease(executableBase) err := createErrorInvalidRelease()
return err return err
} }
} }
@ -471,7 +471,7 @@ func createHelp(cmd *cobra.Command, args []string) {
return return
} }
if err := utils.ShowManual("toolbox-create"); err != nil { if err := showManual("toolbox-create"); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err) fmt.Fprintf(os.Stderr, "Error: %s\n", err)
return return
} }
@ -696,7 +696,7 @@ func pullImage(image, release string) (bool, error) {
fmt.Println("Image required to create toolbox container.") fmt.Println("Image required to create toolbox container.")
prompt := fmt.Sprintf("Download %s (500MB)? [y/N]:", imageFull) prompt := fmt.Sprintf("Download %s (500MB)? [y/N]:", imageFull)
shouldPullImage = utils.AskForConfirmation(prompt) shouldPullImage = askForConfirmation(prompt)
} }
if !shouldPullImage { if !shouldPullImage {

View file

@ -111,7 +111,7 @@ func enter(cmd *cobra.Command, args []string) error {
var err error var err error
release, err = utils.ParseRelease(enterFlags.distro, enterFlags.release) release, err = utils.ParseRelease(enterFlags.distro, enterFlags.release)
if err != nil { if err != nil {
err := utils.CreateErrorInvalidRelease(executableBase) err := createErrorInvalidRelease()
return err return err
} }
} }
@ -178,7 +178,7 @@ func enterHelp(cmd *cobra.Command, args []string) {
return return
} }
if err := utils.ShowManual("toolbox-enter"); err != nil { if err := showManual("toolbox-enter"); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err) fmt.Fprintf(os.Stderr, "Error: %s\n", err)
return return
} }

View file

@ -88,7 +88,7 @@ func helpShowManual(args []string) error {
manual = "toolbox-" + args[0] manual = "toolbox-" + args[0]
} }
if err := utils.ShowManual(manual); err != nil { if err := showManual(manual); err != nil {
return err return err
} }

View file

@ -350,7 +350,7 @@ func initContainerHelp(cmd *cobra.Command, args []string) {
return return
} }
if err := utils.ShowManual("toolbox-init-container"); err != nil { if err := showManual("toolbox-init-container"); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err) fmt.Fprintf(os.Stderr, "Error: %s\n", err)
return return
} }

View file

@ -185,7 +185,7 @@ func listHelp(cmd *cobra.Command, args []string) {
return return
} }
if err := utils.ShowManual("toolbox-list"); err != nil { if err := showManual("toolbox-list"); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err) fmt.Fprintf(os.Stderr, "Error: %s\n", err)
return return
} }

View file

@ -122,7 +122,7 @@ func rmHelp(cmd *cobra.Command, args []string) {
return return
} }
if err := utils.ShowManual("toolbox-rm"); err != nil { if err := showManual("toolbox-rm"); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err) fmt.Fprintf(os.Stderr, "Error: %s\n", err)
return return
} }

View file

@ -122,7 +122,7 @@ func rmiHelp(cmd *cobra.Command, args []string) {
return return
} }
if err := utils.ShowManual("toolbox-rmi"); err != nil { if err := showManual("toolbox-rmi"); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err) fmt.Fprintf(os.Stderr, "Error: %s\n", err)
return return
} }

View file

@ -174,7 +174,7 @@ func rootHelp(cmd *cobra.Command, args []string) {
} }
} }
if err := utils.ShowManual(manual); err != nil { if err := showManual(manual); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err) fmt.Fprintf(os.Stderr, "Error: %s\n", err)
return return
} }

View file

@ -109,7 +109,7 @@ func run(cmd *cobra.Command, args []string) error {
var err error var err error
release, err = utils.ParseRelease(runFlags.distro, runFlags.release) release, err = utils.ParseRelease(runFlags.distro, runFlags.release)
if err != nil { if err != nil {
err := utils.CreateErrorInvalidRelease(executableBase) err := createErrorInvalidRelease()
return err return err
} }
} }
@ -170,13 +170,13 @@ func runCommand(container string,
logrus.Debugf("Container %s not found", container) logrus.Debugf("Container %s not found", container)
if pedantic { if pedantic {
err := utils.CreateErrorContainerNotFound(container, executableBase) err := createErrorContainerNotFound(container)
return err return err
} }
containers, err := getContainers() containers, err := getContainers()
if err != nil { if err != nil {
err := utils.CreateErrorContainerNotFound(container, executableBase) err := createErrorContainerNotFound(container)
return err return err
} }
@ -194,7 +194,7 @@ func runCommand(container string,
if promptForCreate { if promptForCreate {
prompt := "No toolbox containers found. Create now? [y/N]" prompt := "No toolbox containers found. Create now? [y/N]"
shouldCreateContainer = utils.AskForConfirmation(prompt) shouldCreateContainer = askForConfirmation(prompt)
} }
if !shouldCreateContainer { if !shouldCreateContainer {
@ -383,7 +383,7 @@ func runHelp(cmd *cobra.Command, args []string) {
return return
} }
if err := utils.ShowManual("toolbox-run"); err != nil { if err := showManual("toolbox-run"); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err) fmt.Fprintf(os.Stderr, "Error: %s\n", err)
return return
} }

92
src/cmd/utils.go Normal file
View file

@ -0,0 +1,92 @@
package cmd
import (
"errors"
"fmt"
"os"
"os/exec"
"strings"
"syscall"
)
// askForConfirmation prints prompt to stdout and waits for response from the
// user
//
// Expected answers are: "yes", "y", "no", "n"
//
// Answers are internally converted to lower case.
//
// The default answer is "no" ([y/N])
func askForConfirmation(prompt string) bool {
var retVal bool
for {
fmt.Printf("%s ", prompt)
var response string
fmt.Scanf("%s", &response)
if response == "" {
response = "n"
} else {
response = strings.ToLower(response)
}
if response == "no" || response == "n" {
break
} else if response == "yes" || response == "y" {
retVal = true
break
}
}
return retVal
}
func createErrorContainerNotFound(container string) error {
var builder strings.Builder
fmt.Fprintf(&builder, "container %s not found\n", container)
fmt.Fprintf(&builder, "Use the 'create' command to create a toolbox.\n")
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
errMsg := builder.String()
return errors.New(errMsg)
}
func createErrorInvalidRelease() error {
var builder strings.Builder
fmt.Fprintf(&builder, "invalid argument for '--release'\n")
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
errMsg := builder.String()
return errors.New(errMsg)
}
// showManual tries to open the specified manual page using man on stdout
func showManual(manual string) error {
manBinary, err := exec.LookPath("man")
if err != nil {
if errors.Is(err, exec.ErrNotFound) {
return errors.New("man(1) not found")
}
return errors.New("failed to lookup man(1)")
}
manualArgs := []string{"man", manual}
env := os.Environ()
stderrFd := os.Stderr.Fd()
stderrFdInt := int(stderrFd)
stdoutFd := os.Stdout.Fd()
stdoutFdInt := int(stdoutFd)
if err := syscall.Dup3(stdoutFdInt, stderrFdInt, 0); err != nil {
return errors.New("failed to redirect standard error to standard output")
}
if err := syscall.Exec(manBinary, manualArgs, env); err != nil {
return errors.New("failed to invoke man(1)")
}
return nil
}

View file

@ -12,6 +12,7 @@ sources = files(
'cmd/rmi.go', 'cmd/rmi.go',
'cmd/root.go', 'cmd/root.go',
'cmd/run.go', 'cmd/run.go',
'cmd/utils.go',
'pkg/podman/podman.go', 'pkg/podman/podman.go',
'pkg/shell/shell.go', 'pkg/shell/shell.go',
'pkg/utils/utils.go', 'pkg/utils/utils.go',

View file

@ -20,7 +20,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"os/exec"
"os/user" "os/user"
"path" "path"
"path/filepath" "path/filepath"
@ -136,32 +135,6 @@ func init() {
ContainerNameDefault = containerNamePrefixDefault + "-" + releaseDefault ContainerNameDefault = containerNamePrefixDefault + "-" + releaseDefault
} }
func AskForConfirmation(prompt string) bool {
var retVal bool
for {
fmt.Printf("%s ", prompt)
var response string
fmt.Scanf("%s", &response)
if response == "" {
response = "n"
} else {
response = strings.ToLower(response)
}
if response == "no" || response == "n" {
break
} else if response == "yes" || response == "y" {
retVal = true
break
}
}
return retVal
}
func CallFlatpakSessionHelper() (string, error) { func CallFlatpakSessionHelper() (string, error) {
logrus.Debug("Calling org.freedesktop.Flatpak.SessionHelper.RequestSession") logrus.Debug("Calling org.freedesktop.Flatpak.SessionHelper.RequestSession")
@ -191,25 +164,6 @@ func CallFlatpakSessionHelper() (string, error) {
return path, nil return path, nil
} }
func CreateErrorContainerNotFound(container, executableBase string) error {
var builder strings.Builder
fmt.Fprintf(&builder, "container %s not found\n", container)
fmt.Fprintf(&builder, "Use the 'create' command to create a toolbox.\n")
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
errMsg := builder.String()
return errors.New(errMsg)
}
func CreateErrorInvalidRelease(executableBase string) error {
var builder strings.Builder
fmt.Fprintf(&builder, "invalid argument for '--release'\n")
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
errMsg := builder.String()
return errors.New(errMsg)
}
func EnsureXdgRuntimeDirIsSet(uid int) { func EnsureXdgRuntimeDirIsSet(uid int) {
if _, ok := os.LookupEnv("XDG_RUNTIME_DIR"); !ok { if _, ok := os.LookupEnv("XDG_RUNTIME_DIR"); !ok {
logrus.Debug("XDG_RUNTIME_DIR is unset") logrus.Debug("XDG_RUNTIME_DIR is unset")
@ -819,31 +773,3 @@ func ResolveImageName(distroCLI, imageCLI, releaseCLI string) (string, string, e
return image, release, nil return image, release, nil
} }
func ShowManual(manual string) error {
manBinary, err := exec.LookPath("man")
if err != nil {
if errors.Is(err, exec.ErrNotFound) {
return errors.New("man(1) not found")
}
return errors.New("failed to lookup man(1)")
}
manualArgs := []string{"man", manual}
env := os.Environ()
stderrFd := os.Stderr.Fd()
stderrFdInt := int(stderrFd)
stdoutFd := os.Stdout.Fd()
stdoutFdInt := int(stdoutFd)
if err := syscall.Dup3(stdoutFdInt, stderrFdInt, 0); err != nil {
return errors.New("failed to redirect standard error to standard output")
}
if err := syscall.Exec(manBinary, manualArgs, env); err != nil {
return errors.New("failed to invoke man(1)")
}
return nil
}