Initial commit
commit
e02498ec0e
|
@ -0,0 +1,5 @@
|
||||||
|
# Emacs
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
*.test
|
|
@ -0,0 +1,24 @@
|
||||||
|
# BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2022, Sean Hickey (Wisellama)
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,11 @@
|
||||||
|
all: linter test
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test
|
||||||
|
|
||||||
|
lint: linter
|
||||||
|
linter:
|
||||||
|
golangci-lint run
|
||||||
|
|
||||||
|
goimports_everything:
|
||||||
|
find . -name "*.go" -exec goimports -w {} \;
|
|
@ -0,0 +1,13 @@
|
||||||
|
# gopackagebase
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get git.wisellama.rocks/Wisellama/gopackagebase@v0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
This modules contains basic setup and initialization functions that
|
||||||
|
are useful to run at the beginning of a program. This integrates with
|
||||||
|
[gosimpleconf][1] to also support parsing config files.
|
||||||
|
|
||||||
|
I wanted to create this package to centralize some of the basic setup
|
||||||
|
boilerplate I had started to collect in my Go projects. This
|
||||||
|
repository may be considered an anti-pattern, but it fits my use-case.
|
|
@ -0,0 +1,5 @@
|
||||||
|
module git.wisellama.rocks/gopackagebase
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require git.wisellama.rocks/Wisellama/gosimpleconf v0.1.0
|
|
@ -0,0 +1,2 @@
|
||||||
|
git.wisellama.rocks/Wisellama/gosimpleconf v0.1.0 h1:Z2FAzARct8ShV4NSueC/y+PyuSQVcyo4WnW7GoZ9L10=
|
||||||
|
git.wisellama.rocks/Wisellama/gosimpleconf v0.1.0/go.mod h1:Gg1vUTBRZD7qcXvdF8L50PsnL9coLt/XbWa5BwSDN/M=
|
|
@ -0,0 +1,53 @@
|
||||||
|
package gopackagebase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.wisellama.rocks/Wisellama/gosimpleconf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseConfig struct {
|
||||||
|
Ctx context.Context
|
||||||
|
Cancel func()
|
||||||
|
ConfigMap gosimpleconf.ConfigMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize will configure all of the base stuff that we want
|
||||||
|
// commonly set up at the beginning of a Go project.
|
||||||
|
func Initialize(configFile string, defaultConfig gosimpleconf.ConfigMap) (*BaseConfig, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx, cancel := InitSignalHandler()
|
||||||
|
|
||||||
|
err = InitRNG()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
configMap, err := gosimpleconf.ConfigureWithDefaults(configFile, defaultConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error during configure: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeToFile := gosimpleconf.Bool(configMap["log.writeToFile"])
|
||||||
|
logFilename := configMap["log.file"]
|
||||||
|
|
||||||
|
logWriter, err := SetupLogging(writeToFile, logFilename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fullCancel := func() {
|
||||||
|
logWriter.Cleanup()
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
baseConfig := BaseConfig{
|
||||||
|
Ctx: ctx,
|
||||||
|
Cancel: fullCancel,
|
||||||
|
ConfigMap: configMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &baseConfig, nil
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package gopackagebase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type logWriter struct {
|
||||||
|
writeToFile bool
|
||||||
|
logFile *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *logWriter) Write(bytes []byte) (int, error) {
|
||||||
|
t := time.Now().UTC().Format(time.RFC3339)
|
||||||
|
return fmt.Fprintf(w.logFile, "%v %v", t, string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *logWriter) Cleanup() {
|
||||||
|
defer w.logFile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupLogging(writeToFile bool, logFilename string) (*logWriter, error) {
|
||||||
|
var err error
|
||||||
|
log.SetFlags(0)
|
||||||
|
|
||||||
|
logFile := os.Stdout
|
||||||
|
if writeToFile {
|
||||||
|
logFile, err = os.OpenFile(logFilename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer := &logWriter{
|
||||||
|
writeToFile: writeToFile,
|
||||||
|
logFile: logFile,
|
||||||
|
}
|
||||||
|
log.SetOutput(writer)
|
||||||
|
|
||||||
|
return writer, nil
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package gopackagebase
|
||||||
|
|
||||||
|
import (
|
||||||
|
crypto_rand "crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
math_rand "math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitRNG will grab some cryptographically secure bytes to use as the
|
||||||
|
// seed for the non-crypto random number generator. Suggested on
|
||||||
|
// StackOverflow to avoid time-based seeds:
|
||||||
|
// https://stackoverflow.com/a/54491783
|
||||||
|
func InitRNG() error {
|
||||||
|
var b [8]byte
|
||||||
|
_, err := crypto_rand.Read(b[:])
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error seeding random number generator: %w", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
math_rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package gopackagebase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitSignalHandler() (context.Context, func()) {
|
||||||
|
// Setup some initial context for gracefully killing the program
|
||||||
|
// with system interrupt signals like ctrl+c
|
||||||
|
// https://pace.dev/blog/2020/02/17/repond-to-ctrl-c-interrupt-signals-gracefully-with-context-in-golang-by-mat-ryer.html
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
signalChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signalChan, os.Interrupt)
|
||||||
|
|
||||||
|
// Start a goroutine to listen for signal interrupts
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-signalChan:
|
||||||
|
// Graceful exit on ctrl+c
|
||||||
|
log.Printf("Attempting to exit gracefully...\n")
|
||||||
|
cancel()
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
<-signalChan // Hard exit on second ctrl+c
|
||||||
|
log.Printf("Hard kill\n")
|
||||||
|
os.Exit(2)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Return a new cancel function that also sends the stop signal.
|
||||||
|
killFunc := func() {
|
||||||
|
signal.Stop(signalChan)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx, killFunc
|
||||||
|
}
|
Loading…
Reference in New Issue