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

View file

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

View file

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

View file

@ -350,7 +350,7 @@ func initContainerHelp(cmd *cobra.Command, args []string) {
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)
return
}

View file

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

View file

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

View file

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

View file

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

View file

@ -20,7 +20,6 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"os/user"
"path"
"path/filepath"
@ -136,32 +135,6 @@ func init() {
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) {
logrus.Debug("Calling org.freedesktop.Flatpak.SessionHelper.RequestSession")
@ -191,25 +164,6 @@ func CallFlatpakSessionHelper() (string, error) {
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) {
if _, ok := os.LookupEnv("XDG_RUNTIME_DIR"); !ok {
logrus.Debug("XDG_RUNTIME_DIR is unset")
@ -819,31 +773,3 @@ func ResolveImageName(distroCLI, imageCLI, releaseCLI string) (string, string, e
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
}