mirror of https://github.com/spf13/viper.git
parent
a93eb7a85a
commit
f06cfd8785
|
@ -6,45 +6,39 @@ import (
|
||||||
"github.com/hashicorp/consul/watch"
|
"github.com/hashicorp/consul/watch"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type consulConfigProvider struct{}
|
type consulConfigProvider struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
idxMap map[string]uint64
|
||||||
|
}
|
||||||
|
|
||||||
func (rc consulConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {
|
func (rc *consulConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {
|
||||||
config := api.DefaultConfig()
|
config := api.DefaultConfig()
|
||||||
config.Address = rp.Endpoint()
|
config.Address = rp.Endpoint()
|
||||||
client, err := api.NewClient(config)
|
client, err := api.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
kv, _, err := client.KV().Get(rp.Path(), nil)
|
kv, meta, err := client.KV().Get(rp.Path(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
rc.updateIndex(rp, meta.LastIndex)
|
||||||
return bytes.NewReader(kv.Value), nil
|
return bytes.NewReader(kv.Value), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc consulConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {
|
func (rc *consulConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {
|
||||||
// TODO same as Get(), but behave like before(viper/remote), maybe record LastIndex in rp?
|
return rc.watch(rp)
|
||||||
resp, quit := newWatcher(rp.Path(), rp.Endpoint())
|
|
||||||
r := <-resp
|
|
||||||
close(quit)
|
|
||||||
return bytes.NewReader(r.Value), r.Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc consulConfigProvider) WatchChannel(
|
func (rc *consulConfigProvider) watch(rp viper.RemoteProvider) (r io.Reader, err error) {
|
||||||
rp viper.RemoteProvider) (resp <-chan *viper.RemoteResponse, quit chan bool) {
|
p, err := watch.Parse(map[string]interface{}{"type": "key", "key": rp.Path()})
|
||||||
return newWatcher(rp.Path(), rp.Endpoint())
|
|
||||||
}
|
|
||||||
|
|
||||||
// To stop watch, just close(quit)
|
|
||||||
func newWatcher(key, addr string) (<-chan *viper.RemoteResponse, chan bool) {
|
|
||||||
p, err := watch.Parse(map[string]interface{}{"type": "key", "key": key})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
quit := make(chan bool)
|
// handler
|
||||||
viperResponseCh := make(chan *viper.RemoteResponse)
|
|
||||||
p.Handler = func(index uint64, data interface{}) {
|
p.Handler = func(index uint64, data interface{}) {
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return
|
return
|
||||||
|
@ -53,12 +47,49 @@ func newWatcher(key, addr string) (<-chan *viper.RemoteResponse, chan bool) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !rc.updateIndex(rp, index) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r = bytes.NewReader(kv.Value)
|
||||||
|
p.Stop()
|
||||||
|
}
|
||||||
|
// start watch
|
||||||
|
p.Run(rp.Endpoint())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *consulConfigProvider) WatchChannel(
|
||||||
|
rp viper.RemoteProvider) (resp <-chan *viper.RemoteResponse, quit chan bool) {
|
||||||
|
return rc.watchChannel(rp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *consulConfigProvider) watchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
|
||||||
|
p, err := watch.Parse(map[string]interface{}{"type": "key", "key": rp.Path()})
|
||||||
|
if err != nil {
|
||||||
|
// this should not happen
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
quit := make(chan bool)
|
||||||
|
viperResponseCh := make(chan *viper.RemoteResponse)
|
||||||
|
// handler
|
||||||
|
p.Handler = func(index uint64, data interface{}) {
|
||||||
|
if data == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
kv, ok := data.(*api.KVPair)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !rc.updateIndex(rp, index) {
|
||||||
|
return
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case viperResponseCh <- &viper.RemoteResponse{Value: kv.Value}:
|
case viperResponseCh <- &viper.RemoteResponse{Value: kv.Value}:
|
||||||
case <-quit:
|
case <-quit:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
go p.Run(addr)
|
// start watcher
|
||||||
|
go p.Run(rp.Endpoint())
|
||||||
// wait quit
|
// wait quit
|
||||||
go func() {
|
go func() {
|
||||||
<-quit
|
<-quit
|
||||||
|
@ -67,6 +98,22 @@ func newWatcher(key, addr string) (<-chan *viper.RemoteResponse, chan bool) {
|
||||||
return viperResponseCh, quit
|
return viperResponseCh, quit
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func makeIndexKey(rp viper.RemoteProvider) string {
|
||||||
viper.RemoteConfig = &consulConfigProvider{}
|
return rp.Endpoint() + "_" + rp.Path()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *consulConfigProvider) updateIndex(
|
||||||
|
rp viper.RemoteProvider, lastIndex uint64) (updated bool) {
|
||||||
|
rc.mu.Lock()
|
||||||
|
oldLastIndex := rc.idxMap[makeIndexKey(rp)]
|
||||||
|
if oldLastIndex < lastIndex {
|
||||||
|
rc.idxMap[makeIndexKey(rp)] = lastIndex
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
rc.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
viper.RemoteConfig = &consulConfigProvider{idxMap: make(map[string]uint64)}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
// Integrates the consul's remote features of Viper.
|
||||||
|
|
||||||
|
// viper/remote's problems:
|
||||||
|
// 1.consul watch do not work
|
||||||
|
// 2.outdated consul's lib
|
||||||
|
|
||||||
|
// use this ConsulRemoteConfigProvider, just replace
|
||||||
|
// _ "github.com/spf13/viper/remote" => _ "github.com/spf13/viper/remote/consul"
|
||||||
|
|
||||||
|
// usage example 1:
|
||||||
|
//func main() {
|
||||||
|
// v := viper.New()
|
||||||
|
// err := v.AddRemoteProvider("consul", "127.0.0.1:8500", "foo/bar")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// v.SetConfigType("json")
|
||||||
|
// err = v.ReadRemoteConfig()
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// for {
|
||||||
|
// // blocking until consul kv updated
|
||||||
|
// err = v.WatchRemoteConfig()
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// // your config is updated now.
|
||||||
|
// // ...
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// usage example 2:
|
||||||
|
//func main() {
|
||||||
|
// v := viper.New()
|
||||||
|
// err := v.AddRemoteProvider("consul", "127.0.0.1:8500", "foo/bar")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// v.SetConfigType("json")
|
||||||
|
// err = v.ReadRemoteConfig()
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// // your config will be update async
|
||||||
|
// v.WatchRemoteConfigOnChannel()
|
||||||
|
// // ....
|
||||||
|
//}
|
Loading…
Reference in New Issue