-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from rebuy-de/reduce-copy-pasta-overhead
reduce copy pasta overhead on all applications
- Loading branch information
Showing
4 changed files
with
209 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package cmdutil | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
graylog "gopkg.in/gemnasium/logrus-graylog-hook.v2" | ||
|
||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
// Application provides the basic behaviour for NewRootCommand. | ||
type Application interface { | ||
// Run contains the actual application code. It is equivalent to | ||
// the Run command of Cobra. | ||
Run(cmd *cobra.Command, args []string) | ||
|
||
// Bind is used to bind command line flags to fields of the | ||
// application struct. | ||
Bind(cmd *cobra.Command) | ||
} | ||
|
||
// NewRootCommand creates a Cobra command, which reflects our current best | ||
// practices. It adds a verbose flag, sets up logrus and registers a Graylog | ||
// hook. Also it registers the NewVersionCommand and prints the version on | ||
// startup. | ||
func NewRootCommand(app Application) *cobra.Command { | ||
var ( | ||
gelfAddress string | ||
verbose bool | ||
) | ||
|
||
cmd := &cobra.Command{ | ||
Use: BuildName, | ||
Run: app.Run, | ||
|
||
PersistentPreRun: func(cmd *cobra.Command, args []string) { | ||
log.SetLevel(log.InfoLevel) | ||
|
||
if verbose { | ||
log.SetLevel(log.DebugLevel) | ||
} | ||
|
||
if gelfAddress != "" { | ||
labels := map[string]interface{}{ | ||
"facility": BuildName, | ||
"version": BuildVersion, | ||
"commit-sha": BuildHash, | ||
} | ||
hook := graylog.NewGraylogHook(gelfAddress, labels) | ||
hook.Level = log.DebugLevel | ||
log.AddHook(hook) | ||
} | ||
|
||
log.WithFields(log.Fields{ | ||
"Version": BuildVersion, | ||
"Date": BuildDate, | ||
"Commit": BuildHash, | ||
}).Infof("%s started", BuildName) | ||
}, | ||
|
||
PersistentPostRun: func(cmd *cobra.Command, args []string) { | ||
log.Infof("%s stopped", BuildName) | ||
}, | ||
} | ||
|
||
cmd.PersistentFlags().BoolVarP( | ||
&verbose, "verbose", "v", false, | ||
`Show debug logs.`) | ||
cmd.PersistentFlags().StringVar( | ||
&gelfAddress, "gelf-address", "", | ||
`Address to Graylog for logging (format: "ip:port").`) | ||
|
||
app.Bind(cmd) | ||
|
||
cmd.AddCommand(NewVersionCommand()) | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// 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}' \ | ||
package cmdutil |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package cmdutil | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
// The Build* variables are used by NewVersionCommand and NewRootCommand. They | ||
// should be overwritten on build time by using ldflags. | ||
var ( | ||
BuildVersion = "unknown" | ||
BuildPackage = "unknown" | ||
BuildDate = "unknown" | ||
BuildHash = "unknown" | ||
BuildEnvironment = "unknown" | ||
BuildName = "unknown" | ||
) | ||
|
||
// NewVersionCommand creates a Cobra command, which prints the version | ||
// and other build parameters (see Build* variables) and exits. | ||
func NewVersionCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "version", | ||
Short: "shows version of this application", | ||
PersistentPreRun: func(cmd *cobra.Command, args []string) {}, | ||
PersistentPostRun: func(cmd *cobra.Command, args []string) {}, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
fmt.Printf("name: %s\n", BuildName) | ||
fmt.Printf("package: %s\n", BuildPackage) | ||
fmt.Printf("version: %s\n", BuildVersion) | ||
fmt.Printf("build date: %s\n", BuildDate) | ||
fmt.Printf("scm hash: %s\n", BuildHash) | ||
fmt.Printf("environment: %s\n", BuildEnvironment) | ||
}, | ||
} | ||
|
||
return cmd | ||
} |