2018-05-28 08:00:31 +00:00
|
|
|
// Package m is the glue which holds all the other packages in this project
|
|
|
|
// together. While other packages in this project are intended to be able to be
|
|
|
|
// used separately and largely independently, this package combines them in ways
|
|
|
|
// which I specifically like.
|
|
|
|
package m
|
|
|
|
|
|
|
|
import (
|
2019-02-05 20:18:17 +00:00
|
|
|
"context"
|
2019-01-25 03:04:58 +00:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
|
2018-05-28 08:00:31 +00:00
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mcfg"
|
2019-02-09 19:08:30 +00:00
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mctx"
|
2019-01-25 03:04:58 +00:00
|
|
|
"github.com/mediocregopher/mediocre-go-lib/merr"
|
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mlog"
|
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mrun"
|
2018-05-28 08:00:31 +00:00
|
|
|
)
|
|
|
|
|
2019-04-03 03:35:45 +00:00
|
|
|
type cfgSrcKey int
|
2019-01-25 03:04:58 +00:00
|
|
|
|
2019-04-03 03:35:45 +00:00
|
|
|
// ProcessContext returns a Context which should be used as the root Context
|
|
|
|
// when implementing most processes.
|
2019-02-03 00:27:28 +00:00
|
|
|
//
|
|
|
|
// The returned Context will automatically handle setting up global
|
|
|
|
// configuration parameters like "log-level", as well as an http endpoint where
|
|
|
|
// debug information about the running process can be accessed.
|
2019-04-03 03:35:45 +00:00
|
|
|
func ProcessContext() context.Context {
|
2019-02-05 20:18:17 +00:00
|
|
|
ctx := context.Background()
|
2019-02-03 00:27:28 +00:00
|
|
|
|
2019-04-03 03:35:45 +00:00
|
|
|
// embed confuration source which should be used into the context.
|
|
|
|
ctx = context.WithValue(ctx, cfgSrcKey(0), mcfg.Source(mcfg.SourceCLI{}))
|
|
|
|
|
2019-02-03 00:27:28 +00:00
|
|
|
// set up log level handling
|
2019-02-05 20:18:17 +00:00
|
|
|
logger := mlog.NewLogger()
|
2019-02-09 19:08:30 +00:00
|
|
|
ctx = mlog.WithLogger(ctx, logger)
|
|
|
|
ctx, logLevelStr := mcfg.WithString(ctx, "log-level", "info", "Maximum log level which will be printed.")
|
|
|
|
ctx = mrun.WithStartHook(ctx, func(context.Context) error {
|
2019-02-03 00:27:28 +00:00
|
|
|
logLevel := mlog.LevelFromString(*logLevelStr)
|
|
|
|
if logLevel == nil {
|
2019-02-27 18:05:51 +00:00
|
|
|
ctx := mctx.Annotate(ctx, "log-level", *logLevelStr)
|
|
|
|
return merr.New("invalid log level", ctx)
|
2019-02-03 00:27:28 +00:00
|
|
|
}
|
2019-02-05 20:18:17 +00:00
|
|
|
logger.SetMaxLevel(logLevel)
|
2019-02-03 00:27:28 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return ctx
|
|
|
|
}
|
2019-01-25 03:04:58 +00:00
|
|
|
|
2019-04-03 03:35:45 +00:00
|
|
|
// ServiceContext extends ProcessContext so that it better supports long running
|
|
|
|
// processes which are expected to handle requests from outside clients.
|
|
|
|
//
|
|
|
|
// Additional behavior it adds includes setting up an http endpoint where debug
|
|
|
|
// information about the running process can be accessed.
|
|
|
|
func ServiceContext() context.Context {
|
|
|
|
ctx := ProcessContext()
|
|
|
|
|
|
|
|
// services expect to use many different configuration sources
|
|
|
|
ctx = context.WithValue(ctx, cfgSrcKey(0), mcfg.Source(mcfg.Sources{
|
|
|
|
mcfg.SourceEnv{},
|
|
|
|
mcfg.SourceCLI{},
|
|
|
|
}))
|
|
|
|
|
|
|
|
// TODO set up the debug endpoint.
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2019-02-24 22:23:38 +00:00
|
|
|
// Start performs the work of populating configuration parameters and triggering
|
|
|
|
// the start event. It will return once the Start event has completed running.
|
|
|
|
func Start(ctx context.Context) {
|
2019-04-03 03:35:45 +00:00
|
|
|
src, _ := ctx.Value(cfgSrcKey(0)).(mcfg.Source)
|
|
|
|
if src == nil {
|
|
|
|
mlog.Fatal("ctx not sourced from m package", ctx)
|
|
|
|
}
|
|
|
|
|
2019-02-24 22:25:53 +00:00
|
|
|
// no logging should happen before populate, primarily because log-level
|
|
|
|
// hasn't been populated yet, but also because it makes help output on cli
|
|
|
|
// look weird.
|
2019-04-03 03:35:45 +00:00
|
|
|
if err := mcfg.Populate(ctx, src); err != nil {
|
2019-02-24 22:23:38 +00:00
|
|
|
mlog.Fatal("error populating configuration", ctx, merr.Context(err))
|
2019-01-25 03:04:58 +00:00
|
|
|
} else if err := mrun.Start(ctx); err != nil {
|
2019-02-24 22:23:38 +00:00
|
|
|
mlog.Fatal("error triggering start event", ctx, merr.Context(err))
|
2019-01-25 03:04:58 +00:00
|
|
|
}
|
2019-03-03 20:35:49 +00:00
|
|
|
mlog.Info("start hooks completed", ctx)
|
2019-02-24 22:23:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StartWaitStop performs the work of populating configuration parameters,
|
|
|
|
// triggering the start event, waiting for an interrupt, and then triggering the
|
|
|
|
// stop event. Run will block until the stop event is done. If any errors are
|
|
|
|
// encountered a fatal is thrown.
|
|
|
|
func StartWaitStop(ctx context.Context) {
|
|
|
|
Start(ctx)
|
2019-01-25 03:04:58 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
ch := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(ch, os.Interrupt)
|
|
|
|
s := <-ch
|
2019-02-24 22:23:38 +00:00
|
|
|
mlog.Info("signal received, stopping", mctx.Annotate(ctx, "signal", s))
|
2019-01-25 03:04:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := mrun.Stop(ctx); err != nil {
|
2019-02-24 22:23:38 +00:00
|
|
|
mlog.Fatal("error triggering stop event", ctx, merr.Context(err))
|
2019-01-25 03:04:58 +00:00
|
|
|
}
|
2019-02-24 22:23:38 +00:00
|
|
|
mlog.Info("exiting process", ctx)
|
2019-01-25 03:04:58 +00:00
|
|
|
}
|