cmdutil

package
v3.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 15, 2022 License: MIT Imports: 9 Imported by: 5

Documentation

Overview

Package cmdutil contains helper utilities for setting up a CLI with Go, providing basic application behavior and for reducing boilerplate code.

An example application can be found at https://github.com/rebuy-de/golang-template.

Graceful Application Exits

In many command line applications it is desired to exit the process immediately, if it is clear that the application cannot recover. Important note: This is designed for actual applications (ie not libraries), because only the application itself should decide when to exit. Libraries should alway return errors.

There are three ways to handle fatal errors in Go. With os.Exit() the process will terminate immediately, but it will not call any deferrers which means that possible cleanup task do not get called. The next way is to call panic, which respects the defer statements, but unfortunately it is not possible to define an exit code and the user gets confused with a stack trace. Finally, the function could just return an error indicating that things failed, but this introduces a lot of code, conditionals and appears unnecessary, when it is already clear that the application cannot recover.

The package cmdutil provides an alternative, which panics with a known struct and catches it right before the application exit. This is an example to illustrate the usage:

func main() {
  defer cmdutil.HandleExit()
  run()
}

func run() {
  defer fmt.Println("important cleanup")
  err := doSomething()
  if err != nil {
    log.Error(err)
    cmdutil.Exit(2)
  }
}

The defer of HandleExit is the first statement in the main function. It ensures a pretty output and that the application exits with the specified exit code. The run function does something and makes the application exit with an exit code. The specified defer statement is still called. Also the application logging facility should be used to communicate the error, so the error actually appears on external logging applications like Syslog or Graylog.

Minimal Application Boilerplate

Golang is very helpful for creating glue code in the ops area and creating micro services. But when you want features like proper logging, a version subcommand and a clean structure, there is still a lot of boilerplate code needed. NewRootCommand creates a ready-to-use Cobra command to reduce the necessary code. This is an example to illustrate the usage:

type App struct {
    Name string
}

func (app *App) Run(cmd *cobra.Command, args []string) {
    log.Infof("hello %s", app.Name)
}

func (app *App) Bind(cmd *cobra.Command) {
    cmd.PersistentFlags().StringVarP(
        &app.Name, "name", "n", "world",
        `Your name.`)
}

func NewRootCommand() *cobra.Command {
    cmd := cmdutil.NewRootCommand(new(App))
    cmd.Short = "an example app for golang which can be used as template"
    return cmd
}

The App struct contains fields for parameters which are defined in Bind or for internal states which might get defined while running the application.

NewRootCommand also attaches NewVersionCommand to the application. It prints the compiled version of the application and other build parameters. These values need to be set by the build system via ldflags.

BUILD_XDST=$(pwd)/vendor/github.com/rebuy-de/rebuy-go-sdk/cmdutil
go build -ldflags "\
  -X '${BUILD_XDST}.BuildName=${NAME}' \
  -X '${BUILD_XDST}.BuildPackage=${PACKAGE}' \
  -X '${BUILD_XDST}.BuildVersion=${BUILD_VERSION}' \
  -X '${BUILD_XDST}.BuildDate=${BUILD_DATE}' \
  -X '${BUILD_XDST}.BuildHash=${BUILD_HASH}' \
  -X '${BUILD_XDST}.BuildEnvironment=${BUILD_ENVIRONMENT}' \

Index

Constants

View Source
const (
	ExitCodeOK           = 0
	ExitCodeGeneralError = 1
	ExitCodeUsage        = 2
	ExitCodeSDK          = 16
	ExitCodeCustom       = 32

	ExitCodeMultipleInterrupts = ExitCodeSDK + 0
)

Variables

View Source
var (
	Name       = "unknown"
	Version    = "unknown"
	GoModule   = "unknown"
	GoPackage  = "unknown"
	GoVersion  = "unknown"
	SDKVersion = "unknown"
	BuildDate  = "unknown"
	CommitDate = "unknown"
	CommitHash = "unknown"
)

The Build* variables are used by NewVersionCommand and NewRootCommand. They should be overwritten on build time by using ldflags.

Functions

func Exit

func Exit(code int)

Exit causes the current program to exit with the given status code. On the contrary to os.Exit, it respects defer statements. It requires the HandleExit function to be deferred in top of the main function.

Internally this is done by throwing a panic with the ExitCode type, which gets recovered in the HandleExit function.

func HandleExit

func HandleExit()

HandleExit recovers from Exit calls and terminates the current program with a proper exit code. It should get deferred at the beginning of the main function.

func Must

func Must(err error)

Must exits the application via Exit(1) and logs the error, if err does not equal nil. Additionally it logs the error with `%+v` to the debug log, so it can used together with github.com/pkg/errors to retrive more details about the error.

func New

func New(use, desc string, options ...Option) *cobra.Command

func NewVersionCommand

func NewVersionCommand() *cobra.Command

NewVersionCommand creates a Cobra command, which prints the version and other build parameters (see Build* variables) and exits.

func SignalContext

func SignalContext(ctx context.Context, signals ...os.Signal) context.Context

SignalContext returns a copy of the parent context that gets cancelled if the application gets any of the given signals.

func SignalRootContext

func SignalRootContext() context.Context

SignalRootContext returns a new empty context, that gets canneld on SIGINT or SIGTEM.

Types

type LoggerOption

type LoggerOption struct {
	JSONFormatter bool
	GELFLogger    bool
}

func (*LoggerOption) Bind

func (o *LoggerOption) Bind(cmd *cobra.Command) error

type Option

type Option func(*cobra.Command) error

func WithLogToGraylog

func WithLogToGraylog() Option

func WithLogToGraylogHostname

func WithLogToGraylogHostname(hostname string) Option

func WithLogVerboseFlag

func WithLogVerboseFlag() Option

func WithRun

func WithRun(run RunFuncWithContext) Option

func WithSubCommand

func WithSubCommand(sub *cobra.Command) Option

func WithVersionCommand

func WithVersionCommand() Option

func WithVersionLog

func WithVersionLog(level logrus.Level) Option

type RunFunc

type RunFunc func(cmd *cobra.Command, args []string)

type RunFuncWithContext

type RunFuncWithContext func(ctx context.Context, cmd *cobra.Command, args []string)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL