Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
f54b37e6db | |||
b5cdb71ebe | |||
f0598d8d8c | |||
faba797beb | |||
d10b82bb31 |
13
.gitlab-ci.yml
Normal file
13
.gitlab-ci.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# You can override the included template(s) by including variable overrides
|
||||||
|
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||||
|
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
|
||||||
|
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
|
||||||
|
# Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
|
||||||
|
# Note that environment variables can be set in several places
|
||||||
|
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
|
||||||
|
stages:
|
||||||
|
- test
|
||||||
|
sast:
|
||||||
|
stage: test
|
||||||
|
include:
|
||||||
|
- template: Security/SAST.gitlab-ci.yml
|
@ -1,7 +1,7 @@
|
|||||||
[![GoDoc](http://godoc.org/gitlab.com/CRThaze/sugar?status.svg)](http://godoc.org/gitlab.com/CRThaze/sugar)
|
|
||||||
|
|
||||||
# sugar
|
# sugar
|
||||||
|
|
||||||
|
[![GoDoc](http://godoc.org/gitlab.com/CRThaze/sugar?status.svg)](http://godoc.org/gitlab.com/CRThaze/sugar)
|
||||||
|
|
||||||
## Syntactic Sugar for Golang
|
## Syntactic Sugar for Golang
|
||||||
|
|
||||||
This package provides some convenience functions that help keep code clean.
|
This package provides some convenience functions that help keep code clean.
|
||||||
|
51
errors.go
Normal file
51
errors.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package sugar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrorWriter = os.Stderr
|
||||||
|
ErrorPrefix = "Error:"
|
||||||
|
ErrorFmt = "%s %+v"
|
||||||
|
StandardExitCode = 1
|
||||||
|
|
||||||
|
PanicFunc = func(err error) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ErrorPrintFunc = func(err error) {
|
||||||
|
fmt.Fprintf(ErrorWriter, ErrorFmt, ErrorPrefix, err)
|
||||||
|
}
|
||||||
|
ExitFunc = func(code int) {
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check takes an error and prints it if it is not nil; but will continue.
|
||||||
|
func Check(err error) {
|
||||||
|
if err != nil {
|
||||||
|
ErrorPrintFunc(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPanic takes an error and will panic if it is not nil.
|
||||||
|
func CheckPanic(err error) {
|
||||||
|
if err != nil {
|
||||||
|
PanicFunc(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckExit takes an error and will print it and exit if it is not nil.
|
||||||
|
// If no exit code is provided, the StandardExitCode will be used.
|
||||||
|
// Any exit codes beyond the first one one provided will be discarded.
|
||||||
|
func CheckExit(err error, code ...int) {
|
||||||
|
if err != nil {
|
||||||
|
ErrorPrintFunc(err)
|
||||||
|
exitCode := StandardExitCode
|
||||||
|
if len(code) > 0 {
|
||||||
|
exitCode = code[0]
|
||||||
|
}
|
||||||
|
ExitFunc(exitCode)
|
||||||
|
}
|
||||||
|
}
|
@ -1,117 +0,0 @@
|
|||||||
package exiter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"gitlab.com/CRThaze/cworthy/exit"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ExitCode represents an unsigned 16 bit integer used to define exit statuses.
|
|
||||||
type ExitCode exit.ExitT
|
|
||||||
|
|
||||||
const (
|
|
||||||
ExitOK ExitCode = ExitCode(exit.EX_OK) // 0: successful termination
|
|
||||||
ExitError ExitCode = ExitCode(1) // 1: generic error
|
|
||||||
ExitUsage ExitCode = ExitCode(exit.EX_USAGE) // 64: command line usage error
|
|
||||||
ExitDataErr ExitCode = ExitCode(exit.EX_DATAERR) // 65: data format error
|
|
||||||
ExitNoInput ExitCode = ExitCode(exit.EX_NOINPUT) // 66: cannot open input
|
|
||||||
ExitNoUser ExitCode = ExitCode(exit.EX_NOUSER) // 67: addressee unknown
|
|
||||||
ExitNoHost ExitCode = ExitCode(exit.EX_NOHOST) // 68: host name unknown
|
|
||||||
ExitUnavailable ExitCode = ExitCode(exit.EX_UNAVAILABLE) // 69: service unavailable
|
|
||||||
ExitSoftware ExitCode = ExitCode(exit.EX_SOFTWARE) // 70: internal software error
|
|
||||||
ExitOSErr ExitCode = ExitCode(exit.EX_OSERR) // 71: system error (e.g., can't fork)
|
|
||||||
ExitOSFile ExitCode = ExitCode(exit.EX_OSFILE) // 72: critical OS file missing
|
|
||||||
ExitCantCreate ExitCode = ExitCode(exit.EX_CANTCREAT) // 73: can't create (user) output file
|
|
||||||
ExitIOErr ExitCode = ExitCode(exit.EX_IOERR) // 74: input/output error
|
|
||||||
ExitTempFail ExitCode = ExitCode(exit.EX_TEMPFAIL) // 75: temp failure; user is invited to retry
|
|
||||||
ExitProtocol ExitCode = ExitCode(exit.EX_PROTOCOL) // 76: remote error in protocol
|
|
||||||
ExitNoPerm ExitCode = ExitCode(exit.EX_NOPERM) // 77: permission denied
|
|
||||||
ExitConfig ExitCode = ExitCode(exit.EX_CONFIG) // 78: configuration error
|
|
||||||
ExitAbort ExitCode = ExitCode(exit.EX_ABORT) // 134: process was aborted
|
|
||||||
)
|
|
||||||
|
|
||||||
var exitCodeDescs [^ExitCode(0)]string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
exitCodeDescs = exit.GetAllExitCodeDescriptions()
|
|
||||||
exitCodeDescs[ExitError] = "generic error"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Desc returns the description string for a given ExitCode.
|
|
||||||
func (e ExitCode) Desc() string {
|
|
||||||
return exitCodeDescs[e]
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterNewExitCode reserves an ExitCode and stores its corresponding
|
|
||||||
// description string for recall.
|
|
||||||
// It returns an error if the given integer is already in use or outside the
|
|
||||||
// usable range. And also if the description string is empty.
|
|
||||||
func RegisterNewExitCode(num int, desc string) (ExitCode, error) {
|
|
||||||
if num < 0 || num > int(^ExitCode(0)) {
|
|
||||||
return 0, errors.New("New exit code out of range")
|
|
||||||
}
|
|
||||||
if num == 0 || num == int(exit.EX_ABORT) || (num >= int(exit.EX__BASE) && num <= int(exit.EX__MAX)) {
|
|
||||||
return 0, errors.New("New exit code not user reservable")
|
|
||||||
}
|
|
||||||
if exitCodeDescs[num] != "" {
|
|
||||||
return 0, errors.New("New exit code already defined")
|
|
||||||
}
|
|
||||||
if desc == "" {
|
|
||||||
return 0, errors.New("New exit code description cannot be blank")
|
|
||||||
}
|
|
||||||
exitCodeDescs[num] = desc
|
|
||||||
return ExitCode(num), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterNextExitCode find the next available ExitCode number and associates the
|
|
||||||
// given description string with it.
|
|
||||||
// It returns an error if there are no more available numbers to reserve.
|
|
||||||
func RegisterNextExitCode(desc string) (ExitCode, error) {
|
|
||||||
nextUsable := func(n int) int {
|
|
||||||
n++
|
|
||||||
for n == int(exit.EX_ABORT) || (n >= int(exit.EX__BASE) && n <= int(exit.EX__MAX)) {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
for i := 1; i <= int(^ExitCode(0)); i = nextUsable(i) {
|
|
||||||
if exitCodeDescs[i] == "" {
|
|
||||||
exitCodeDescs[i] = desc
|
|
||||||
return ExitCode(i), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, errors.New("Available exit codes exhausted")
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapCodes(codes []string, offset int, m map[ExitCode]string) {
|
|
||||||
for i, desc := range codes {
|
|
||||||
if desc != "" {
|
|
||||||
m[ExitCode(i+offset)] = desc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserCodeMap returns a map of ExitCodes and their associated description strings
|
|
||||||
// with in the User-Definable range.
|
|
||||||
func UserCodeMap() (m map[ExitCode]string) {
|
|
||||||
m = map[ExitCode]string{}
|
|
||||||
mapCodes(exitCodeDescs[1:exit.EX__BASE], 1, m)
|
|
||||||
mapCodes(exitCodeDescs[exit.EX__MAX+1:exit.EX_ABORT], int(exit.EX__MAX)+1, m)
|
|
||||||
mapCodes(exitCodeDescs[exit.EX_ABORT+1:], int(exit.EX_ABORT)+1, m)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FullCodeMap returns a map of all ExitCodes and their associated description
|
|
||||||
// strings.
|
|
||||||
func FullCodeMap() (m map[ExitCode]string) {
|
|
||||||
m = map[ExitCode]string{}
|
|
||||||
mapCodes(exitCodeDescs[:], 0, m)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllExitCodeDescriptions returns an array of all possible ExitCode description
|
|
||||||
// strings (where the index is the exit code). Any unreserved ExitCodes are also
|
|
||||||
// included with empty string descriptions.
|
|
||||||
func GetAllExitCodeDescriptions() [^ExitCode(0)]string {
|
|
||||||
return exitCodeDescs
|
|
||||||
}
|
|
373
exiter/exiter.go
373
exiter/exiter.go
@ -1,373 +0,0 @@
|
|||||||
package exiter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"gitlab.com/CRThaze/sugar/sigar"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
exitSignalGroupName = "exitSignalGroup"
|
|
||||||
abortSignalGroupName = "abortSignalGroup"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ExitTaskFn (`func()`) is a closure represening something that should be
|
|
||||||
// executed right before exiting.
|
|
||||||
type ExitTaskFn sigar.TaskFn
|
|
||||||
|
|
||||||
// WriteFn is a format function that will write output somewhere.
|
|
||||||
// This could be a custom function or simply a logger function pointer.
|
|
||||||
type WriteFn func(format string, a ...interface{})
|
|
||||||
|
|
||||||
// ExiterConfig contains options for the creation of an Exiter.
|
|
||||||
type ExiterConfig struct {
|
|
||||||
ParentCtx context.Context
|
|
||||||
ExitSignals []os.Signal
|
|
||||||
SuppressAllExitMsg bool
|
|
||||||
ShowOKExitMsg bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exiter is a tool for cleanly and consistently exiting the program.
|
|
||||||
// Instead of creating the struct directly, use the NewExiter() or
|
|
||||||
// NewExiterWithConfig() factories.
|
|
||||||
type Exiter struct {
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
sigar *sigar.SignalCatcher
|
|
||||||
preCancelTasks *list.List
|
|
||||||
postCancelTasks *list.List
|
|
||||||
usagePrintFunc ExitTaskFn
|
|
||||||
exitInfoMsgWriteFunc WriteFn
|
|
||||||
exitErrMsgWriteFunc WriteFn
|
|
||||||
suppressAllExitMsg bool
|
|
||||||
showOKExitMsg bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultExiterConfig is the ExiterConfig used when calling the
|
|
||||||
// NewExiter() factory.
|
|
||||||
var DefaultExiterConfig = ExiterConfig{
|
|
||||||
ParentCtx: context.Background(),
|
|
||||||
ExitSignals: []os.Signal{
|
|
||||||
syscall.SIGINT,
|
|
||||||
syscall.SIGTERM,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewExiterWithConfig is an Exiter factory that allows you to
|
|
||||||
// customize the configuration.
|
|
||||||
// Be sure to call (Exiter).ListenForSignals() once created to handle
|
|
||||||
// all exits properly.
|
|
||||||
func NewExiterWithConfig(config ExiterConfig) (e *Exiter) {
|
|
||||||
e = &Exiter{
|
|
||||||
suppressAllExitMsg: config.SuppressAllExitMsg,
|
|
||||||
showOKExitMsg: config.ShowOKExitMsg,
|
|
||||||
}
|
|
||||||
e.ctx, e.cancel = context.WithCancel(config.ParentCtx)
|
|
||||||
e.sigar = sigar.NewSignalCatcher(
|
|
||||||
sigar.SignalGroups{
|
|
||||||
exitSignalGroupName: config.ExitSignals,
|
|
||||||
abortSignalGroupName: []os.Signal{syscall.SIGABRT},
|
|
||||||
},
|
|
||||||
e.ctx,
|
|
||||||
)
|
|
||||||
e.sigar.RegisterTask(exitSignalGroupName, func() {
|
|
||||||
e.ExitWithOK()
|
|
||||||
})
|
|
||||||
e.sigar.RegisterTask(abortSignalGroupName, func() {
|
|
||||||
e.Exit(ExitAbort)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewExiter is an Exiter factory that uses the DefaultExiterConfig.
|
|
||||||
// Be sure to call (Exiter).ListenForSignals() once created to handle
|
|
||||||
// all exits properly.
|
|
||||||
func NewExiter() *Exiter { return NewExiterWithConfig(DefaultExiterConfig) }
|
|
||||||
|
|
||||||
// SpawnExiter is a convenience function that calls both NewExiter()
|
|
||||||
// and (Exiter).ListenForSignals().
|
|
||||||
func SpawnExiter() (e *Exiter) {
|
|
||||||
e = NewExiter()
|
|
||||||
e.ListenForSignals()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SpawnExiterWithConfig is a convenience function that calls both
|
|
||||||
// NewExiterWithConfig() and (Exiter).ListenForSignals().
|
|
||||||
func SpawnExiterWithConfig(config ExiterConfig) (e *Exiter) {
|
|
||||||
e = NewExiterWithConfig(config)
|
|
||||||
e.ListenForSignals()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListenForSignals spawns a goroutine to catch signals so that the
|
|
||||||
// Exiter can gracefully shutdown the program.
|
|
||||||
func (e *Exiter) ListenForSignals() {
|
|
||||||
e.sigar.Listen()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPreTask adds a task to the queue that will be executed
|
|
||||||
// prior to unblocking goroutines blocked at (Exiter).Wait().
|
|
||||||
func (e *Exiter) RegisterPreTask(fn ExitTaskFn) {
|
|
||||||
e.preCancelTasks.PushBack(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPostTask adds a task to the queue that will be executed
|
|
||||||
// after unblocking goroutines blocked at (Exiter).Wait().
|
|
||||||
func (e *Exiter) RegisterPostTask(fn ExitTaskFn) {
|
|
||||||
e.postCancelTasks.PushBack(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUsageFunc tells the Exiter what function to call to print the
|
|
||||||
// program's help/usage information.
|
|
||||||
func (e *Exiter) SetUsageFunc(usage ExitTaskFn) {
|
|
||||||
e.usagePrintFunc = usage
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitOnPanic is a convenience function that is meant to be defered
|
|
||||||
// as a means to gracefully exit in the event of a panic.
|
|
||||||
func (e *Exiter) ExitOnPanic() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
e.exitErrMsgWriteFunc("Paniced: %v", r)
|
|
||||||
e.ExitWithSoftwareErr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Exiter) exit(code ExitCode) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
e.exitErrMsgWriteFunc(
|
|
||||||
"Paniced attempting to cleanup tasks (with exit code: %d): %v", code, r,
|
|
||||||
)
|
|
||||||
os.Exit(int(ExitSoftware))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for x := e.preCancelTasks.Front(); x != nil; x = x.Next() {
|
|
||||||
x.Value.(ExitTaskFn)()
|
|
||||||
}
|
|
||||||
e.cancel()
|
|
||||||
for x := e.postCancelTasks.Front(); x != nil; x = x.Next() {
|
|
||||||
x.Value.(ExitTaskFn)()
|
|
||||||
}
|
|
||||||
os.Exit(int(code))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Exiter) writeExitMsg(code ExitCode, fn WriteFn, err ...error) {
|
|
||||||
if !e.suppressAllExitMsg && fn != nil {
|
|
||||||
if code == ExitOK && !e.showOKExitMsg {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(err) != 0 && err[0] != nil {
|
|
||||||
fn("%s: %v", ExitSoftware.Desc(), err[0])
|
|
||||||
} else {
|
|
||||||
fn("%s", ExitSoftware.Desc())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithOK calls (Exiter).Exit() with ExitOK (0).
|
|
||||||
//
|
|
||||||
// It is recommended to use this for all normal exit conditions.
|
|
||||||
func (e *Exiter) ExitWithOK() {
|
|
||||||
e.Exit(ExitOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithUsageOK prints the help/usage and calls (Exiter).Exit() with
|
|
||||||
// ExitOK (0). If no UsageFunc was set, it will not print anything.
|
|
||||||
//
|
|
||||||
// It is recommended to use this when responding to help arguments.
|
|
||||||
func (e *Exiter) ExitWithUsageOK() {
|
|
||||||
e.RegisterPostTask(e.usagePrintFunc)
|
|
||||||
e.Exit(ExitOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithError calls (Exiter).Exit() with ExitError (1).
|
|
||||||
//
|
|
||||||
// It is recommended to use this when no other exit code makes sense.
|
|
||||||
func (e *Exiter) ExitWithError(err ...error) {
|
|
||||||
e.Exit(ExitError, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithUsageErr prints the help/usage and calls (Exiter).Exit() with
|
|
||||||
// ExitUsage (64). If no UsageFunc was set, it will not print anything.
|
|
||||||
//
|
|
||||||
// It is recommended to use this when bad arguments are passed.
|
|
||||||
func (e *Exiter) ExitWithUsageErr(err ...error) {
|
|
||||||
e.Exit(ExitUsage, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithDataErr calls (Exiter).Exit() with ExitDataErr (65).
|
|
||||||
//
|
|
||||||
// It is recommended to use this when data provided through any means
|
|
||||||
// is invalid but NOT when an argument fails basic validation
|
|
||||||
// (though schema errors in complex arguments could be appropriate UNLESS
|
|
||||||
// they are configuartion settings, in which case you should use
|
|
||||||
// (Exiter).ExitWithConfigErr()).
|
|
||||||
func (e *Exiter) ExitWithDataErr(err ...error) {
|
|
||||||
e.Exit(ExitDataErr, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithNoInputErr calls (Exiter).Exit() with ExitNoInput (66).
|
|
||||||
//
|
|
||||||
// It is recommended to use this only when an input channel (such as
|
|
||||||
// a pipe, os.STDIN, or virtual device) cannot be opened or read when
|
|
||||||
// needed.
|
|
||||||
// Permissions errors should use (Exiter).ExitWithNoPermErr() instead.
|
|
||||||
func (e *Exiter) ExitWithNoInputErr(err ...error) {
|
|
||||||
e.Exit(ExitNoInput, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithNoUserErr calls (Exiter).Exit() with ExitNoUser (67).
|
|
||||||
//
|
|
||||||
// It is recommended to use this only when attempting to address data to
|
|
||||||
// a local or network user, and no suitable match can be found. Mostly
|
|
||||||
// useful for multi-user machines or intranet communication not using
|
|
||||||
// a protocol that has its own suitable statuses for this.
|
|
||||||
func (e *Exiter) ExitWithNoUserErr(err ...error) {
|
|
||||||
e.Exit(ExitNoUser, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithNoHostErr calls (Exiter).Exit() with ExitNoHost (68).
|
|
||||||
//
|
|
||||||
// It is recommended to use this when a provided hostname cannot be resolved.
|
|
||||||
func (e *Exiter) ExitWithNoHostErr(err ...error) {
|
|
||||||
e.Exit(ExitNoHost, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithUnavailableErr calls (Exiter).Exit() with ExitUnavailable (69) (nice).
|
|
||||||
//
|
|
||||||
// It is recommended to use this for client programs which cannot communicate
|
|
||||||
// with another process or remote service.
|
|
||||||
func (e *Exiter) ExitWithUnavailableErr(err ...error) {
|
|
||||||
e.Exit(ExitUnavailable, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithSoftwareErr calls (Exiter).Exit() with ExitSoftware (70).
|
|
||||||
//
|
|
||||||
// It is recommended to use this as a more meaningful alternative to
|
|
||||||
// ExitWithError() or for more graceful exits in the event of a Panic.
|
|
||||||
// For the later case consider deferring (Exiter).ExitOnPanic() which
|
|
||||||
// will call this function in that event.
|
|
||||||
func (e *Exiter) ExitWithSoftwareErr(err ...error) {
|
|
||||||
e.Exit(ExitSoftware, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithOSErr calls (Exiter).Exit() with ExitOSErr (71).
|
|
||||||
//
|
|
||||||
// It is recommended to use this only when unable to continue due to
|
|
||||||
// encountering a generalized system error.
|
|
||||||
func (e *Exiter) ExitWithOSErr(err ...error) {
|
|
||||||
e.Exit(ExitOSErr, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithOSFile calls (Exiter).Exit() with ExitOSFile (72).
|
|
||||||
//
|
|
||||||
// It is recommended to use this only when unable to find a file or directory
|
|
||||||
// not owned by this or any othe program but is part of the general
|
|
||||||
// system configuration. Such as a standard FHS directory, or a device file
|
|
||||||
// in /dev.
|
|
||||||
func (e *Exiter) ExitWithOSFile(err ...error) {
|
|
||||||
e.Exit(ExitOSFile, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithCantCreateErr calls (Exiter).Exit() with ExitCantCreate (73).
|
|
||||||
//
|
|
||||||
// It is recommended to use this only when the creation of a user output
|
|
||||||
// file is impossible for any reason and the program is unable to continue.
|
|
||||||
func (e *Exiter) ExitWithCantCreateErr(err ...error) {
|
|
||||||
e.Exit(ExitCantCreate, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithIOErr calls (Exiter).Exit() with ExitIOErr (74).
|
|
||||||
//
|
|
||||||
// It is recommended to use this only when unable to interact with I/O
|
|
||||||
// hardware, failure to open or write to an output stream, or when
|
|
||||||
// encountering an interuption from an already open input stream
|
|
||||||
// (otherwise use (Exiter).ExitWithNoInputErr()).
|
|
||||||
// Permissions errors should use (Exiter).ExitWithNoPermErr() instead.
|
|
||||||
func (e *Exiter) ExitWithIOErr(err ...error) {
|
|
||||||
e.Exit(ExitIOErr, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithTempFailErr calls (Exiter).Exit() with ExitTempFail (75).
|
|
||||||
//
|
|
||||||
// It is recommended to use this when the program chooses (by design) to
|
|
||||||
// terminate instead of retrying some operation that is expected to have
|
|
||||||
// failed merely intermittently.
|
|
||||||
func (e *Exiter) ExitWithTempFailErr(err ...error) {
|
|
||||||
e.Exit(ExitTempFail, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithProtocolErr calls (Exiter).Exit() with ExitProtocol (76).
|
|
||||||
//
|
|
||||||
// It is recommended to use this only when some communication with a
|
|
||||||
// process or network location fails due to a protocol violation from
|
|
||||||
// the other communication participant. And example would be receiving
|
|
||||||
// a response that is not inline with the protocol (like a SYN or ACK
|
|
||||||
// in response to the same instead of a SYN/ACK when performing a TCP
|
|
||||||
// handshake).
|
|
||||||
func (e *Exiter) ExitWithProtocolErr(err ...error) {
|
|
||||||
e.Exit(ExitTempFail, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithNoPermErr calls (Exiter).Exit() with ExitNoPerm (77).
|
|
||||||
//
|
|
||||||
// It is recommended to use this whenever user or credential Permissions
|
|
||||||
// do not allow you to continue.
|
|
||||||
func (e *Exiter) ExitWithNoPermErr(err ...error) {
|
|
||||||
e.Exit(ExitNoPerm, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithConfigErr calls (Exiter).Exit() with ExitConfig (78).
|
|
||||||
//
|
|
||||||
// It is recommended to use this whenever a schema validation is encountered
|
|
||||||
// in a config file or other serialiezed configuarion source.
|
|
||||||
func (e *Exiter) ExitWithConfigErr(err ...error) {
|
|
||||||
e.Exit(ExitConfig, err...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitWithConfigErr exits with the given ExitCode, and prints the
|
|
||||||
// first error provided (if any).
|
|
||||||
func (e *Exiter) Exit(code ExitCode, err ...error) {
|
|
||||||
switch code {
|
|
||||||
case ExitOK:
|
|
||||||
e.RegisterPostTask(func() {
|
|
||||||
e.writeExitMsg(code, e.exitInfoMsgWriteFunc)
|
|
||||||
})
|
|
||||||
e.exit(code)
|
|
||||||
case ExitUsage:
|
|
||||||
e.RegisterPostTask(func() {
|
|
||||||
e.writeExitMsg(code, e.exitErrMsgWriteFunc, err...)
|
|
||||||
})
|
|
||||||
e.RegisterPostTask(e.usagePrintFunc)
|
|
||||||
e.exit(code)
|
|
||||||
default:
|
|
||||||
e.RegisterPostTask(func() {
|
|
||||||
e.writeExitMsg(code, e.exitErrMsgWriteFunc, err...)
|
|
||||||
})
|
|
||||||
e.exit(code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait blocks till (Exiter).Exit() is called.
|
|
||||||
// It is recommended to call this function in a select block case in
|
|
||||||
// all goroutines to ensure they are properly terminated.
|
|
||||||
func (e *Exiter) Wait() {
|
|
||||||
<-e.ctx.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCancelableContext returns the underlying cancelable context
|
|
||||||
// used by the Exiter.
|
|
||||||
func (e *Exiter) GetCancelableContext() context.Context {
|
|
||||||
return e.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSignalCatcher returns the underlying *sigar.SignalCatcher
|
|
||||||
// used by the Exiter.
|
|
||||||
func (e *Exiter) GetSignalCatcher() *sigar.SignalCatcher {
|
|
||||||
return e.sigar
|
|
||||||
}
|
|
2
go.mod
2
go.mod
@ -1,5 +1,3 @@
|
|||||||
module gitlab.com/CRThaze/sugar
|
module gitlab.com/CRThaze/sugar
|
||||||
|
|
||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require gitlab.com/CRThaze/cworthy v0.0.4
|
|
||||||
|
12
go.sum
12
go.sum
@ -1,12 +0,0 @@
|
|||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
github.com/novalagung/go-eek v1.0.1/go.mod h1:Q9MQtLRP21rO5/7UmIJUr5URe1ysI+K/wM3zYJCxPxo=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
|
||||||
gitlab.com/CRThaze/cworthy v0.0.4 h1:fvbM0UMdYxhHSE19GyIFuGly/hM9UT3Pw3E1urHKt1I=
|
|
||||||
gitlab.com/CRThaze/cworthy v0.0.4/go.mod h1:eaZtqJCLikd8gegTlbxaOvo8623mHhIoOySY00RfrZQ=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
@ -1,11 +1,6 @@
|
|||||||
// Package sugar provides convenience functions for keeping code clean and less repetitive.
|
// Package sugar provides convenience functions for keeping code clean and less repetitive.
|
||||||
package sugar
|
package sugar
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Literal Addressers
|
* Literal Addressers
|
||||||
*/
|
*/
|
||||||
@ -104,36 +99,3 @@ func StrPtr(v string) *string {
|
|||||||
func BoolPtr(v bool) *bool {
|
func BoolPtr(v bool) *bool {
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Error Handling
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Check takes an error and prints it if it is not nil; but will continue.
|
|
||||||
func Check(err error) {
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%+v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckLog takes an error and a format function for leveraging a custom printer like a logger.
|
|
||||||
func CheckLog(err error, fn func(format string, a ...interface{})) {
|
|
||||||
if err != nil {
|
|
||||||
fn("%+v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckPanic takes an error and will panic if it is not nil.
|
|
||||||
func CheckPanic(err error) {
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckExit takes an error and will print it and exit with status 1 if it is not nil.
|
|
||||||
func CheckExit(err error) {
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%+v", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
26
sets.go
Normal file
26
sets.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package sugar
|
||||||
|
|
||||||
|
// StrSet a set type for strings.
|
||||||
|
type StrSet map[string]struct{}
|
||||||
|
|
||||||
|
// Add adds a new key to the set if it does not already exist.
|
||||||
|
func (s *StrSet) Add(str string) {
|
||||||
|
(*s)[str] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has checks if the given string is present in the set.
|
||||||
|
func (s *StrSet) Has(str string) bool {
|
||||||
|
if _, ok := (*s)[str]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHas behaves like Add, but returns true if the key already existed.
|
||||||
|
func (s *StrSet) AddHas(str string) bool {
|
||||||
|
if ok := s.Has(str); ok {
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
s.Add(str)
|
||||||
|
return false
|
||||||
|
}
|
@ -1,87 +0,0 @@
|
|||||||
package sigar
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
)
|
|
||||||
|
|
||||||
type taskMap map[string]*list.List
|
|
||||||
|
|
||||||
type SignalGroups map[string][]os.Signal
|
|
||||||
|
|
||||||
func (sg SignalGroups) initTaskMap(tm taskMap) {
|
|
||||||
for groupName := range sg {
|
|
||||||
tm[groupName] = list.New()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TaskFn func()
|
|
||||||
|
|
||||||
type SignalCatcher struct {
|
|
||||||
signals SignalGroups
|
|
||||||
tasks map[string]*list.List
|
|
||||||
exitCtx context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSignalCatcher(sg SignalGroups, exitCtx context.Context) (sc *SignalCatcher) {
|
|
||||||
if sg == nil {
|
|
||||||
sg = SignalGroups{}
|
|
||||||
}
|
|
||||||
sc = &SignalCatcher{
|
|
||||||
signals: sg,
|
|
||||||
exitCtx: exitCtx,
|
|
||||||
tasks: taskMap{},
|
|
||||||
}
|
|
||||||
sg.initTaskMap(sc.tasks)
|
|
||||||
if exitCtx == nil {
|
|
||||||
// Create a cancelable context and discard the cancel function, as we will never use it.
|
|
||||||
sc.exitCtx, _ = context.WithCancel(context.Background())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func SpawnSignalCatcher(sg SignalGroups, exitCtx context.Context) (sc *SignalCatcher) {
|
|
||||||
sc = NewSignalCatcher(sg, exitCtx)
|
|
||||||
sc.Listen()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *SignalCatcher) runTasks(group string) {
|
|
||||||
taskList := sc.tasks[group]
|
|
||||||
for e := taskList.Front(); e != nil; e = e.Next() {
|
|
||||||
e.Value.(TaskFn)()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *SignalCatcher) Listen() {
|
|
||||||
for groupName, signalSet := range sc.signals {
|
|
||||||
// Prevent loop closure by copying variables.
|
|
||||||
group := groupName
|
|
||||||
signals := signalSet
|
|
||||||
go func() {
|
|
||||||
sigChan := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigChan, signals...)
|
|
||||||
select {
|
|
||||||
case <-sigChan:
|
|
||||||
sc.runTasks(group)
|
|
||||||
case <-sc.exitCtx.Done():
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *SignalCatcher) RegisterTaskGroup(group string, signals []os.Signal) {
|
|
||||||
sc.signals[group] = signals
|
|
||||||
sc.tasks[group] = list.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *SignalCatcher) RegisterTask(group string, fn TaskFn) error {
|
|
||||||
if _, ok := sc.tasks[group]; !ok {
|
|
||||||
return errors.New("No such task group")
|
|
||||||
}
|
|
||||||
sc.tasks[group].PushBack(fn)
|
|
||||||
return nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user