online mapping configuration

This commit is contained in:
Chris Sexton 2019-12-27 08:51:59 -05:00
parent 05b8981cea
commit 5a486ea48e
3 changed files with 210 additions and 19 deletions

View File

@ -1,6 +1,8 @@
package config package config
import ( import (
"bytes"
"encoding/gob"
"encoding/json" "encoding/json"
"fmt" "fmt"
"sync" "sync"
@ -9,6 +11,8 @@ import (
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"code.chrissexton.org/cws/lameralert/mapping"
) )
var ( var (
@ -24,10 +28,29 @@ func (value configValue) getBytes() ([]byte, error) {
return json.Marshal(value) return json.Marshal(value)
} }
func decodeConfig(in []byte) (configValue, error) { func decodeConfig(in []byte, target *configValue) error {
var out configValue err := json.Unmarshal(in, target)
err := json.Unmarshal(in, &out) return err
return out, err }
func encodeMapping(in mapping.Mapping) ([]byte, error) {
var out []byte
buf := bytes.NewBuffer(out)
enc := gob.NewEncoder(buf)
err := enc.Encode(in)
return buf.Bytes(), err
}
func decodeMapping(in []byte) (mapping.Mapping, error) {
var target mapping.Mapping
dec := gob.NewDecoder(bytes.NewBuffer(in))
err := dec.Decode(&target)
log.Debug().
Interface("err", err).
Str("in", string(in)).
Interface("target", target).
Msg("decodeMapping")
return target, err
} }
type Config struct { type Config struct {
@ -65,7 +88,8 @@ func (c *Config) getAll() (map[string]configValue, error) {
err := db.View(func(tx *bolt.Tx) error { err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket) b := tx.Bucket(bucket)
err := b.ForEach(func(k, v []byte) error { err := b.ForEach(func(k, v []byte) error {
value, err := decodeConfig(v) var value configValue
err := decodeConfig(v, &value)
if err != nil { if err != nil {
return fmt.Errorf("could not convert value: %w", err) return fmt.Errorf("could not convert value: %w", err)
} }
@ -80,6 +104,31 @@ func (c *Config) getAll() (map[string]configValue, error) {
return vals, err return vals, err
} }
func (c *Config) getMapping(key string) (mapping.Mapping, error) {
log.Debug().Msgf("getMapping(%s)", key)
c.Lock()
defer c.Unlock()
db := c.db()
defer db.Close()
var err error
var out mapping.Mapping
err = db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket)
out, err = decodeMapping(b.Get([]byte(key)))
log.Debug().AnErr("err", err).Interface("out", out).Msgf("decode %s", key)
if err != nil {
log.Error().Msgf("Error decoding: %w", err)
return err
}
return nil
})
if err != nil {
log.Error().Msgf("Error getting: %w", err)
return mapping.Mapping{}, err
}
return out, nil
}
func (c *Config) get(key string) (configValue, error) { func (c *Config) get(key string) (configValue, error) {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
@ -88,11 +137,10 @@ func (c *Config) get(key string) (configValue, error) {
var out configValue var out configValue
err := db.View(func(tx *bolt.Tx) error { err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket) b := tx.Bucket(bucket)
value, err := decodeConfig(b.Get([]byte(key))) err := decodeConfig(b.Get([]byte(key)), &out)
if err != nil { if err != nil {
return err return err
} }
out = value
return nil return nil
}) })
if err != nil { if err != nil {
@ -101,6 +149,26 @@ func (c *Config) get(key string) (configValue, error) {
return out, nil return out, nil
} }
func (c *Config) setMapping(key string, value mapping.Mapping) error {
c.Lock()
defer c.Unlock()
db := c.db()
defer db.Close()
err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket)
v, err := encodeMapping(value)
if err != nil {
return err
}
err = b.Put([]byte(key), v)
return err
})
if err != nil {
log.Error().Msgf("Error setting key: %s", err)
}
return err
}
func (c *Config) set(key string, value configValue) error { func (c *Config) set(key string, value configValue) error {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
@ -192,6 +260,27 @@ func (c *Config) GetString(key, defaultValue string) string {
return v.Value.(string) return v.Value.(string)
} }
func (c *Config) GetMapping(key string, defaultValue mapping.Mapping) mapping.Mapping {
m, err := c.getMapping(key)
if err != nil {
log.Error().Msgf("Error: %w", err)
return defaultValue
}
return m
}
func (c *Config) GetGeneric(key string, defaultValue interface{}) interface{} {
v, err := c.get(key)
if err != nil {
return defaultValue
}
return v.Value
}
func (c *Config) SetMapping(key string, value mapping.Mapping) error {
return c.setMapping(key, value)
}
func (c *Config) Set(key string, value interface{}) error { func (c *Config) Set(key string, value interface{}) error {
return c.set(key, configValue{key, value}) return c.set(key, configValue{key, value})
} }

View File

@ -7,7 +7,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"code.chrissexton.org/cws/lameralert/config" "code.chrissexton.org/cws/lameralert/config"
"code.chrissexton.org/cws/lameralert/event" "code.chrissexton.org/cws/lameralert/mapping"
"code.chrissexton.org/cws/lameralert/sinks" "code.chrissexton.org/cws/lameralert/sinks"
"code.chrissexton.org/cws/lameralert/sources" "code.chrissexton.org/cws/lameralert/sources"
) )
@ -28,8 +28,6 @@ func NewConnection(from sources.Source, to sinks.Sink) Connection {
} }
} }
var mapping = map[chan event.Event][]chan event.Event{}
func logger(next http.Handler) http.Handler { func logger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Do stuff here // Do stuff here
@ -38,22 +36,55 @@ func logger(next http.Handler) http.Handler {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
} }
func Setup() chan bool { func Setup() chan bool {
r := mux.NewRouter() r := mux.NewRouter()
r.Use(logger) r.Use(logger)
c := config.New(r.PathPrefix("/config").Subrouter()) c := config.New(r.PathPrefix("/config").Subrouter())
ch := make(chan bool) ch := make(chan bool)
h := sources.NewGenericRest(r.PathPrefix("/rest").Subrouter()) mappings := c.GetMapping("mappings", mapping.Example())
NewConnection(h, sinks.NewConsoleID(c, "00"))
NewConnection(h, sinks.NewConsoleID(c, "01"))
rss := sources.NewRSS(c, r.PathPrefix("/rss/webshit").Subrouter(), "webshit", "http://n-gate.com/hackernews/index.rss") //mappings := mapping.Example()
NewConnection(rss, sinks.NewConsoleID(c, "webshit-sink")) //c.SetMapping("mappings", mappings)
if push, err := sinks.NewPushover(c, "webshit-push"); err != nil {
log.Fatal().Msgf("error: %s", err) srcs := map[string]sources.Source{}
} else { snks := map[string]sinks.Sink{}
NewConnection(rss, push) for _, source := range mappings.Sources {
var src sources.Source
// TODO: Move this logic out to the sources package
switch source.Type {
case "GenericRest":
path := source.Config["prefix"]
src = sources.NewGenericRest(r.PathPrefix(path).Subrouter())
case "RSS":
path := source.Config["prefix"]
url := source.Config["URL"]
src = sources.NewRSS(c, r.PathPrefix(path).Subrouter(), source.Name, url)
}
srcs[source.Name] = src
}
for _, sink := range mappings.Sinks {
var snk sinks.Sink
// TODO: Move this logic out to the sinks package
switch sink.Type {
case "Console":
snk = sinks.NewConsoleID(c, sink.Config["ID"])
case "Pushover":
var err error
snk, err = sinks.NewPushover(c, sink.Name)
if err != nil {
log.Fatal().Msgf("Error setting up pushover: %w", err)
}
}
snks[sink.Name] = snk
}
for _, m := range mappings.Mappings {
src := srcs[m.From]
snk := snks[m.To]
NewConnection(src, snk)
} }
http.ListenAndServe(":9090", r) http.ListenAndServe(":9090", r)

71
mapping/mapping.go Normal file
View File

@ -0,0 +1,71 @@
package mapping
type Mapping struct {
Sources []MapItem
Sinks []MapItem
Mappings []MapPair
}
type MapPair struct {
From string
To string
}
type MapItem struct {
Name string
Type string
Config map[string]string
}
// TODO: Make a web interface to configure these horrible structures
func Example() Mapping {
mappings := Mapping{}
webshit := MapItem{
Name: "webshit",
Type: "RSS",
Config: map[string]string{
"prefix": "/rss/webshit",
"URL": "http://n-gate.com/hackernews/index.rss",
},
}
mappings.Sources = append(mappings.Sources, webshit)
genericRest := MapItem{
Name: "GenericRest",
Type: "GenericRest",
Config: map[string]string{
"prefix": "/rest",
"name": "GenericRest",
},
}
mappings.Sources = append(mappings.Sources, genericRest)
consoleZero := MapItem{
Name: "Console-00",
Type: "Console",
Config: map[string]string{
"ID": "00",
},
}
mappings.Sinks = append(mappings.Sinks, consoleZero)
consoleOne := MapItem{
Name: "Console-01",
Type: "Console",
Config: map[string]string{
"ID": "01",
},
}
mappings.Sinks = append(mappings.Sinks, consoleOne)
pushOver := MapItem{
Name: "Pushover",
Type: "Pushover",
Config: map[string]string{},
}
mappings.Sinks = append(mappings.Sinks, pushOver)
mappings.Mappings = append(mappings.Mappings, MapPair{"GenericRest", "Console-00"})
mappings.Mappings = append(mappings.Mappings, MapPair{"GenericRest", "Console-01"})
mappings.Mappings = append(mappings.Mappings, MapPair{"webshit", "Pushover"})
return mappings
}