summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/http/plugin.go20
-rw-r--r--plugins/status/config.go13
-rw-r--r--plugins/status/interface.go7
-rw-r--r--plugins/status/plugin.go98
-rw-r--r--plugins/status/rpc.go20
5 files changed, 137 insertions, 21 deletions
diff --git a/plugins/http/plugin.go b/plugins/http/plugin.go
index 82cf76ed..86fcb329 100644
--- a/plugins/http/plugin.go
+++ b/plugins/http/plugin.go
@@ -405,7 +405,25 @@ func (s *Plugin) Status() status.Status {
}
// if there are no workers, threat this as error
return status.Status{
- Code: http.StatusInternalServerError,
+ Code: http.StatusServiceUnavailable,
+ }
+}
+
+// Status return status of the particular plugin
+func (s *Plugin) Ready() status.Status {
+ workers := s.Workers()
+ for i := 0; i < len(workers); i++ {
+ // If state of the worker is ready (at least 1)
+ // we assume, that plugin's worker pool is ready
+ if workers[i].State().Value() == worker.StateReady {
+ return status.Status{
+ Code: http.StatusOK,
+ }
+ }
+ }
+ // if there are no workers, threat this as no content error
+ return status.Status{
+ Code: http.StatusServiceUnavailable,
}
}
diff --git a/plugins/status/config.go b/plugins/status/config.go
index 23a6ede2..f751898b 100644
--- a/plugins/status/config.go
+++ b/plugins/status/config.go
@@ -1,5 +1,18 @@
package status
+import "net/http"
+
+// Config is the configuration reference for the Status plugin
type Config struct {
+ // Address of the http server
Address string
+ // Status code returned in case of fail, 503 by default
+ UnavailableStatusCode int `mapstructure:"unavailable_status_code"`
+}
+
+// InitDefaults configuration options
+func (c *Config) InitDefaults() {
+ if c.UnavailableStatusCode == 0 {
+ c.UnavailableStatusCode = http.StatusServiceUnavailable
+ }
}
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..dc4e506d 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,8 +37,14 @@ func (c *Plugin) Init(log logger.Logger, cfg config.Configurer) error {
return errors.E(op, errors.Disabled, err)
}
- c.registry = make(map[string]Checker)
+ // init defaults for the status plugin
+ c.cfg.InitDefaults()
+
+ c.readyRegistry = make(map[string]Readiness)
+ c.statusRegistry = make(map[string]Checker)
+
c.log = log
+
return nil
}
@@ -49,6 +58,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 +79,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 +91,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,
}
}
@@ -123,15 +152,15 @@ 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
+ // if there is 500 or 503 status code return immediately
+ ctx.Status(c.cfg.UnavailableStatusCode)
+ return nil
} else if st.Code >= 100 && st.Code <= 400 {
_, _ = ctx.WriteString(fmt.Sprintf(template, plugins.Plugins[i], st.Code))
}
@@ -139,11 +168,44 @@ func (c *Plugin) healthHandler(ctx *fiber.Ctx) error {
_, _ = ctx.WriteString(fmt.Sprintf("Service: %s not found", plugins.Plugins[i]))
}
}
- if failed {
- ctx.Status(http.StatusInternalServerError)
+
+ ctx.Status(http.StatusOK)
+ return nil
+}
+
+// readinessHandler return 200OK if all plugins are ready to serve
+// if one of the plugins return status from the 5xx range, the status for all query will be 503
+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()
+ if st.Code >= 500 {
+ // if there is 500 or 503 status code return immediately
+ ctx.Status(c.cfg.UnavailableStatusCode)
+ return nil
+ } else if st.Code >= 100 && st.Code <= 400 {
+ _, _ = 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
}
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
}