1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
package checker
import (
"fmt"
"net/http"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/spiral/endure"
"github.com/spiral/errors"
config2 "github.com/spiral/roadrunner/v2/interfaces/config"
"github.com/spiral/roadrunner/v2/interfaces/log"
"github.com/spiral/roadrunner/v2/interfaces/status"
)
const (
// PluginName declares public plugin name.
PluginName = "status"
)
type Plugin struct {
registry map[string]status.Checker
server *fiber.App
log log.Logger
cfg *Config
}
func (c *Plugin) Init(log log.Logger, cfg config2.Configurer) error {
const op = errors.Op("status plugin init")
err := cfg.UnmarshalKey(PluginName, &c.cfg)
if err != nil {
return errors.E(op, errors.Disabled, err)
}
if c.cfg == nil {
return errors.E(errors.Disabled)
}
c.registry = make(map[string]status.Checker)
c.log = log
return nil
}
func (c *Plugin) Serve() chan error {
errCh := make(chan error, 1)
c.server = fiber.New(fiber.Config{
ReadTimeout: time.Second * 5,
WriteTimeout: time.Second * 5,
IdleTimeout: time.Second * 5,
})
c.server.Group("/v1", c.healthHandler)
c.server.Use(logger.New())
c.server.Use("/health", c.healthHandler)
go func() {
err := c.server.Listen(c.cfg.Address)
if err != nil {
errCh <- err
}
}()
return errCh
}
func (c *Plugin) Stop() error {
const op = errors.Op("checker stop")
err := c.server.Shutdown()
if err != nil {
return errors.E(op, err)
}
return nil
}
// Reset named service.
func (c *Plugin) Status(name string) (status.Status, error) {
const op = errors.Op("get status")
svc, ok := c.registry[name]
if !ok {
return status.Status{}, errors.E(op, errors.Errorf("no such service: %s", name))
}
return svc.Status(), nil
}
// CollectTarget collecting services which can provide Status.
func (c *Plugin) CollectTarget(name endure.Named, r status.Checker) error {
c.registry[name.Name()] = r
return nil
}
// Collects declares services to be collected.
func (c *Plugin) Collects() []interface{} {
return []interface{}{
c.CollectTarget,
}
}
// Name of the service.
func (c *Plugin) Name() string {
return PluginName
}
// RPCService returns associated rpc service.
func (c *Plugin) RPC() interface{} {
return &rpc{srv: c, log: c.log}
}
type Plugins struct {
Plugins []string `query:"plugin"`
}
const template string = "Service: %s: Status: %d\n"
func (c *Plugin) healthHandler(ctx *fiber.Ctx) error {
const op = errors.Op("health_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: /v1/health?plugin=plugin1&plugin=plugin2 \n")
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 {
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))
}
} 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
}
|