diff options
author | Valery Piashchynski <[email protected]> | 2021-04-04 18:39:52 +0300 |
---|---|---|
committer | Valery Piashchynski <[email protected]> | 2021-04-04 18:39:52 +0300 |
commit | cc56299b877f3fbbae1e3368d98804d06564a424 (patch) | |
tree | 6b2bcd13eb32e31cef556f57869b9dcdeea4472a /plugins/status | |
parent | c1664e0815727e599dcb7f7a0a7a95a5be974197 (diff) |
- 🔥 Support Readiness checks (via `/ready`) status plugin endpoint.
Signed-off-by: Valery Piashchynski <[email protected]>
Diffstat (limited to 'plugins/status')
-rw-r--r-- | plugins/status/interface.go | 7 | ||||
-rw-r--r-- | plugins/status/plugin.go | 92 | ||||
-rw-r--r-- | plugins/status/rpc.go | 20 |
3 files changed, 93 insertions, 26 deletions
diff --git a/plugins/status/interface.go b/plugins/status/interface.go index 0a92bc52..9d5a13af 100644 --- a/plugins/status/interface.go +++ b/plugins/status/interface.go @@ -9,3 +9,10 @@ type Status struct { type Checker interface { Status() Status } + +// Readiness interface used to get readiness status from the plugin +// that means, that worker poll inside the plugin has 1+ plugins which are ready to work +// at the particular moment +type Readiness interface { + Ready() Status +} diff --git a/plugins/status/plugin.go b/plugins/status/plugin.go index 6fbe67cf..693440bf 100644 --- a/plugins/status/plugin.go +++ b/plugins/status/plugin.go @@ -18,10 +18,13 @@ const ( ) type Plugin struct { - registry map[string]Checker - server *fiber.App - log logger.Logger - cfg *Config + // plugins which needs to be checked just as Status + statusRegistry map[string]Checker + // plugins which needs to send Readiness status + readyRegistry map[string]Readiness + server *fiber.App + log logger.Logger + cfg *Config } func (c *Plugin) Init(log logger.Logger, cfg config.Configurer) error { @@ -34,7 +37,9 @@ func (c *Plugin) Init(log logger.Logger, cfg config.Configurer) error { return errors.E(op, errors.Disabled, err) } - c.registry = make(map[string]Checker) + c.readyRegistry = make(map[string]Readiness) + c.statusRegistry = make(map[string]Checker) + c.log = log return nil } @@ -49,6 +54,7 @@ func (c *Plugin) Serve() chan error { }) c.server.Use("/health", c.healthHandler) + c.server.Use("/ready", c.readinessHandler) go func() { err := c.server.Listen(c.cfg.Address) @@ -69,10 +75,11 @@ func (c *Plugin) Stop() error { return nil } -// Reset named service. -func (c *Plugin) Status(name string) (Status, error) { +// status returns a Checker interface implementation +// Reset named service. This is not an Status interface implementation +func (c *Plugin) status(name string) (Status, error) { const op = errors.Op("checker_plugin_status") - svc, ok := c.registry[name] + svc, ok := c.statusRegistry[name] if !ok { return Status{}, errors.E(op, errors.Errorf("no such service: %s", name)) } @@ -80,16 +87,34 @@ func (c *Plugin) Status(name string) (Status, error) { return svc.Status(), nil } -// CollectTarget collecting services which can provide Status. -func (c *Plugin) CollectTarget(name endure.Named, r Checker) error { - c.registry[name.Name()] = r +// ready used to provide a readiness check for the plugin +func (c *Plugin) ready(name string) (Status, error) { + const op = errors.Op("checker_plugin_ready") + svc, ok := c.readyRegistry[name] + if !ok { + return Status{}, errors.E(op, errors.Errorf("no such service: %s", name)) + } + + return svc.Ready(), nil +} + +// CollectCheckerImpls collects services which can provide Status. +func (c *Plugin) CollectCheckerImpls(name endure.Named, r Checker) error { + c.statusRegistry[name.Name()] = r + return nil +} + +// CollectReadinessImpls collects services which can provide Readiness check. +func (c *Plugin) CollectReadinessImpls(name endure.Named, r Readiness) error { + c.readyRegistry[name.Name()] = r return nil } // Collects declares services to be collected. func (c *Plugin) Collects() []interface{} { return []interface{}{ - c.CollectTarget, + c.CollectReadinessImpls, + c.CollectCheckerImpls, } } @@ -109,6 +134,35 @@ type Plugins struct { const template string = "Service: %s: Status: %d\n" +func (c *Plugin) readinessHandler(ctx *fiber.Ctx) error { + const op = errors.Op("checker_plugin_readiness_handler") + plugins := &Plugins{} + err := ctx.QueryParser(plugins) + if err != nil { + return errors.E(op, err) + } + + if len(plugins.Plugins) == 0 { + ctx.Status(http.StatusOK) + _, _ = ctx.WriteString("No plugins provided in query. Query should be in form of: ready?plugin=plugin1&plugin=plugin2 \n") + return nil + } + + // iterate over all provided plugins + for i := 0; i < len(plugins.Plugins); i++ { + // check if the plugin exists + if plugin, ok := c.readyRegistry[plugins.Plugins[i]]; ok { + st := plugin.Ready() + _, _ = ctx.WriteString(fmt.Sprintf(template, plugins.Plugins[i], st.Code)) + } else { + _, _ = ctx.WriteString(fmt.Sprintf("Service: %s not found", plugins.Plugins[i])) + } + } + + ctx.Status(http.StatusOK) + return nil +} + func (c *Plugin) healthHandler(ctx *fiber.Ctx) error { const op = errors.Op("checker_plugin_health_handler") plugins := &Plugins{} @@ -123,26 +177,16 @@ func (c *Plugin) healthHandler(ctx *fiber.Ctx) error { return nil } - failed := false // iterate over all provided plugins for i := 0; i < len(plugins.Plugins); i++ { // check if the plugin exists - if plugin, ok := c.registry[plugins.Plugins[i]]; ok { + if plugin, ok := c.statusRegistry[plugins.Plugins[i]]; ok { st := plugin.Status() - if st.Code >= 500 { - failed = true - continue - } else if st.Code >= 100 && st.Code <= 400 { - _, _ = ctx.WriteString(fmt.Sprintf(template, plugins.Plugins[i], st.Code)) - } + _, _ = ctx.WriteString(fmt.Sprintf(template, plugins.Plugins[i], st.Code)) } else { _, _ = ctx.WriteString(fmt.Sprintf("Service: %s not found", plugins.Plugins[i])) } } - if failed { - ctx.Status(http.StatusInternalServerError) - return nil - } ctx.Status(http.StatusOK) return nil diff --git a/plugins/status/rpc.go b/plugins/status/rpc.go index 396ff451..755a06fa 100644 --- a/plugins/status/rpc.go +++ b/plugins/status/rpc.go @@ -14,7 +14,7 @@ type rpc struct { func (rpc *rpc) Status(service string, status *Status) error { const op = errors.Op("checker_rpc_status") rpc.log.Debug("started Status method", "service", service) - st, err := rpc.srv.Status(service) + st, err := rpc.srv.status(service) if err != nil { return errors.E(op, err) } @@ -22,6 +22,22 @@ func (rpc *rpc) Status(service string, status *Status) error { *status = st rpc.log.Debug("status code", "code", st.Code) - rpc.log.Debug("successfully finished Status method") + rpc.log.Debug("successfully finished the Status method") + return nil +} + +// Status return current status of the provided plugin +func (rpc *rpc) Ready(service string, status *Status) error { + const op = errors.Op("checker_rpc_ready") + rpc.log.Debug("started Ready method", "service", service) + st, err := rpc.srv.ready(service) + if err != nil { + return errors.E(op, err) + } + + *status = st + + rpc.log.Debug("status code", "code", st.Code) + rpc.log.Debug("successfully finished the Ready method") return nil } |