2019-10-02 18:56:53 +01:00
|
|
|
package etcd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2020-08-08 00:57:57 +01:00
|
|
|
"sync"
|
2019-10-02 18:56:53 +01:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/coreos/etcd/clientv3"
|
2020-08-19 17:47:17 +03:00
|
|
|
"github.com/unistack-org/micro/v3/registry"
|
2019-10-02 18:56:53 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type etcdWatcher struct {
|
|
|
|
w clientv3.WatchChan
|
|
|
|
client *clientv3.Client
|
|
|
|
timeout time.Duration
|
2020-08-08 00:57:57 +01:00
|
|
|
|
2020-08-08 01:40:41 +01:00
|
|
|
mtx sync.Mutex
|
|
|
|
stop chan bool
|
|
|
|
cancel func()
|
2019-10-02 18:56:53 +01:00
|
|
|
}
|
|
|
|
|
2020-08-08 01:40:41 +01:00
|
|
|
func newEtcdWatcher(c *clientv3.Client, timeout time.Duration, opts ...registry.WatchOption) (registry.Watcher, error) {
|
2019-10-02 18:56:53 +01:00
|
|
|
var wo registry.WatchOptions
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&wo)
|
|
|
|
}
|
2020-06-19 14:58:16 +01:00
|
|
|
if len(wo.Domain) == 0 {
|
|
|
|
wo.Domain = defaultDomain
|
|
|
|
}
|
2019-10-02 18:56:53 +01:00
|
|
|
|
|
|
|
watchPath := prefix
|
2020-06-19 14:58:16 +01:00
|
|
|
if wo.Domain == registry.WildcardDomain {
|
|
|
|
if len(wo.Service) > 0 {
|
2020-08-06 11:32:06 +01:00
|
|
|
return nil, errors.New("Cannot watch a service across domains")
|
2020-06-19 14:58:16 +01:00
|
|
|
}
|
|
|
|
watchPath = prefix
|
|
|
|
} else if len(wo.Service) > 0 {
|
|
|
|
watchPath = servicePath(wo.Domain, wo.Service) + "/"
|
2019-10-02 18:56:53 +01:00
|
|
|
}
|
|
|
|
|
2020-08-08 01:40:41 +01:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
w := c.Watch(ctx, watchPath, clientv3.WithPrefix(), clientv3.WithPrevKV())
|
|
|
|
stop := make(chan bool, 1)
|
|
|
|
|
2019-10-02 18:56:53 +01:00
|
|
|
return &etcdWatcher{
|
2020-08-08 01:40:41 +01:00
|
|
|
cancel: cancel,
|
2019-10-02 18:56:53 +01:00
|
|
|
stop: stop,
|
2020-08-08 01:40:41 +01:00
|
|
|
w: w,
|
|
|
|
client: c,
|
2019-10-02 18:56:53 +01:00
|
|
|
timeout: timeout,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ew *etcdWatcher) Next() (*registry.Result, error) {
|
|
|
|
for wresp := range ew.w {
|
|
|
|
if wresp.Err() != nil {
|
|
|
|
return nil, wresp.Err()
|
|
|
|
}
|
2020-02-13 22:34:56 +00:00
|
|
|
if wresp.Canceled {
|
|
|
|
return nil, errors.New("could not get next")
|
|
|
|
}
|
2019-10-02 18:56:53 +01:00
|
|
|
for _, ev := range wresp.Events {
|
|
|
|
service := decode(ev.Kv.Value)
|
|
|
|
var action string
|
|
|
|
|
|
|
|
switch ev.Type {
|
|
|
|
case clientv3.EventTypePut:
|
|
|
|
if ev.IsCreate() {
|
|
|
|
action = "create"
|
|
|
|
} else if ev.IsModify() {
|
|
|
|
action = "update"
|
|
|
|
}
|
|
|
|
case clientv3.EventTypeDelete:
|
|
|
|
action = "delete"
|
|
|
|
|
|
|
|
// get service from prevKv
|
|
|
|
service = decode(ev.PrevKv.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
if service == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return ®istry.Result{
|
|
|
|
Action: action,
|
|
|
|
Service: service,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, errors.New("could not get next")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ew *etcdWatcher) Stop() {
|
2020-08-08 00:57:57 +01:00
|
|
|
ew.mtx.Lock()
|
|
|
|
defer ew.mtx.Unlock()
|
|
|
|
|
2019-10-02 18:56:53 +01:00
|
|
|
select {
|
|
|
|
case <-ew.stop:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
close(ew.stop)
|
2020-08-08 01:40:41 +01:00
|
|
|
ew.cancel()
|
|
|
|
ew.client.Close()
|
2019-10-02 18:56:53 +01:00
|
|
|
}
|
|
|
|
}
|