1
0
Fork 0
secondly/confection.go

131 lines
2.6 KiB
Go
Raw Normal View History

2015-08-29 09:45:00 +00:00
package confection2
import (
"encoding/json"
2015-08-29 10:51:28 +00:00
"flag"
2015-08-29 09:59:45 +00:00
"log"
2015-08-29 11:14:53 +00:00
"os"
"os/signal"
2015-08-29 09:45:00 +00:00
"reflect"
2015-08-29 11:14:53 +00:00
"syscall"
2015-08-29 09:45:00 +00:00
)
var (
2015-08-29 10:51:28 +00:00
config interface{} // config stores application config
configFile string
callbacks = make(map[string][]func(oldVal, newVal interface{}))
initialized bool
2015-08-29 09:45:00 +00:00
)
2015-08-29 10:51:28 +00:00
// SetFlags sets up Confection configuration flags.
func SetFlags() {
flag.StringVar(&configFile, "config", "config.json", "Path to config file")
}
2015-08-29 09:55:34 +00:00
// Manage accepts a pointer to a configuration struct.
func Manage(target interface{}) {
if ok := isStructPtr(target); !ok {
panic("Argument must be a pointer to a struct")
}
config = target
2015-08-29 11:10:56 +00:00
bootstrap()
2015-08-29 09:55:34 +00:00
}
2015-08-29 11:14:53 +00:00
// HandleSIGHUP waits a SIGHUP system call and reloads configuration when
// receives one.
func HandleSIGHUP() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGHUP)
go func() {
for _ = range ch {
log.Println("SIGHUP received, reloading config")
readConfig()
}
}()
}
2015-08-29 10:18:32 +00:00
// OnChange adds a callback function that is triggered every time a value of
// a field changes.
func OnChange(field string, fun func(oldVal, newVal interface{})) {
callbacks[field] = append(callbacks[field], fun)
}
2015-08-29 10:51:28 +00:00
func bootstrap() {
if configFile == "" {
panic("path to config file is not set")
}
if fileExist(configFile) {
log.Println("Loading config file")
readConfig()
2015-08-29 10:51:28 +00:00
} else {
log.Println("Config file not found, saving an empty one")
writeConfig()
}
}
func readConfig() {
body, err := readFile(configFile)
if err != nil {
panic(err)
}
updateConfig(body)
}
func writeConfig() {
body, err := json.Marshal(config)
if err != nil {
panic(err)
}
if err = writeFile(configFile, body); err != nil {
panic(err)
2015-08-29 10:51:28 +00:00
}
}
func updateConfig(body []byte) {
2015-08-29 09:59:45 +00:00
dupe := duplicate(config)
if err := json.Unmarshal(body, dupe); err != nil {
2015-08-29 09:59:45 +00:00
log.Println("Failed to update config")
return
}
2015-08-29 10:18:32 +00:00
defer triggerCallbacks(config, dupe)
// Setting new config
2015-08-29 09:59:45 +00:00
config = dupe
}
2015-08-29 10:18:32 +00:00
func triggerCallbacks(oldConf, newConf interface{}) {
2015-08-29 10:51:28 +00:00
// Don't trigger callbacks on fist load
if !initialized {
initialized = true
return
}
2015-08-29 10:18:32 +00:00
return
}
2015-08-29 09:45:00 +00:00
func isStructPtr(target interface{}) bool {
if val := reflect.ValueOf(target); val.Kind() == reflect.Ptr {
if val = reflect.Indirect(val); val.Kind() == reflect.Struct {
return true
}
}
return false
}
2015-08-29 09:55:18 +00:00
func duplicate(original interface{}) interface{} {
// Get the interface value
val := reflect.ValueOf(original)
// We expect a pointer to a struct, so now we need the underlying staruct
val = reflect.Indirect(val)
// Now we need the type (name) of this struct
typ := val.Type()
// Creating a duplicate instance of that struct
dupe := reflect.New(typ).Interface()
return dupe
}