287 lines
5.8 KiB
Go
287 lines
5.8 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/gob"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/boltdb/bolt"
|
|
"github.com/gorilla/mux"
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"code.chrissexton.org/cws/lameralert/mapping"
|
|
)
|
|
|
|
var (
|
|
bucket = []byte("config")
|
|
)
|
|
|
|
type configValue struct {
|
|
Key string `json:"key"`
|
|
Value interface{} `json:"value"`
|
|
}
|
|
|
|
func (value configValue) getBytes() ([]byte, error) {
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func decodeConfig(in []byte, target *configValue) error {
|
|
err := json.Unmarshal(in, target)
|
|
return 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 {
|
|
// I'm not completely sure if this mutex is necessary.
|
|
// It is left here because I'm opening an closing the db
|
|
// for every operation.
|
|
sync.Mutex
|
|
path string
|
|
}
|
|
|
|
func (c *Config) db() *bolt.DB {
|
|
db, err := bolt.Open(c.path, 0666,
|
|
&bolt.Options{
|
|
Timeout: 1 * time.Second,
|
|
})
|
|
if err != nil {
|
|
log.Fatal().Msgf("Could not open database: %s", err)
|
|
}
|
|
err = db.Update(func(tx *bolt.Tx) error {
|
|
_, err := tx.CreateBucketIfNotExists(bucket)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Could not open bucket")
|
|
}
|
|
return db
|
|
}
|
|
|
|
func (c *Config) getAll() (map[string]configValue, error) {
|
|
c.Lock()
|
|
defer c.Unlock()
|
|
db := c.db()
|
|
defer db.Close()
|
|
vals := map[string]configValue{}
|
|
err := db.View(func(tx *bolt.Tx) error {
|
|
b := tx.Bucket(bucket)
|
|
err := b.ForEach(func(k, v []byte) error {
|
|
var value configValue
|
|
err := decodeConfig(v, &value)
|
|
if err != nil {
|
|
return fmt.Errorf("could not convert value: %w", err)
|
|
}
|
|
vals[string(k)] = value
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("error in forEach: %w", err)
|
|
}
|
|
return nil
|
|
})
|
|
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) {
|
|
c.Lock()
|
|
defer c.Unlock()
|
|
db := c.db()
|
|
defer db.Close()
|
|
var out configValue
|
|
err := db.View(func(tx *bolt.Tx) error {
|
|
b := tx.Bucket(bucket)
|
|
err := decodeConfig(b.Get([]byte(key)), &out)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return configValue{}, err
|
|
}
|
|
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 {
|
|
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 := value.getBytes()
|
|
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) delete(key string) error {
|
|
c.Lock()
|
|
defer c.Unlock()
|
|
db := c.db()
|
|
defer db.Close()
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
b := tx.Bucket(bucket)
|
|
return b.Delete([]byte(key))
|
|
})
|
|
return err
|
|
}
|
|
|
|
// Public interface
|
|
|
|
func New(r *mux.Router) *Config {
|
|
c := &Config{
|
|
path: "lameralert.db",
|
|
}
|
|
c.setup(r)
|
|
return c
|
|
}
|
|
|
|
func (c *Config) GetInt64(key string, defaultValue int64) int64 {
|
|
v, err := c.get(key)
|
|
if err != nil {
|
|
return defaultValue
|
|
}
|
|
return int64(v.Value.(float64))
|
|
}
|
|
|
|
func (c *Config) GetInt(key string, defaultValue int) int {
|
|
v, err := c.get(key)
|
|
if err != nil {
|
|
return defaultValue
|
|
}
|
|
return int(v.Value.(float64))
|
|
}
|
|
|
|
func (c *Config) GetStringSlice(key string, defaultValue []string) []string {
|
|
v, err := c.get(key)
|
|
if err != nil {
|
|
log.Error().Msgf("GetStringSlice errored looking for %s: %s", key, err)
|
|
return defaultValue
|
|
}
|
|
return interfaceSliceToStringSlice(v.Value.([]interface{}))
|
|
}
|
|
|
|
func interfaceSliceToStringSlice(in []interface{}) []string {
|
|
out := []string{}
|
|
for _, it := range in {
|
|
out = append(out, it.(string))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (c *Config) GetStringMap(key string, defaultValue map[string]string) map[string]string {
|
|
v, err := c.get(key)
|
|
if err != nil {
|
|
return defaultValue
|
|
}
|
|
return v.Value.(map[string]string)
|
|
}
|
|
|
|
func (c *Config) GetString(key, defaultValue string) string {
|
|
v, err := c.get(key)
|
|
if err != nil {
|
|
return defaultValue
|
|
}
|
|
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 {
|
|
return c.set(key, configValue{key, value})
|
|
}
|