mirror of https://github.com/tidwall/tile38.git
202 lines
4.3 KiB
Go
202 lines
4.3 KiB
Go
package controller
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/tidwall/tile38/controller/log"
|
|
)
|
|
|
|
type EndpointProtocol string
|
|
|
|
const (
|
|
HTTP = EndpointProtocol("http")
|
|
Disque = EndpointProtocol("disque")
|
|
)
|
|
|
|
type Endpoint struct {
|
|
Protocol EndpointProtocol
|
|
Original string
|
|
}
|
|
type Hook struct {
|
|
Key string
|
|
Name string
|
|
Endpoint Endpoint
|
|
Command string
|
|
Fence *liveFenceSwitches
|
|
ScanWriter *scanWriter
|
|
}
|
|
|
|
func (c *Controller) DoHook(hook *Hook, details *commandDetailsT) error {
|
|
msgs := c.FenceMatch(hook.ScanWriter, hook.Fence, details, false)
|
|
for _, msg := range msgs {
|
|
println(">>", string(msg))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type hooksByName []*Hook
|
|
|
|
func (a hooksByName) Len() int {
|
|
return len(a)
|
|
}
|
|
|
|
func (a hooksByName) Less(i, j int) bool {
|
|
return a[i].Name < a[j].Name
|
|
}
|
|
|
|
func (a hooksByName) Swap(i, j int) {
|
|
a[i], a[j] = a[j], a[i]
|
|
}
|
|
|
|
func parseEndpoint(s string) (Endpoint, error) {
|
|
var endpoint Endpoint
|
|
endpoint.Original = s
|
|
switch {
|
|
default:
|
|
return endpoint, errors.New("unknown scheme")
|
|
case strings.HasPrefix(s, "http:"):
|
|
endpoint.Protocol = HTTP
|
|
case strings.HasPrefix(s, "https:"):
|
|
endpoint.Protocol = HTTP
|
|
case strings.HasPrefix(s, "disque:"):
|
|
endpoint.Protocol = Disque
|
|
}
|
|
s = s[strings.Index(s, ":")+1:]
|
|
if !strings.HasPrefix(s, "//") {
|
|
return endpoint, errors.New("missing the two slashes")
|
|
}
|
|
s = strings.Split(s[2:], "/")[0]
|
|
if s == "" {
|
|
return endpoint, errors.New("missing host")
|
|
}
|
|
return endpoint, nil
|
|
}
|
|
|
|
func (c *Controller) cmdAddHook(line string) (err error) {
|
|
//start := time.Now()
|
|
var name, value, cmd string
|
|
if line, name = token(line); name == "" {
|
|
return errInvalidNumberOfArguments
|
|
}
|
|
if line, value = token(line); value == "" {
|
|
return errInvalidNumberOfArguments
|
|
}
|
|
endpoint, err := parseEndpoint(value)
|
|
if err != nil {
|
|
log.Errorf("addhook: %v", err)
|
|
return errInvalidArgument(value)
|
|
}
|
|
command := line
|
|
if line, cmd = token(line); cmd == "" {
|
|
return errInvalidNumberOfArguments
|
|
}
|
|
cmdlc := strings.ToLower(cmd)
|
|
var types []string
|
|
switch cmdlc {
|
|
default:
|
|
return errInvalidArgument(cmd)
|
|
case "nearby":
|
|
types = nearbyTypes
|
|
case "within", "intersects":
|
|
types = withinOrIntersectsTypes
|
|
}
|
|
s, err := c.cmdSearchArgs(cmdlc, line, types)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !s.fence {
|
|
return errors.New("missing FENCE argument")
|
|
}
|
|
s.cmd = cmdlc
|
|
hook := &Hook{
|
|
Key: s.key,
|
|
Name: name,
|
|
Endpoint: endpoint,
|
|
Fence: &s,
|
|
Command: command,
|
|
}
|
|
var wr bytes.Buffer
|
|
hook.ScanWriter, err = c.newScanWriter(&wr, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// delete the previous hook
|
|
if h, ok := c.hooks[name]; ok {
|
|
if hm, ok := c.hookcols[h.Key]; ok {
|
|
delete(hm, h.Name)
|
|
}
|
|
delete(c.hooks, h.Name)
|
|
}
|
|
c.hooks[name] = hook
|
|
hm, ok := c.hookcols[hook.Key]
|
|
if !ok {
|
|
hm = make(map[string]*Hook)
|
|
c.hookcols[hook.Key] = hm
|
|
}
|
|
hm[name] = hook
|
|
return nil
|
|
}
|
|
|
|
func (c *Controller) cmdDelHook(line string) (err error) {
|
|
var name string
|
|
if line, name = token(line); name == "" {
|
|
return errInvalidNumberOfArguments
|
|
}
|
|
if line != "" {
|
|
return errInvalidNumberOfArguments
|
|
}
|
|
if h, ok := c.hooks[name]; ok {
|
|
if hm, ok := c.hookcols[h.Key]; ok {
|
|
delete(hm, h.Name)
|
|
}
|
|
delete(c.hooks, h.Name)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *Controller) cmdHooks(line string, w io.Writer) (err error) {
|
|
start := time.Now()
|
|
|
|
var pattern string
|
|
if line, pattern = token(line); pattern == "" {
|
|
return errInvalidNumberOfArguments
|
|
}
|
|
if line != "" {
|
|
return errInvalidNumberOfArguments
|
|
}
|
|
|
|
var hooks []*Hook
|
|
for name, hook := range c.hooks {
|
|
if ok, err := globMatch(pattern, name); err == nil && ok {
|
|
hooks = append(hooks, hook)
|
|
} else if err != nil {
|
|
return errInvalidArgument(pattern)
|
|
}
|
|
}
|
|
sort.Sort(hooksByName(hooks))
|
|
|
|
buf := &bytes.Buffer{}
|
|
io.WriteString(buf, `{"ok":true,"hooks":[`)
|
|
for i, hook := range hooks {
|
|
if i > 0 {
|
|
io.WriteString(buf, `,`)
|
|
}
|
|
io.WriteString(buf, `"hook":{`)
|
|
io.WriteString(buf, `"name":`+jsonString(hook.Name))
|
|
io.WriteString(buf, `,"key":`+jsonString(hook.Key))
|
|
io.WriteString(buf, `,"endpoint":`+jsonString(hook.Endpoint.Original))
|
|
io.WriteString(buf, `,"command":`+jsonString(hook.Command))
|
|
io.WriteString(buf, `}`)
|
|
}
|
|
io.WriteString(buf, `],"elapsed":"`+time.Now().Sub(start).String()+"\"}")
|
|
|
|
w.Write(buf.Bytes())
|
|
return
|
|
}
|