Skip to content

Commit

Permalink
story(app): add finalize lifecycle hooks (#29)
Browse files Browse the repository at this point in the history
* added finalizer support

* added finalizer support again
  • Loading branch information
erictg authored Dec 15, 2023
1 parent f0bbe96 commit 5849c1e
Showing 1 changed file with 60 additions and 35 deletions.
95 changes: 60 additions & 35 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"io"
"os"
"os/signal"
"strings"

"github.com/z5labs/app/pkg/config"
"github.com/z5labs/app/pkg/otelconfig"
Expand All @@ -26,9 +27,20 @@ type Runtime interface {
Run(context.Context) error
}

type FinalizerFunc func() error

type finalizer struct {
Finalizers []FinalizerFunc
}

// BuildContext
type BuildContext struct {
Config config.Manager
Config config.Manager
finalizer *finalizer
}

func (b BuildContext) RegisterFinalizers(f ...FinalizerFunc) {
b.finalizer.Finalizers = append(b.finalizer.Finalizers, f...)
}

// RuntimeBuilder
Expand Down Expand Up @@ -119,46 +131,25 @@ func (app *App) Run(args ...string) error {

func buildCmd(app *App) *cobra.Command {
rs := make([]Runtime, len(app.rbs))
bc := BuildContext{finalizer: &finalizer{Finalizers: []FinalizerFunc{finalizeOtel}}}
return &cobra.Command{
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
defer errRecover(&err)

if app.cfgSrc == nil {
otelIniter, err := app.otelIniterFunc(BuildContext{})
if app.cfgSrc != nil {
b, err := readAllAndTryClose(app.cfgSrc)
if err != nil {
return err
}
tp, err := otelIniter.Init()

m, err := config.Read(bytes.NewReader(b), config.Language(config.YAML))
if err != nil {
return err
}
otel.SetTracerProvider(tp)

for i, rb := range app.rbs {
r, err := rb.Build(BuildContext{})
if err != nil {
return err
}
if r == nil {
return errors.New("nil runtime")
}
rs[i] = r
}
return nil
}

b, err := readAllAndTryClose(app.cfgSrc)
if err != nil {
return err
}

m, err := config.Read(bytes.NewReader(b), config.Language(config.YAML))
if err != nil {
return err
bc.Config = m
}
bc := BuildContext{Config: m}

otelIniter, err := app.otelIniterFunc(BuildContext{})
otelIniter, err := app.otelIniterFunc(bc)
if err != nil {
return err
}
Expand All @@ -178,6 +169,7 @@ func buildCmd(app *App) *cobra.Command {
}
rs[i] = r
}

return nil
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
Expand All @@ -201,18 +193,51 @@ func buildCmd(app *App) *cobra.Command {
return g.Wait()
},
PostRunE: func(cmd *cobra.Command, args []string) error {
tp := otel.GetTracerProvider()
stp, ok := tp.(interface {
Shutdown(context.Context) error
})
if !ok {
// will always have at least one finalizer for otel
me := &multiError{}
for _, f := range bc.finalizer.Finalizers {
err := f()
if err != nil {
me.errors = append(me.errors, err)
}
}

if len(me.errors) == 0 {
return nil
}
return stp.Shutdown(context.Background())
return me
},
}
}

type multiError struct {
errors []error
}

func (m multiError) Error() string {
if len(m.errors) == 0 {
return ""
}

e := ""
for _, err := range m.errors {
e += err.Error() + ";"
}

return strings.TrimSuffix(e, ";")
}

func finalizeOtel() error {
tp := otel.GetTracerProvider()
stp, ok := tp.(interface {
Shutdown(context.Context) error
})
if !ok {
return nil
}
return stp.Shutdown(context.Background())
}

func readAllAndTryClose(r io.Reader) ([]byte, error) {
defer func() {
rc, ok := r.(io.ReadCloser)
Expand Down

0 comments on commit 5849c1e

Please sign in to comment.