Documentation ¶
Overview ¶
Package app provides an application framework that manages the live cycle of a running application.
An app could be an HTTP server (or any kind of server), a worker or a simple program. Independently of the kind of the app, it always exposes an admin port at 9000 by default which serves metrics, debug information and kubernetes probes.
An app is also capable of graceful shutdown on receiving signals of terminating by itself.
The recommended way to use the `app` package is to rely on the 'default app'. The 'default app' is a global app that can be accessed by public functions on the app package (the app package can be seen as the application)
There is a running example on `app/examples/servefiles`
Basics ¶
The first thing you should do bootstrap the application. This will initialize the configuration, reading it from the commandline or environment, initialize zerolog (based on the Config) and initialize the default global app.
app.Bootstrap(version, &cfg)
After the bootstrap, the app will already be exposing the admin port and the readiness probe will be returning error, indicating that the application is not yet ready to receive requests. But the healthiness probe will be returning success, indicating the app is alive.
Then you should start initializing all the program dependencies. Because the application is not yet ready, kubernetes will refrain from sending requests (that would fail at this point). Also we already have some metrics and the debug handlers.
During this phase, you will probably want to register your shutdown handlers.
app.RegisterShutdownHandler( &app.ShutdownHandler{ Name: "http_server", Priority: app.ShutdownPriority(100), Handler: httpServer.Shutdown, Policy: app.ErrorPolicyAbort, }, )
They are executed in order by priority. The Highest priority first (in case of the same priority, don't assume any order).
Finally you can run the application by calling RunAndWait:
app.RunAndWait(func(ctx context.Context) error {...})
At this point the application will run until the given function returns or it receives an termination signal.
Updating From Previous Version ¶
This package is a major overhaul over `arquivei/foundationkit/app`. One of the main breaking changes is how the Config struct is used. Now, all app related configuration is inside an App field and new configuration were added. Now the Config struct is expected to be embedded in your application's configuration:
type config struct { // App is the app specific configuration app.Config // Programs can have any configuration the want. HTTP struct { Port string `default:"8000"` } Dir string `default:"."` }
All the initialization now occurs on the Bootstrap functions and you need to initialize logs manually anymore.
RunAndWait was also changed in a couple of ways. Now it does not return anything and will panic if called incorrectly. The error returned by the MainLoopFunc is handled my logging it and triggering a graceful shutdown. The MainLoopFunc was changed to receve a context. The context will be canceled when Shutdown is called.
Using Probes ¶
A Probe is a boolean that indicates if something is OK or not. There are two groups of probes in an app: The Healthiness an Readiness groups. Kubernetes checks on there two probes to decide what to do to the pod, like, from stop sending requests to just kill the pod, sending a signal the app will capture and start a graceful shutdown.
If a single probe of a group is not OK, than the whole group is not OK. In this event, the HTTP handler returns the name of all the probes that are not OK for the given group.
mux.HandleFunc("/healthy", func(w http.ResponseWriter, _ *http.Request) { isHealthy, cause := app.Healthy.CheckProbes() if isHealthy { w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) } else { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(cause)) } }) mux.HandleFunc("/ready", func(w http.ResponseWriter, _ *http.Request) { isReady, cause := app.Ready.CheckProbes() if isReady { w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) } else { w.WriteHeader(http.StatusServiceUnavailable) w.Write([]byte(cause)) } })
If the application is unhealthy kubernetes will send a signal that will trigger the graceful shutdown. All registered shutdown handlers will be executed ordered by priority (highest first) and the pod will be restarted. Only set an application as unhealthy if it reached an unworkable state and should be restarted. We have an example of this on `gokitmiddlewares/stalemiddleware/`. This is a middleware that was developed to be used in workers. It checks if the endpoint is being called (messages are being fetched and processed) and if not, it assumes there could be a problem with the queue and sets the application to unready, causing the application to restart. This mitigated a problem we had with kafka when a change of brokers made the worker stop receiving messages forever.
If the application is unready kubernetes will stop sending requests, but if the application becomes ready again, it will start receiving requests. This is used during initialization to signalize to kubernetes when the application is ready and can receive requests. If we can identify that the the application is degraded we can use this probe to temporary remove the application from the kubernetes service until it recovers.
A probe only exists as part of a group so the group provides a proper constructor for a probe. Probe's name must also be unique for the group but can be reused on different groups.
readinessProbe, err := app.Ready.NewProbe("fkit/app", false) healthnessProbe, err := app.Healthy.NewProbe("fkit/app", true)
The probe is automatically added to the group and any change is automatically reflected on the group it belongs to and the HTTP probe endpoints.
The state of a probe can be altered at any time using SetOk and SetNotOk:
readinessProbe.SetOk() readinessProbe.SetNotOk()
Index ¶
- func Bootstrap(appVersion string, config AppConfig)
- func ErrorPolicyString(p ErrorPolicy) string
- func Recover()
- func RegisterShutdownHandler(sh *ShutdownHandler)
- func RunAndWait(f MainLoopFunc)
- func SetupConfig(config any)
- func Shutdown(ctx context.Context) error
- type App
- type AppConfig
- type Config
- type ErrorPolicy
- type MainLoopFunc
- type Probe
- type ProbeGroup
- type ShutdownFunc
- type ShutdownHandler
- type ShutdownPriority
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Bootstrap ¶
Bootstrap initializes the config structure, the log and creates a new app internally.
func ErrorPolicyString ¶
func ErrorPolicyString(p ErrorPolicy) string
ErrorPolicyString returns a string representation of a ErrorPolicy. This was intended for logging purposes.
func RegisterShutdownHandler ¶
func RegisterShutdownHandler(sh *ShutdownHandler)
RegisterShutdownHandler adds a shutdown handler to the app. Shutdown Handlers are executed one at a time from the highest priority to the lowest priority. Shutdown handlers of the same priority are normally executed in the added order but this is not guaranteed.
func RunAndWait ¶
func RunAndWait(f MainLoopFunc)
RunAndWait executes the main loop on a go-routine and listens to SIGINT and SIGKILL to start the shutdown. This is expected to be called only once and will panic if called a second time.
func SetupConfig ¶
func SetupConfig(config any)
setupConfig loads the configuration in the given struct. In case of error, prints help and exit application.
Types ¶
type App ¶
type App struct { Ready ProbeGroup Healthy ProbeGroup // contains filtered or unexported fields }
App represents an application with a main loop and a shutdown routine
func (*App) RegisterShutdownHandler ¶
func (app *App) RegisterShutdownHandler(sh *ShutdownHandler)
RegisterShutdownHandler adds a shutdown handler to the app. Shutdown Handlers are executed one at a time from the highest priority to the lowest priority. Shutdown handlers of the same priority are normally executed in the added order but this is not guaranteed.
func (*App) RunAndWait ¶
func (app *App) RunAndWait(mainLoop MainLoopFunc)
RunAndWait executes the main loop on a go-routine and listens to SIGINT and SIGKILL to start the shutdown. This is expected to be called only once and will panic if called a second time.
type Config ¶
type Config struct { App struct { Log logger.Config AdminServer struct { // Enabled sets the admin server Enabled bool `default:"true"` // DefaultAdminPort is the default port the app will bind the admin HTTP interface. Addr string `default:"localhost:9000"` With struct { // DebugURLs sets the debug URLs in the admin server. To disable them, set to false. DebugURLs bool `default:"true"` // Metrics Metrics bool `default:"true"` // Probes Probes bool `default:"true"` } } Shutdown struct { // DefaultGracePeriod is the default value for the grace period. // During normal shutdown procedures, the shutdown function will wait // this amount of time before actually starting calling the shutdown handlers. GracePeriod time.Duration `default:"3s"` // DefaultShutdownTimeout is the default value for the timeout during shutdown. Timeout time.Duration `default:"5s"` } Config struct { Output string } } }
func (Config) GetAppConfig ¶
type ErrorPolicy ¶
type ErrorPolicy int
ErrorPolicy specifies what should be done when a handler fails
const ( // ErrorPolicyWarn prints the error as a warning and continues to the next handler. This is the default. ErrorPolicyWarn ErrorPolicy = iota // ErrorPolicyAbort stops the shutdown process and returns an error ErrorPolicyAbort // ErrorPolicyFatal logs the error as Fatal, it means the application will close immediately ErrorPolicyFatal // ErrorPolicyPanic panics if there is an error ErrorPolicyPanic )
type MainLoopFunc ¶
MainLoopFunc is the function runned by app. If it finishes, it will trigger a shutdown. The context will be canceled when application is gracefully shutting down.
type Probe ¶
type Probe struct {
// contains filtered or unexported fields
}
Probe stores the state of a probe (`true` or `false` for ok and not ok respectively).
func (*Probe) IsOk ¶
IsOk returns the state of the probe (`true` or `false` for ok and not ok respectively).
type ProbeGroup ¶
type ProbeGroup struct {
// contains filtered or unexported fields
}
ProbeGroup aggregates and manages probes. Probes inside a group must have a unique name.
func HealthinessProbeGroup ¶
func HealthinessProbeGroup() *ProbeGroup
HealthinessProbeGroup is a colection of healthiness probes.
func NewProbeGroup ¶
func NewProbeGroup(name string) ProbeGroup
NewProbeGroup returns a new ProbeGroup.
func ReadinessProbeGoup ¶
func ReadinessProbeGoup() *ProbeGroup
ReadinessProbeGoup is a collection of readiness probes.
func (*ProbeGroup) CheckProbes ¶
func (g *ProbeGroup) CheckProbes() (bool, string)
CheckProbes range through the probes and returns the state of the group. If any probe is not ok (false) the group state is not ok (false). If the group is not ok, it's also returned the cause in the second return parameter. If all probes are ok (true) the cause is returned as OK. If more than one probe is not ok, the causes are concatenated by a comma.
func (*ProbeGroup) MustNewProbe ¶
func (g *ProbeGroup) MustNewProbe(name string, ok bool) Probe
MustNewProbe returns a new Probe with the given name and panics in case of error.
func (*ProbeGroup) NewProbe ¶
func (g *ProbeGroup) NewProbe(name string, ok bool) (Probe, error)
NewProbe returns a new Probe with the given name. The name must satisfy the following regular expression: a-zA-Z0-9_/-{3,}
func (*ProbeGroup) ServeHTTP ¶
func (g *ProbeGroup) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP serves the Probe Group as an HTTP handler.
type ShutdownFunc ¶
ShutdownFunc is a shutdown function that will be executed when the app is shutting down.
type ShutdownHandler ¶
type ShutdownHandler struct { Name string Handler ShutdownFunc Policy ErrorPolicy Priority ShutdownPriority Timeout time.Duration // contains filtered or unexported fields }
ShutdownHandler is a shutdown structure that allows configuring and storing shutdown information of an orchestrated shutdown flow.
type ShutdownPriority ¶
type ShutdownPriority uint8
ShutdownPriority is used to guide the execution of the shutdown handlers during a graceful shutdown. The shutdown is performed from the higher to the lowest priority
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
examples
|
|
panic
This example shows how panics are captured and gracefuly printed.
|
This example shows how panics are captured and gracefuly printed. |
servefiles
This is an example program on how to use the app package.
|
This is an example program on how to use the app package. |
shutdown-handlers
This example ilustrates how shutdowns are handled.
|
This example ilustrates how shutdowns are handled. |
internal
|
|
thirdparty/uconfig
Package uconfig provides advanced command line flags supporting defaults, env vars, and config structs.
|
Package uconfig provides advanced command line flags supporting defaults, env vars, and config structs. |
thirdparty/uconfig/flat
Package flat provides a flat view of an arbitrary nested structs.
|
Package flat provides a flat view of an arbitrary nested structs. |
thirdparty/uconfig/internal/f
Package f provides simple test fixtures for uconfig.
|
Package f provides simple test fixtures for uconfig. |
thirdparty/uconfig/plugins
Package plugins describes the uconfig provider interface.
|
Package plugins describes the uconfig provider interface. |
thirdparty/uconfig/plugins/defaults
Package defaults provides flags support for uconfig
|
Package defaults provides flags support for uconfig |
thirdparty/uconfig/plugins/env
Package env provides environment variables support for uconfig
|
Package env provides environment variables support for uconfig |
thirdparty/uconfig/plugins/file
Package file provides config file support for uconfig
|
Package file provides config file support for uconfig |
thirdparty/uconfig/plugins/flag
Package flag provides flags support for uconfig
|
Package flag provides flags support for uconfig |
thirdparty/uconfig/plugins/secret
Package secret enable uconfig to integrate with secret plugins.
|
Package secret enable uconfig to integrate with secret plugins. |