Compare commits
129 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9d559848c2 | ||
|
2ae583ce94 | ||
|
7c1e22b607 | ||
|
7d2afa34a0 | ||
|
a6e95d389f | ||
|
b1d5dc20fa | ||
|
be5093798b | ||
|
3759c9c091 | ||
|
4936a2e1a5 | ||
|
ca934951ad | ||
|
ca18089382 | ||
|
7b1f5584ab | ||
|
fed5af68e6 | ||
|
fdfeb437f9 | ||
|
a46133f059 | ||
|
9bd0a8f3b5 | ||
|
44b794722e | ||
|
247249050b | ||
|
b1fed01752 | ||
|
df1e680256 | ||
|
854b01c20c | ||
|
745299bce5 | ||
|
607fdb3fcb | ||
|
a1342c23fb | ||
|
1cea2f5bba | ||
|
a1b4786682 | ||
|
b701da6d69 | ||
|
f77df51f60 | ||
|
d6c6e7815e | ||
|
01492997ea | ||
|
174f1b857c | ||
|
5029d80e68 | ||
|
b59c5a4488 | ||
|
f7f65b82e6 | ||
|
2e47fdc6f5 | ||
|
18ea19a122 | ||
|
4d75b936f8 | ||
|
62aaa72715 | ||
|
8c344ed55b | ||
|
db843c8d87 | ||
|
dd7677e6cc | ||
|
a4f0dd8939 | ||
|
591e87448b | ||
|
09a202ccf0 | ||
|
723c17fdd7 | ||
|
9bd96d4cc1 | ||
|
9bfe4d9bf7 | ||
|
76eee089e3 | ||
|
cfa2b668e2 | ||
|
a96f6adf07 | ||
|
49fe5d9fd5 | ||
|
21469a0427 | ||
|
e351e9518f | ||
|
fc89c9831e | ||
|
5e5d57d954 | ||
|
98e1f2c2d3 | ||
|
59a3e7d4f4 | ||
|
1be6ec9b3c | ||
|
f6931f3da7 | ||
|
b2f99a27b7 | ||
|
1f5ebf330d | ||
|
0dee11e006 | ||
|
b55018eaa1 | ||
|
77108771db | ||
|
5a6e73d4a8 | ||
|
7a4bff4e9f | ||
|
d5ce96da24 | ||
|
837597fe6f | ||
|
96e564e402 | ||
|
fe94237448 | ||
|
107b7419b7 | ||
|
226d55d752 | ||
|
88ef785127 | ||
|
44473f954f | ||
|
fe5846603a | ||
|
61800fb7d7 | ||
|
ec2fbde979 | ||
|
b886dd4b8f | ||
|
94adeebed4 | ||
|
d043ca15c1 | ||
|
ad823d5177 | ||
|
89d71417f5 | ||
|
9d9683b6f9 | ||
|
0edcd5c8dc | ||
|
2e1432d5dc | ||
|
e4f8b5de70 | ||
|
e9dcff49e0 | ||
|
fa6590f999 | ||
|
fd8a0fb2f5 | ||
|
b594547408 | ||
|
2c00e726b6 | ||
|
68a3fc7996 | ||
|
2fb2d7145e | ||
|
6fe9f2a958 | ||
|
86984a8a8a | ||
|
cfb846ee7e | ||
|
e36960612a | ||
|
04320d69ff | ||
|
c4b6d0f3a8 | ||
|
3c6b6553fb | ||
|
d5658ab0b0 | ||
|
2244eb8597 | ||
|
05eacd74c8 | ||
|
b80654bf7e | ||
|
0941a0f031 | ||
|
4de346920f | ||
|
b8815dff14 | ||
|
b1163b9dee | ||
|
af5d7a3420 | ||
|
b5f33b2aaa | ||
|
a9c85eda68 | ||
|
b5ca40a91a | ||
|
b81bb07afc | ||
|
8d2b12258f | ||
|
31026da2a1 | ||
|
1129803bcb | ||
|
25148af44c | ||
|
36675aff1e | ||
|
b6db0d2663 | ||
|
2370fb1209 | ||
|
519e8a7213 | ||
|
308424488b | ||
|
5d77ce9e9b | ||
|
9eb6262168 | ||
b722798caa | |||
|
0cf7b70423 | ||
|
03b8ceab5c | ||
|
e8a53610f1 | ||
|
e48155118f |
@@ -2,7 +2,7 @@ language: go
|
||||
go:
|
||||
- 1.13.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- GO111MODULE=on IN_TRAVIS_CI=yes
|
||||
notifications:
|
||||
slack:
|
||||
secure: aEvhLbhujaGaKSrOokiG3//PaVHTIrc3fBpoRbCRqfZpyq6WREoapJJhF+tIpWWOwaC9GmChbD6aHo/jMUgwKXVyPSaNjiEL87YzUUpL8B2zslNp1rgfTg/LrzthOx3Q1TYwpaAl3to0fuHUVFX4yMeC2vuThq7WSXgMMxFCtbc=
|
||||
|
24
api/server/acme/acme.go
Normal file
24
api/server/acme/acme.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Package acme abstracts away various ACME libraries
|
||||
package acme
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrProviderNotImplemented can be returned when attempting to
|
||||
// instantiate an unimplemented provider
|
||||
ErrProviderNotImplemented = errors.New("Provider not implemented")
|
||||
)
|
||||
|
||||
// Provider is a ACME provider interface
|
||||
type Provider interface {
|
||||
NewListener(...string) (net.Listener, error)
|
||||
}
|
||||
|
||||
// The Let's Encrypt ACME endpoints
|
||||
const (
|
||||
LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
)
|
23
api/server/acme/autocert/autocert.go
Normal file
23
api/server/acme/autocert/autocert.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Package autocert is the ACME provider from golang.org/x/crypto/acme/autocert
|
||||
// This provider does not take any config.
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/micro/go-micro/api/server/acme"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
// autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert
|
||||
type autocertProvider struct{}
|
||||
|
||||
// NewListener implements acme.Provider
|
||||
func (a *autocertProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
|
||||
return autocert.NewListener(ACMEHosts...), nil
|
||||
}
|
||||
|
||||
// New returns an autocert acme.Provider
|
||||
func New() acme.Provider {
|
||||
return &autocertProvider{}
|
||||
}
|
16
api/server/acme/autocert/autocert_test.go
Normal file
16
api/server/acme/autocert/autocert_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAutocert(t *testing.T) {
|
||||
l := New()
|
||||
if _, ok := l.(*autocertProvider); !ok {
|
||||
t.Error("New() didn't return an autocertProvider")
|
||||
}
|
||||
// TODO: Travis CI doesn't let us bind :443
|
||||
// if _, err := l.NewListener(); err != nil {
|
||||
// t.Error(err.Error())
|
||||
// }
|
||||
}
|
49
api/server/acme/certmagic/certmagic.go
Normal file
49
api/server/acme/certmagic/certmagic.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Package certmagic is the ACME provider from github.com/mholt/certmagic
|
||||
package certmagic
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/mholt/certmagic"
|
||||
|
||||
"github.com/micro/go-micro/api/server/acme"
|
||||
)
|
||||
|
||||
type certmagicProvider struct {
|
||||
opts *acme.Options
|
||||
}
|
||||
|
||||
func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
|
||||
if c.opts.ChallengeProvider != nil {
|
||||
// Enabling DNS Challenge disables the other challenges
|
||||
certmagic.Default.DNSProvider = c.opts.ChallengeProvider
|
||||
}
|
||||
if c.opts.OnDemand {
|
||||
certmagic.Default.OnDemand = new(certmagic.OnDemandConfig)
|
||||
}
|
||||
return certmagic.Listen(ACMEHosts)
|
||||
}
|
||||
|
||||
// New returns a certmagic provider
|
||||
func New(options ...acme.Option) acme.Provider {
|
||||
o := &acme.Options{}
|
||||
if len(options) == 0 {
|
||||
for _, op := range acme.Default() {
|
||||
op(o)
|
||||
}
|
||||
} else {
|
||||
for _, op := range options {
|
||||
op(o)
|
||||
}
|
||||
}
|
||||
if o.Cache != nil {
|
||||
if _, ok := o.Cache.(certmagic.Storage); !ok {
|
||||
log.Fatal("ACME: cache provided doesn't implement certmagic's Storage interface")
|
||||
}
|
||||
}
|
||||
|
||||
return &certmagicProvider{
|
||||
opts: o,
|
||||
}
|
||||
}
|
187
api/server/acme/certmagic/certmagic_test.go
Normal file
187
api/server/acme/certmagic/certmagic_test.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package certmagic
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v3/providers/dns/cloudflare"
|
||||
"github.com/mholt/certmagic"
|
||||
"github.com/micro/go-micro/api/server/acme"
|
||||
"github.com/micro/go-micro/config/options"
|
||||
cloudflarestorage "github.com/micro/go-micro/store/cloudflare"
|
||||
"github.com/micro/go-micro/sync/lock/memory"
|
||||
)
|
||||
|
||||
func TestCertMagic(t *testing.T) {
|
||||
if len(os.Getenv("IN_TRAVIS_CI")) != 0 {
|
||||
t.Skip("Travis doesn't let us bind :443")
|
||||
}
|
||||
l, err := New().NewListener()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
l.Close()
|
||||
|
||||
c := cloudflare.NewDefaultConfig()
|
||||
c.AuthEmail = ""
|
||||
c.AuthKey = ""
|
||||
c.AuthToken = "test"
|
||||
c.ZoneToken = "test"
|
||||
|
||||
p, err := cloudflare.NewDNSProviderConfig(c)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
l, err = New(acme.AcceptToS(true),
|
||||
acme.CA(acme.LetsEncryptStagingCA),
|
||||
acme.ChallengeProvider(p),
|
||||
).NewListener()
|
||||
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
l.Close()
|
||||
}
|
||||
|
||||
func TestStorageImplementation(t *testing.T) {
|
||||
apiToken, accountID := os.Getenv("CF_API_TOKEN"), os.Getenv("CF_ACCOUNT_ID")
|
||||
kvID := os.Getenv("KV_NAMESPACE_ID")
|
||||
if len(apiToken) == 0 || len(accountID) == 0 || len(kvID) == 0 {
|
||||
t.Skip("No Cloudflare API keys available, skipping test")
|
||||
}
|
||||
|
||||
var s certmagic.Storage
|
||||
st, err := cloudflarestorage.New(
|
||||
options.WithValue("CF_API_TOKEN", apiToken),
|
||||
options.WithValue("CF_ACCOUNT_ID", accountID),
|
||||
options.WithValue("KV_NAMESPACE_ID", kvID),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't initialise cloudflare storage: %s\n", err.Error())
|
||||
}
|
||||
s = &storage{
|
||||
lock: memory.NewLock(),
|
||||
store: st,
|
||||
}
|
||||
|
||||
// Test Lock
|
||||
if err := s.Lock("test"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Test Unlock
|
||||
if err := s.Unlock("test"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Test data
|
||||
testdata := []struct {
|
||||
key string
|
||||
value []byte
|
||||
}{
|
||||
{key: "/foo/a", value: []byte("lorem")},
|
||||
{key: "/foo/b", value: []byte("ipsum")},
|
||||
{key: "/foo/c", value: []byte("dolor")},
|
||||
{key: "/foo/d", value: []byte("sit")},
|
||||
{key: "/bar/a", value: []byte("amet")},
|
||||
{key: "/bar/b", value: []byte("consectetur")},
|
||||
{key: "/bar/c", value: []byte("adipiscing")},
|
||||
{key: "/bar/d", value: []byte("elit")},
|
||||
{key: "/foo/bar/a", value: []byte("sed")},
|
||||
{key: "/foo/bar/b", value: []byte("do")},
|
||||
{key: "/foo/bar/c", value: []byte("eiusmod")},
|
||||
{key: "/foo/bar/d", value: []byte("tempor")},
|
||||
{key: "/foo/bar/baz/a", value: []byte("incididunt")},
|
||||
{key: "/foo/bar/baz/b", value: []byte("ut")},
|
||||
{key: "/foo/bar/baz/c", value: []byte("labore")},
|
||||
{key: "/foo/bar/baz/d", value: []byte("et")},
|
||||
// a duplicate just in case there's any edge cases
|
||||
{key: "/foo/a", value: []byte("lorem")},
|
||||
}
|
||||
|
||||
// Test Store
|
||||
for _, d := range testdata {
|
||||
if err := s.Store(d.key, d.value); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Test Load
|
||||
for _, d := range testdata {
|
||||
if value, err := s.Load(d.key); err != nil {
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(value, d.value) {
|
||||
t.Errorf("Load %s: expected %v, got %v", d.key, d.value, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test Exists
|
||||
for _, d := range testdata {
|
||||
if !s.Exists(d.key) {
|
||||
t.Errorf("%s should exist, but doesn't\n", d.key)
|
||||
}
|
||||
}
|
||||
|
||||
// Test List
|
||||
if list, err := s.List("/", true); err != nil {
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
var expected []string
|
||||
for i, d := range testdata {
|
||||
if i != len(testdata)-1 {
|
||||
// Don't store the intentionally duplicated key
|
||||
expected = append(expected, d.key)
|
||||
}
|
||||
}
|
||||
sort.Strings(expected)
|
||||
sort.Strings(list)
|
||||
if !reflect.DeepEqual(expected, list) {
|
||||
t.Errorf("List: Expected %v, got %v\n", expected, list)
|
||||
}
|
||||
}
|
||||
if list, err := s.List("/foo", false); err != nil {
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
sort.Strings(list)
|
||||
expected := []string{"/foo/a", "/foo/b", "/foo/bar", "/foo/c", "/foo/d"}
|
||||
if !reflect.DeepEqual(expected, list) {
|
||||
t.Errorf("List: expected %s, got %s\n", expected, list)
|
||||
}
|
||||
}
|
||||
|
||||
// Test Stat
|
||||
for _, d := range testdata {
|
||||
info, err := s.Stat(d.key)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
if info.Key != d.key {
|
||||
t.Errorf("Stat().Key: expected %s, got %s\n", d.key, info.Key)
|
||||
}
|
||||
if info.Size != int64(len(d.value)) {
|
||||
t.Errorf("Stat().Size: expected %d, got %d\n", len(d.value), info.Size)
|
||||
}
|
||||
if time.Since(info.Modified) > time.Minute {
|
||||
t.Errorf("Stat().Modified: expected time since last modified to be < 1 minute, got %v\n", time.Since(info.Modified))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Test Delete
|
||||
for _, d := range testdata {
|
||||
if err := s.Delete(d.key); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// New interface doesn't return an error, so call it in case any log.Fatal
|
||||
// happens
|
||||
New(acme.Cache(s))
|
||||
}
|
134
api/server/acme/certmagic/storage.go
Normal file
134
api/server/acme/certmagic/storage.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package certmagic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mholt/certmagic"
|
||||
"github.com/micro/go-micro/store"
|
||||
"github.com/micro/go-micro/sync/lock"
|
||||
)
|
||||
|
||||
// File represents a "File" that will be stored in store.Store - the contents and last modified time
|
||||
type File struct {
|
||||
// last modified time
|
||||
LastModified time.Time
|
||||
// Contents
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
// storage is an implementation of certmagic.Storage using micro's sync.Map and store.Store interfaces.
|
||||
// As certmagic storage expects a filesystem (with stat() abilities) we have to implement
|
||||
// the bare minimum of metadata.
|
||||
type storage struct {
|
||||
lock lock.Lock
|
||||
store store.Store
|
||||
}
|
||||
|
||||
func (s *storage) Lock(key string) error {
|
||||
return s.lock.Acquire(key, lock.TTL(10*time.Minute))
|
||||
}
|
||||
|
||||
func (s *storage) Unlock(key string) error {
|
||||
return s.lock.Release(key)
|
||||
}
|
||||
|
||||
func (s *storage) Store(key string, value []byte) error {
|
||||
f := File{
|
||||
LastModified: time.Now(),
|
||||
Contents: value,
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
e := gob.NewEncoder(buf)
|
||||
if err := e.Encode(f); err != nil {
|
||||
return err
|
||||
}
|
||||
r := &store.Record{
|
||||
Key: key,
|
||||
Value: buf.Bytes(),
|
||||
}
|
||||
return s.store.Write(r)
|
||||
}
|
||||
|
||||
func (s *storage) Load(key string) ([]byte, error) {
|
||||
records, err := s.store.Read(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(records) != 1 {
|
||||
return nil, fmt.Errorf("ACME Storage: multiple records matched key %s", key)
|
||||
}
|
||||
b := bytes.NewBuffer(records[0].Value)
|
||||
d := gob.NewDecoder(b)
|
||||
var f File
|
||||
err = d.Decode(&f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.Contents, nil
|
||||
}
|
||||
|
||||
func (s *storage) Delete(key string) error {
|
||||
return s.store.Delete(key)
|
||||
}
|
||||
|
||||
func (s *storage) Exists(key string) bool {
|
||||
_, err := s.store.Read()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *storage) List(prefix string, recursive bool) ([]string, error) {
|
||||
records, err := s.store.Sync()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var results []string
|
||||
for _, r := range records {
|
||||
if strings.HasPrefix(r.Key, prefix) {
|
||||
results = append(results, r.Key)
|
||||
}
|
||||
}
|
||||
if recursive {
|
||||
return results, nil
|
||||
}
|
||||
keysMap := make(map[string]bool)
|
||||
for _, key := range results {
|
||||
dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
|
||||
keysMap[dir[0]] = true
|
||||
}
|
||||
results = make([]string, 0)
|
||||
for k := range keysMap {
|
||||
results = append(results, path.Join(prefix, k))
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (s *storage) Stat(key string) (certmagic.KeyInfo, error) {
|
||||
records, err := s.store.Read(key)
|
||||
if err != nil {
|
||||
return certmagic.KeyInfo{}, err
|
||||
}
|
||||
if len(records) != 1 {
|
||||
return certmagic.KeyInfo{}, fmt.Errorf("ACME Storage: multiple records matched key %s", key)
|
||||
}
|
||||
b := bytes.NewBuffer(records[0].Value)
|
||||
d := gob.NewDecoder(b)
|
||||
var f File
|
||||
err = d.Decode(&f)
|
||||
if err != nil {
|
||||
return certmagic.KeyInfo{}, err
|
||||
}
|
||||
return certmagic.KeyInfo{
|
||||
Key: key,
|
||||
Modified: f.LastModified,
|
||||
Size: int64(len(f.Contents)),
|
||||
IsTerminal: false,
|
||||
}, nil
|
||||
}
|
73
api/server/acme/options.go
Normal file
73
api/server/acme/options.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package acme
|
||||
|
||||
import "github.com/go-acme/lego/v3/challenge"
|
||||
|
||||
// Option (or Options) are passed to New() to configure providers
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options represents various options you can present to ACME providers
|
||||
type Options struct {
|
||||
// AcceptTLS must be set to true to indicate that you have read your
|
||||
// provider's terms of service.
|
||||
AcceptToS bool
|
||||
// CA is the CA to use
|
||||
CA string
|
||||
// ChallengeProvider is a go-acme/lego challenge provider. Set this if you
|
||||
// want to use DNS Challenges. Otherwise, tls-alpn-01 will be used
|
||||
ChallengeProvider challenge.Provider
|
||||
// Issue certificates for domains on demand. Otherwise, certs will be
|
||||
// retrieved / issued on start-up.
|
||||
OnDemand bool
|
||||
// Cache is a storage interface. Most ACME libraries have an cache, but
|
||||
// there's no defined interface, so if you consume this option
|
||||
// sanity check it before using.
|
||||
Cache interface{}
|
||||
}
|
||||
|
||||
// AcceptToS indicates whether you accept your CA's terms of service
|
||||
func AcceptToS(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.AcceptToS = b
|
||||
}
|
||||
}
|
||||
|
||||
// CA sets the CA of an acme.Options
|
||||
func CA(CA string) Option {
|
||||
return func(o *Options) {
|
||||
o.CA = CA
|
||||
}
|
||||
}
|
||||
|
||||
// ChallengeProvider sets the Challenge provider of an acme.Options
|
||||
// if set, it enables the DNS challenge, otherwise tls-alpn-01 will be used.
|
||||
func ChallengeProvider(p challenge.Provider) Option {
|
||||
return func(o *Options) {
|
||||
o.ChallengeProvider = p
|
||||
}
|
||||
}
|
||||
|
||||
// OnDemand enables on-demand certificate issuance. Not recommended for use
|
||||
// with the DNS challenge, as the first connection may be very slow.
|
||||
func OnDemand(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.OnDemand = b
|
||||
}
|
||||
}
|
||||
|
||||
// Cache provides a cache / storage interface to the underlying ACME library
|
||||
// as there is no standard, this needs to be validated by the underlying
|
||||
// implentation.
|
||||
func Cache(c interface{}) Option {
|
||||
return func(o *Options) {
|
||||
o.Cache = c
|
||||
}
|
||||
}
|
||||
|
||||
// Default uses the Let's Encrypt Production CA, with DNS Challenge disabled.
|
||||
func Default() []Option {
|
||||
return []Option{
|
||||
AcceptToS(true),
|
||||
CA(LetsEncryptProductionCA),
|
||||
OnDemand(true),
|
||||
}
|
||||
}
|
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/micro/go-micro/api/server"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
type httpServer struct {
|
||||
@@ -53,9 +52,9 @@ func (s *httpServer) Start() error {
|
||||
var l net.Listener
|
||||
var err error
|
||||
|
||||
if s.opts.EnableACME {
|
||||
if s.opts.EnableACME && s.opts.ACMEProvider != nil {
|
||||
// should we check the address to make sure its using :443?
|
||||
l = autocert.NewListener(s.opts.ACMEHosts...)
|
||||
l, err = s.opts.ACMEProvider.NewListener(s.opts.ACMEHosts...)
|
||||
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
|
||||
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig)
|
||||
} else {
|
||||
|
@@ -2,15 +2,24 @@ package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/micro/go-micro/api/server/acme"
|
||||
)
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
type Options struct {
|
||||
EnableACME bool
|
||||
EnableTLS bool
|
||||
ACMEHosts []string
|
||||
TLSConfig *tls.Config
|
||||
EnableACME bool
|
||||
ACMEProvider acme.Provider
|
||||
EnableTLS bool
|
||||
ACMEHosts []string
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
func EnableACME(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.EnableACME = b
|
||||
}
|
||||
}
|
||||
|
||||
func ACMEHosts(hosts ...string) Option {
|
||||
@@ -19,9 +28,9 @@ func ACMEHosts(hosts ...string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func EnableACME(b bool) Option {
|
||||
func ACMEProvider(p acme.Provider) Option {
|
||||
return func(o *Options) {
|
||||
o.EnableACME = b
|
||||
o.ACMEProvider = p
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -324,15 +324,21 @@ func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
p := &httpEvent{m: m, t: topic}
|
||||
id := req.Form.Get("id")
|
||||
|
||||
var subs []Handler
|
||||
|
||||
h.RLock()
|
||||
for _, subscriber := range h.subscribers[topic] {
|
||||
if id == subscriber.id {
|
||||
// sub is sync; crufty rate limiting
|
||||
// so we don't hose the cpu
|
||||
subscriber.fn(p)
|
||||
if id != subscriber.id {
|
||||
continue
|
||||
}
|
||||
subs = append(subs, subscriber.fn)
|
||||
}
|
||||
h.RUnlock()
|
||||
|
||||
// execute the handler
|
||||
for _, fn := range subs {
|
||||
fn(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *httpBroker) Address() string {
|
||||
@@ -420,7 +426,6 @@ func (h *httpBroker) Connect() error {
|
||||
}
|
||||
|
||||
func (h *httpBroker) Disconnect() error {
|
||||
|
||||
h.RLock()
|
||||
if !h.running {
|
||||
h.RUnlock()
|
||||
|
66
broker/service/handler/handler.go
Normal file
66
broker/service/handler/handler.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/go-micro/broker"
|
||||
pb "github.com/micro/go-micro/broker/service/proto"
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
type Broker struct {
|
||||
Broker broker.Broker
|
||||
}
|
||||
|
||||
func (b *Broker) Publish(ctx context.Context, req *pb.PublishRequest, rsp *pb.Empty) error {
|
||||
log.Debugf("Publishing message to %s topic", req.Topic)
|
||||
err := b.Broker.Publish(req.Topic, &broker.Message{
|
||||
Header: req.Message.Header,
|
||||
Body: req.Message.Body,
|
||||
})
|
||||
log.Debugf("Published message to %s topic", req.Topic)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.broker", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Broker) Subscribe(ctx context.Context, req *pb.SubscribeRequest, stream pb.Broker_SubscribeStream) error {
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
// message handler to stream back messages from broker
|
||||
handler := func(p broker.Event) error {
|
||||
if err := stream.Send(&pb.Message{
|
||||
Header: p.Message().Header,
|
||||
Body: p.Message().Body,
|
||||
}); err != nil {
|
||||
select {
|
||||
case errChan <- err:
|
||||
return err
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("Subscribing to %s topic", req.Topic)
|
||||
sub, err := b.Broker.Subscribe(req.Topic, handler, broker.Queue(req.Queue))
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.broker", err.Error())
|
||||
}
|
||||
defer func() {
|
||||
log.Debugf("Unsubscribing from topic %s", req.Topic)
|
||||
sub.Unsubscribe()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Debugf("Context done for subscription to topic %s", req.Topic)
|
||||
return nil
|
||||
case err := <-errChan:
|
||||
log.Debugf("Subscription error for topic %s: %v", req.Topic, err)
|
||||
return err
|
||||
}
|
||||
}
|
173
broker/service/proto/broker.micro.go
Normal file
173
broker/service/proto/broker.micro.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: github.com/micro/go-micro/broker/proto/broker.proto
|
||||
|
||||
package go_micro_broker
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
client "github.com/micro/go-micro/client"
|
||||
server "github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Client API for Broker service
|
||||
|
||||
type BrokerService interface {
|
||||
Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error)
|
||||
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error)
|
||||
}
|
||||
|
||||
type brokerService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewBrokerService(name string, c client.Client) BrokerService {
|
||||
if c == nil {
|
||||
c = client.NewClient()
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = "go.micro.broker"
|
||||
}
|
||||
return &brokerService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *brokerService) Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error) {
|
||||
req := c.c.NewRequest(c.name, "Broker.Publish", in)
|
||||
out := new(Empty)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *brokerService) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error) {
|
||||
req := c.c.NewRequest(c.name, "Broker.Subscribe", &SubscribeRequest{})
|
||||
stream, err := c.c.Stream(ctx, req, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := stream.Send(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &brokerServiceSubscribe{stream}, nil
|
||||
}
|
||||
|
||||
type Broker_SubscribeService interface {
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Recv() (*Message, error)
|
||||
}
|
||||
|
||||
type brokerServiceSubscribe struct {
|
||||
stream client.Stream
|
||||
}
|
||||
|
||||
func (x *brokerServiceSubscribe) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *brokerServiceSubscribe) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *brokerServiceSubscribe) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *brokerServiceSubscribe) Recv() (*Message, error) {
|
||||
m := new(Message)
|
||||
err := x.stream.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Server API for Broker service
|
||||
|
||||
type BrokerHandler interface {
|
||||
Publish(context.Context, *PublishRequest, *Empty) error
|
||||
Subscribe(context.Context, *SubscribeRequest, Broker_SubscribeStream) error
|
||||
}
|
||||
|
||||
func RegisterBrokerHandler(s server.Server, hdlr BrokerHandler, opts ...server.HandlerOption) error {
|
||||
type broker interface {
|
||||
Publish(ctx context.Context, in *PublishRequest, out *Empty) error
|
||||
Subscribe(ctx context.Context, stream server.Stream) error
|
||||
}
|
||||
type Broker struct {
|
||||
broker
|
||||
}
|
||||
h := &brokerHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Broker{h}, opts...))
|
||||
}
|
||||
|
||||
type brokerHandler struct {
|
||||
BrokerHandler
|
||||
}
|
||||
|
||||
func (h *brokerHandler) Publish(ctx context.Context, in *PublishRequest, out *Empty) error {
|
||||
return h.BrokerHandler.Publish(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *brokerHandler) Subscribe(ctx context.Context, stream server.Stream) error {
|
||||
m := new(SubscribeRequest)
|
||||
if err := stream.Recv(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return h.BrokerHandler.Subscribe(ctx, m, &brokerSubscribeStream{stream})
|
||||
}
|
||||
|
||||
type Broker_SubscribeStream interface {
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*Message) error
|
||||
}
|
||||
|
||||
type brokerSubscribeStream struct {
|
||||
stream server.Stream
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeStream) Send(m *Message) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
364
broker/service/proto/broker.pb.go
Normal file
364
broker/service/proto/broker.pb.go
Normal file
@@ -0,0 +1,364 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/micro/go-micro/broker/proto/broker.proto
|
||||
|
||||
package go_micro_broker
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Empty struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Empty) Reset() { *m = Empty{} }
|
||||
func (m *Empty) String() string { return proto.CompactTextString(m) }
|
||||
func (*Empty) ProtoMessage() {}
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_5edf81766900dd99, []int{0}
|
||||
}
|
||||
|
||||
func (m *Empty) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Empty.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Empty) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Empty.Merge(m, src)
|
||||
}
|
||||
func (m *Empty) XXX_Size() int {
|
||||
return xxx_messageInfo_Empty.Size(m)
|
||||
}
|
||||
func (m *Empty) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Empty.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Empty proto.InternalMessageInfo
|
||||
|
||||
type PublishRequest struct {
|
||||
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
|
||||
Message *Message `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PublishRequest) Reset() { *m = PublishRequest{} }
|
||||
func (m *PublishRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*PublishRequest) ProtoMessage() {}
|
||||
func (*PublishRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_5edf81766900dd99, []int{1}
|
||||
}
|
||||
|
||||
func (m *PublishRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PublishRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PublishRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PublishRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PublishRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PublishRequest.Merge(m, src)
|
||||
}
|
||||
func (m *PublishRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_PublishRequest.Size(m)
|
||||
}
|
||||
func (m *PublishRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PublishRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PublishRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *PublishRequest) GetTopic() string {
|
||||
if m != nil {
|
||||
return m.Topic
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PublishRequest) GetMessage() *Message {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SubscribeRequest struct {
|
||||
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
|
||||
Queue string `protobuf:"bytes,2,opt,name=queue,proto3" json:"queue,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} }
|
||||
func (m *SubscribeRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SubscribeRequest) ProtoMessage() {}
|
||||
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_5edf81766900dd99, []int{2}
|
||||
}
|
||||
|
||||
func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SubscribeRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *SubscribeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_SubscribeRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *SubscribeRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_SubscribeRequest.Merge(m, src)
|
||||
}
|
||||
func (m *SubscribeRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_SubscribeRequest.Size(m)
|
||||
}
|
||||
func (m *SubscribeRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_SubscribeRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_SubscribeRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *SubscribeRequest) GetTopic() string {
|
||||
if m != nil {
|
||||
return m.Topic
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SubscribeRequest) GetQueue() string {
|
||||
if m != nil {
|
||||
return m.Queue
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Header map[string]string `protobuf:"bytes,1,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_5edf81766900dd99, []int{3}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Message.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Message) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Message.Merge(m, src)
|
||||
}
|
||||
func (m *Message) XXX_Size() int {
|
||||
return xxx_messageInfo_Message.Size(m)
|
||||
}
|
||||
func (m *Message) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Message.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Message proto.InternalMessageInfo
|
||||
|
||||
func (m *Message) GetHeader() map[string]string {
|
||||
if m != nil {
|
||||
return m.Header
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Empty)(nil), "go.micro.broker.Empty")
|
||||
proto.RegisterType((*PublishRequest)(nil), "go.micro.broker.PublishRequest")
|
||||
proto.RegisterType((*SubscribeRequest)(nil), "go.micro.broker.SubscribeRequest")
|
||||
proto.RegisterType((*Message)(nil), "go.micro.broker.Message")
|
||||
proto.RegisterMapType((map[string]string)(nil), "go.micro.broker.Message.HeaderEntry")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/micro/go-micro/broker/proto/broker.proto", fileDescriptor_5edf81766900dd99)
|
||||
}
|
||||
|
||||
var fileDescriptor_5edf81766900dd99 = []byte{
|
||||
// 309 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xcf, 0x4a, 0xf3, 0x40,
|
||||
0x14, 0xc5, 0x3b, 0xed, 0xd7, 0x86, 0xde, 0x7e, 0x68, 0x19, 0x8a, 0x84, 0x6e, 0x8c, 0xc1, 0x45,
|
||||
0x36, 0x4e, 0x24, 0xdd, 0xa8, 0x88, 0x0b, 0xb1, 0xe0, 0x42, 0x41, 0xc6, 0x9d, 0xbb, 0x4c, 0x3a,
|
||||
0x24, 0xa1, 0x8d, 0x93, 0x4e, 0x66, 0x84, 0xbc, 0x88, 0x2b, 0x1f, 0x56, 0x3a, 0x93, 0xfa, 0xa7,
|
||||
0xa1, 0xee, 0xee, 0x49, 0x7e, 0x73, 0xee, 0xe1, 0x5c, 0x98, 0xa5, 0xb9, 0xca, 0x34, 0x23, 0x89,
|
||||
0x28, 0xc2, 0x22, 0x4f, 0xa4, 0x08, 0x53, 0x71, 0x66, 0x07, 0x26, 0xc5, 0x92, 0xcb, 0xb0, 0x94,
|
||||
0x42, 0x6d, 0x05, 0x31, 0x02, 0x1f, 0xa6, 0x82, 0x18, 0x86, 0xd8, 0xcf, 0xbe, 0x03, 0xfd, 0x79,
|
||||
0x51, 0xaa, 0xda, 0x7f, 0x81, 0x83, 0x27, 0xcd, 0x56, 0x79, 0x95, 0x51, 0xbe, 0xd6, 0xbc, 0x52,
|
||||
0x78, 0x02, 0x7d, 0x25, 0xca, 0x3c, 0x71, 0x91, 0x87, 0x82, 0x21, 0xb5, 0x02, 0x47, 0xe0, 0x14,
|
||||
0xbc, 0xaa, 0xe2, 0x94, 0xbb, 0x5d, 0x0f, 0x05, 0xa3, 0xc8, 0x25, 0x3b, 0x9e, 0xe4, 0xd1, 0xfe,
|
||||
0xa7, 0x5b, 0xd0, 0xbf, 0x81, 0xf1, 0xb3, 0x66, 0x55, 0x22, 0x73, 0xc6, 0xff, 0x76, 0x9f, 0x40,
|
||||
0x7f, 0xad, 0xb9, 0xb6, 0xde, 0x43, 0x6a, 0x85, 0xff, 0x8e, 0xc0, 0x69, 0x4c, 0xf1, 0x35, 0x0c,
|
||||
0x32, 0x1e, 0x2f, 0xb8, 0x74, 0x91, 0xd7, 0x0b, 0x46, 0xd1, 0xe9, 0xbe, 0xf5, 0xe4, 0xde, 0x60,
|
||||
0xf3, 0x57, 0x25, 0x6b, 0xda, 0xbc, 0xc1, 0x18, 0xfe, 0x31, 0xb1, 0xa8, 0x8d, 0xfd, 0x7f, 0x6a,
|
||||
0xe6, 0xe9, 0x25, 0x8c, 0x7e, 0xa0, 0x78, 0x0c, 0xbd, 0x25, 0xaf, 0x9b, 0x58, 0x9b, 0x71, 0x13,
|
||||
0xea, 0x2d, 0x5e, 0x7d, 0x87, 0x32, 0xe2, 0xaa, 0x7b, 0x81, 0xa2, 0x0f, 0x04, 0x83, 0x5b, 0xb3,
|
||||
0x15, 0xdf, 0x81, 0xd3, 0xf4, 0x87, 0x8f, 0x5b, 0x91, 0x7e, 0x37, 0x3b, 0x3d, 0x6a, 0x01, 0xf6,
|
||||
0x06, 0x1d, 0xfc, 0x00, 0xc3, 0xaf, 0xa6, 0xf0, 0x49, 0x0b, 0xdb, 0x6d, 0x71, 0xba, 0xb7, 0x7c,
|
||||
0xbf, 0x73, 0x8e, 0xd8, 0xc0, 0x1c, 0x7d, 0xf6, 0x19, 0x00, 0x00, 0xff, 0xff, 0x25, 0x38, 0xfa,
|
||||
0x02, 0x2b, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// BrokerClient is the client API for Broker service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type BrokerClient interface {
|
||||
Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*Empty, error)
|
||||
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Broker_SubscribeClient, error)
|
||||
}
|
||||
|
||||
type brokerClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewBrokerClient(cc *grpc.ClientConn) BrokerClient {
|
||||
return &brokerClient{cc}
|
||||
}
|
||||
|
||||
func (c *brokerClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.broker.Broker/Publish", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *brokerClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Broker_SubscribeClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_Broker_serviceDesc.Streams[0], "/go.micro.broker.Broker/Subscribe", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &brokerSubscribeClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Broker_SubscribeClient interface {
|
||||
Recv() (*Message, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type brokerSubscribeClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeClient) Recv() (*Message, error) {
|
||||
m := new(Message)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// BrokerServer is the server API for Broker service.
|
||||
type BrokerServer interface {
|
||||
Publish(context.Context, *PublishRequest) (*Empty, error)
|
||||
Subscribe(*SubscribeRequest, Broker_SubscribeServer) error
|
||||
}
|
||||
|
||||
func RegisterBrokerServer(s *grpc.Server, srv BrokerServer) {
|
||||
s.RegisterService(&_Broker_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Broker_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PublishRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BrokerServer).Publish(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.broker.Broker/Publish",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BrokerServer).Publish(ctx, req.(*PublishRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Broker_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SubscribeRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(BrokerServer).Subscribe(m, &brokerSubscribeServer{stream})
|
||||
}
|
||||
|
||||
type Broker_SubscribeServer interface {
|
||||
Send(*Message) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type brokerSubscribeServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeServer) Send(m *Message) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
var _Broker_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.broker.Broker",
|
||||
HandlerType: (*BrokerServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Publish",
|
||||
Handler: _Broker_Publish_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Subscribe",
|
||||
Handler: _Broker_Subscribe_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "github.com/micro/go-micro/broker/proto/broker.proto",
|
||||
}
|
25
broker/service/proto/broker.proto
Normal file
25
broker/service/proto/broker.proto
Normal file
@@ -0,0 +1,25 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package go.micro.broker;
|
||||
|
||||
service Broker {
|
||||
rpc Publish(PublishRequest) returns (Empty) {};
|
||||
rpc Subscribe(SubscribeRequest) returns (stream Message) {};
|
||||
}
|
||||
|
||||
message Empty {}
|
||||
|
||||
message PublishRequest {
|
||||
string topic = 1;
|
||||
Message message = 2;
|
||||
}
|
||||
|
||||
message SubscribeRequest {
|
||||
string topic = 1;
|
||||
string queue = 2;
|
||||
}
|
||||
|
||||
message Message {
|
||||
map<string,string> header = 1;
|
||||
bytes body = 2;
|
||||
}
|
132
broker/service/service.go
Normal file
132
broker/service/service.go
Normal file
@@ -0,0 +1,132 @@
|
||||
// Package service provides the broker service client
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/broker"
|
||||
pb "github.com/micro/go-micro/broker/service/proto"
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
type serviceBroker struct {
|
||||
Addrs []string
|
||||
Client pb.BrokerService
|
||||
options broker.Options
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultName = "go.micro.broker"
|
||||
)
|
||||
|
||||
func (b *serviceBroker) Address() string {
|
||||
return b.Addrs[0]
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Disconnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Init(opts ...broker.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&b.options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Options() broker.Options {
|
||||
return b.options
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
|
||||
log.Debugf("Publishing to topic %s broker %v", topic, b.Addrs)
|
||||
_, err := b.Client.Publish(context.TODO(), &pb.PublishRequest{
|
||||
Topic: topic,
|
||||
Message: &pb.Message{
|
||||
Header: msg.Header,
|
||||
Body: msg.Body,
|
||||
},
|
||||
}, client.WithAddress(b.Addrs...))
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *serviceBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
||||
var options broker.SubscribeOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
log.Debugf("Subscribing to topic %s queue %s broker %v", topic, options.Queue, b.Addrs)
|
||||
stream, err := b.Client.Subscribe(context.TODO(), &pb.SubscribeRequest{
|
||||
Topic: topic,
|
||||
Queue: options.Queue,
|
||||
}, client.WithAddress(b.Addrs...), client.WithRequestTimeout(time.Hour))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sub := &serviceSub{
|
||||
topic: topic,
|
||||
queue: options.Queue,
|
||||
handler: handler,
|
||||
stream: stream,
|
||||
closed: make(chan bool),
|
||||
options: options,
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-sub.closed:
|
||||
log.Debugf("Unsubscribed from topic %s", topic)
|
||||
return
|
||||
default:
|
||||
// run the subscriber
|
||||
log.Debugf("Streaming from broker %v to topic [%s] queue [%s]", b.Addrs, topic, options.Queue)
|
||||
if err := sub.run(); err != nil {
|
||||
log.Debugf("Resubscribing to topic %s broker %v", topic, b.Addrs)
|
||||
stream, err := b.Client.Subscribe(context.TODO(), &pb.SubscribeRequest{
|
||||
Topic: topic,
|
||||
Queue: options.Queue,
|
||||
}, client.WithAddress(b.Addrs...), client.WithRequestTimeout(time.Hour))
|
||||
if err != nil {
|
||||
log.Debugf("Failed to resubscribe to topic %s: %v", topic, err)
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
// new stream
|
||||
sub.stream = stream
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return sub, nil
|
||||
}
|
||||
|
||||
func (b *serviceBroker) String() string {
|
||||
return "service"
|
||||
}
|
||||
|
||||
func NewBroker(opts ...broker.Option) broker.Broker {
|
||||
var options broker.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
addrs := options.Addrs
|
||||
if len(addrs) == 0 {
|
||||
addrs = []string{"127.0.0.1:8001"}
|
||||
}
|
||||
|
||||
return &serviceBroker{
|
||||
Addrs: addrs,
|
||||
Client: pb.NewBrokerService(DefaultName, client.DefaultClient),
|
||||
options: options,
|
||||
}
|
||||
}
|
101
broker/service/subscriber.go
Normal file
101
broker/service/subscriber.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/broker"
|
||||
pb "github.com/micro/go-micro/broker/service/proto"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
type serviceSub struct {
|
||||
topic string
|
||||
queue string
|
||||
handler broker.Handler
|
||||
stream pb.Broker_SubscribeService
|
||||
closed chan bool
|
||||
options broker.SubscribeOptions
|
||||
}
|
||||
|
||||
type serviceEvent struct {
|
||||
topic string
|
||||
message *broker.Message
|
||||
}
|
||||
|
||||
func (s *serviceEvent) Topic() string {
|
||||
return s.topic
|
||||
}
|
||||
|
||||
func (s *serviceEvent) Message() *broker.Message {
|
||||
return s.message
|
||||
}
|
||||
|
||||
func (s *serviceEvent) Ack() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *serviceSub) isClosed() bool {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceSub) run() error {
|
||||
exit := make(chan bool)
|
||||
go func() {
|
||||
select {
|
||||
case <-exit:
|
||||
case <-s.closed:
|
||||
}
|
||||
|
||||
// close the stream
|
||||
s.stream.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
// TODO: do not fail silently
|
||||
msg, err := s.stream.Recv()
|
||||
if err != nil {
|
||||
log.Debugf("Streaming error for subcription to topic %s: %v", s.Topic(), err)
|
||||
|
||||
// close the exit channel
|
||||
close(exit)
|
||||
|
||||
// don't return an error if we unsubscribed
|
||||
if s.isClosed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// return stream error
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: handle error
|
||||
s.handler(&serviceEvent{
|
||||
topic: s.topic,
|
||||
message: &broker.Message{
|
||||
Header: msg.Header,
|
||||
Body: msg.Body,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceSub) Options() broker.SubscribeOptions {
|
||||
return s.options
|
||||
}
|
||||
|
||||
func (s *serviceSub) Topic() string {
|
||||
return s.topic
|
||||
}
|
||||
|
||||
func (s *serviceSub) Unsubscribe() error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return nil
|
||||
default:
|
||||
close(s.closed)
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -110,12 +110,21 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
|
||||
|
||||
var grr error
|
||||
|
||||
cc, err := g.pool.getConn(address, grpc.WithDefaultCallOptions(grpc.ForceCodec(cf)),
|
||||
grpc.WithTimeout(opts.DialTimeout), g.secure(),
|
||||
grpcDialOptions := []grpc.DialOption{
|
||||
grpc.WithDefaultCallOptions(grpc.ForceCodec(cf)),
|
||||
grpc.WithTimeout(opts.DialTimeout),
|
||||
g.secure(),
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(maxRecvMsgSize),
|
||||
grpc.MaxCallSendMsgSize(maxSendMsgSize),
|
||||
))
|
||||
),
|
||||
}
|
||||
|
||||
if opts := g.getGrpcDialOptions(); opts != nil {
|
||||
grpcDialOptions = append(grpcDialOptions, opts...)
|
||||
}
|
||||
|
||||
cc, err := g.pool.getConn(address, grpcDialOptions...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
|
||||
}
|
||||
@@ -127,7 +136,11 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
|
||||
ch := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpc.CallContentSubtype(cf.Name()))
|
||||
grpcCallOptions := []grpc.CallOption{grpc.CallContentSubtype(cf.Name())}
|
||||
if opts := g.getGrpcCallOptions(); opts != nil {
|
||||
grpcCallOptions = append(grpcCallOptions, opts...)
|
||||
}
|
||||
err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpcCallOptions...)
|
||||
ch <- microError(err)
|
||||
}()
|
||||
|
||||
@@ -175,7 +188,16 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
|
||||
|
||||
wc := wrapCodec{cf}
|
||||
|
||||
cc, err := grpc.DialContext(dialCtx, address, grpc.WithDefaultCallOptions(grpc.ForceCodec(wc)), g.secure())
|
||||
grpcDialOptions := []grpc.DialOption{
|
||||
grpc.WithDefaultCallOptions(grpc.ForceCodec(wc)),
|
||||
g.secure(),
|
||||
}
|
||||
|
||||
if opts := g.getGrpcDialOptions(); opts != nil {
|
||||
grpcDialOptions = append(grpcDialOptions, opts...)
|
||||
}
|
||||
|
||||
cc, err := grpc.DialContext(dialCtx, address, grpcDialOptions...)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
|
||||
}
|
||||
@@ -186,7 +208,11 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
|
||||
ServerStreams: true,
|
||||
}
|
||||
|
||||
st, err := cc.NewStream(ctx, desc, methodToGRPC(req.Service(), req.Endpoint()))
|
||||
grpcCallOptions := []grpc.CallOption{}
|
||||
if opts := g.getGrpcCallOptions(); opts != nil {
|
||||
grpcCallOptions = append(grpcCallOptions, opts...)
|
||||
}
|
||||
st, err := cc.NewStream(ctx, desc, methodToGRPC(req.Service(), req.Endpoint()), grpcCallOptions...)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err))
|
||||
}
|
||||
@@ -514,6 +540,46 @@ func (g *grpcClient) String() string {
|
||||
return "grpc"
|
||||
}
|
||||
|
||||
func (g *grpcClient) getGrpcDialOptions() []grpc.DialOption {
|
||||
if g.opts.CallOptions.Context == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := g.opts.CallOptions.Context.Value(grpcDialOptions{})
|
||||
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
opts, ok := v.([]grpc.DialOption)
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func (g *grpcClient) getGrpcCallOptions() []grpc.CallOption {
|
||||
if g.opts.CallOptions.Context == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := g.opts.CallOptions.Context.Value(grpcCallOptions{})
|
||||
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
opts, ok := v.([]grpc.CallOption)
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func newClient(opts ...client.Option) client.Client {
|
||||
options := client.Options{
|
||||
Codecs: make(map[string]codec.NewCodec),
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/micro/go-micro/client"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/encoding"
|
||||
)
|
||||
|
||||
@@ -23,6 +24,8 @@ type codecsKey struct{}
|
||||
type tlsAuth struct{}
|
||||
type maxRecvMsgSizeKey struct{}
|
||||
type maxSendMsgSizeKey struct{}
|
||||
type grpcDialOptions struct{}
|
||||
type grpcCallOptions struct{}
|
||||
|
||||
// gRPC Codec to be used to encode/decode requests for a given content type
|
||||
func Codec(contentType string, c encoding.Codec) client.Option {
|
||||
@@ -72,3 +75,27 @@ func MaxSendMsgSize(s int) client.Option {
|
||||
o.Context = context.WithValue(o.Context, maxSendMsgSizeKey{}, s)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// DialOptions to be used to configure gRPC dial options
|
||||
//
|
||||
func DialOptions(opts ...grpc.DialOption) client.CallOption {
|
||||
return func(o *client.CallOptions) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, grpcDialOptions{}, opts)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CallOptions to be used to configure gRPC call options
|
||||
//
|
||||
func CallOptions(opts ...grpc.CallOption) client.CallOption {
|
||||
return func(o *client.CallOptions) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, grpcCallOptions{}, opts)
|
||||
}
|
||||
}
|
||||
|
@@ -43,9 +43,9 @@ type routerKey struct{}
|
||||
func (r *routerSelector) getRoutes(service string) ([]router.Route, error) {
|
||||
if !r.remote {
|
||||
// lookup router for routes for the service
|
||||
return r.r.Lookup(router.NewQuery(
|
||||
return r.r.Lookup(
|
||||
router.QueryService(service),
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
// lookup the remote router
|
||||
|
@@ -23,11 +23,12 @@ import (
|
||||
"github.com/micro/go-micro/broker/http"
|
||||
"github.com/micro/go-micro/broker/memory"
|
||||
"github.com/micro/go-micro/broker/nats"
|
||||
brokerSrv "github.com/micro/go-micro/broker/service"
|
||||
|
||||
// registries
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/registry/consul"
|
||||
"github.com/micro/go-micro/registry/gossip"
|
||||
"github.com/micro/go-micro/registry/etcd"
|
||||
"github.com/micro/go-micro/registry/mdns"
|
||||
rmem "github.com/micro/go-micro/registry/memory"
|
||||
regSrv "github.com/micro/go-micro/registry/service"
|
||||
@@ -154,7 +155,7 @@ var (
|
||||
cli.StringFlag{
|
||||
Name: "registry",
|
||||
EnvVar: "MICRO_REGISTRY",
|
||||
Usage: "Registry for discovery. consul, mdns",
|
||||
Usage: "Registry for discovery. consul, etcd, mdns",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "registry_address",
|
||||
@@ -179,9 +180,11 @@ var (
|
||||
}
|
||||
|
||||
DefaultBrokers = map[string]func(...broker.Option) broker.Broker{
|
||||
"http": http.NewBroker,
|
||||
"memory": memory.NewBroker,
|
||||
"nats": nats.NewBroker,
|
||||
"go.micro.broker": brokerSrv.NewBroker,
|
||||
"service": brokerSrv.NewBroker,
|
||||
"http": http.NewBroker,
|
||||
"memory": memory.NewBroker,
|
||||
"nats": nats.NewBroker,
|
||||
}
|
||||
|
||||
DefaultClients = map[string]func(...client.Option) client.Client{
|
||||
@@ -194,7 +197,7 @@ var (
|
||||
"go.micro.registry": regSrv.NewRegistry,
|
||||
"service": regSrv.NewRegistry,
|
||||
"consul": consul.NewRegistry,
|
||||
"gossip": gossip.NewRegistry,
|
||||
"etcd": etcd.NewRegistry,
|
||||
"mdns": mdns.NewRegistry,
|
||||
"memory": rmem.NewRegistry,
|
||||
}
|
||||
|
@@ -1,49 +0,0 @@
|
||||
# Consul Source
|
||||
|
||||
The consul source reads config from consul key/values
|
||||
|
||||
## Consul Format
|
||||
|
||||
The consul source expects keys under the default prefix `/micro/config`
|
||||
|
||||
Values are expected to be json
|
||||
|
||||
```
|
||||
// set database
|
||||
consul kv put micro/config/database '{"address": "10.0.0.1", "port": 3306}'
|
||||
// set cache
|
||||
consul kv put micro/config/cache '{"address": "10.0.0.2", "port": 6379}'
|
||||
```
|
||||
|
||||
Keys are split on `/` so access becomes
|
||||
|
||||
```
|
||||
conf.Get("micro", "config", "database")
|
||||
```
|
||||
|
||||
## New Source
|
||||
|
||||
Specify source with data
|
||||
|
||||
```go
|
||||
consulSource := consul.NewSource(
|
||||
// optionally specify consul address; default to localhost:8500
|
||||
consul.WithAddress("10.0.0.10:8500"),
|
||||
// optionally specify prefix; defaults to /micro/config
|
||||
consul.WithPrefix("/my/prefix"),
|
||||
// optionally strip the provided prefix from the keys, defaults to false
|
||||
consul.StripPrefix(true),
|
||||
)
|
||||
```
|
||||
|
||||
## Load Source
|
||||
|
||||
Load the source into config
|
||||
|
||||
```go
|
||||
// Create new config
|
||||
conf := config.NewConfig()
|
||||
|
||||
// Load consul source
|
||||
conf.Load(consulSource)
|
||||
```
|
@@ -1,126 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/micro/go-micro/config/source"
|
||||
)
|
||||
|
||||
// Currently a single consul reader
|
||||
type consul struct {
|
||||
prefix string
|
||||
stripPrefix string
|
||||
addr string
|
||||
opts source.Options
|
||||
client *api.Client
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultPrefix is the prefix that consul keys will be assumed to have if you
|
||||
// haven't specified one
|
||||
DefaultPrefix = "/micro/config/"
|
||||
)
|
||||
|
||||
func (c *consul) Read() (*source.ChangeSet, error) {
|
||||
kv, _, err := c.client.KV().List(c.prefix, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if kv == nil || len(kv) == 0 {
|
||||
return nil, fmt.Errorf("source not found: %s", c.prefix)
|
||||
}
|
||||
|
||||
data, err := makeMap(c.opts.Encoder, kv, c.stripPrefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading data: %v", err)
|
||||
}
|
||||
|
||||
b, err := c.opts.Encoder.Encode(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading source: %v", err)
|
||||
}
|
||||
|
||||
cs := &source.ChangeSet{
|
||||
Timestamp: time.Now(),
|
||||
Format: c.opts.Encoder.String(),
|
||||
Source: c.String(),
|
||||
Data: b,
|
||||
}
|
||||
cs.Checksum = cs.Sum()
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
func (c *consul) String() string {
|
||||
return "consul"
|
||||
}
|
||||
|
||||
func (c *consul) Watch() (source.Watcher, error) {
|
||||
w, err := newWatcher(c.prefix, c.addr, c.String(), c.stripPrefix, c.opts.Encoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// NewSource creates a new consul source
|
||||
func NewSource(opts ...source.Option) source.Source {
|
||||
options := source.NewOptions(opts...)
|
||||
|
||||
// use default config
|
||||
config := api.DefaultConfig()
|
||||
|
||||
// use the consul config passed in the options if any
|
||||
if co, ok := options.Context.Value(configKey{}).(*api.Config); ok {
|
||||
config = co
|
||||
}
|
||||
|
||||
// check if there are any addrs
|
||||
a, ok := options.Context.Value(addressKey{}).(string)
|
||||
if ok {
|
||||
addr, port, err := net.SplitHostPort(a)
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "8500"
|
||||
addr = a
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
} else if err == nil {
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
}
|
||||
}
|
||||
|
||||
dc, ok := options.Context.Value(dcKey{}).(string)
|
||||
if ok {
|
||||
config.Datacenter = dc
|
||||
}
|
||||
|
||||
token, ok := options.Context.Value(tokenKey{}).(string)
|
||||
if ok {
|
||||
config.Token = token
|
||||
}
|
||||
|
||||
// create the client
|
||||
client, _ := api.NewClient(config)
|
||||
|
||||
prefix := DefaultPrefix
|
||||
sp := ""
|
||||
f, ok := options.Context.Value(prefixKey{}).(string)
|
||||
if ok {
|
||||
prefix = f
|
||||
}
|
||||
|
||||
if b, ok := options.Context.Value(stripPrefixKey{}).(bool); ok && b {
|
||||
sp = prefix
|
||||
}
|
||||
|
||||
return &consul{
|
||||
prefix: prefix,
|
||||
stripPrefix: sp,
|
||||
addr: config.Address,
|
||||
opts: options,
|
||||
client: client,
|
||||
}
|
||||
}
|
@@ -1,89 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/micro/go-micro/config/encoder"
|
||||
)
|
||||
|
||||
type configValue interface {
|
||||
Value() interface{}
|
||||
Decode(encoder.Encoder, []byte) error
|
||||
}
|
||||
type configArrayValue struct {
|
||||
v []interface{}
|
||||
}
|
||||
|
||||
func (a *configArrayValue) Value() interface{} { return a.v }
|
||||
func (a *configArrayValue) Decode(e encoder.Encoder, b []byte) error {
|
||||
return e.Decode(b, &a.v)
|
||||
}
|
||||
|
||||
type configMapValue struct {
|
||||
v map[string]interface{}
|
||||
}
|
||||
|
||||
func (m *configMapValue) Value() interface{} { return m.v }
|
||||
func (m *configMapValue) Decode(e encoder.Encoder, b []byte) error {
|
||||
return e.Decode(b, &m.v)
|
||||
}
|
||||
|
||||
func makeMap(e encoder.Encoder, kv api.KVPairs, stripPrefix string) (map[string]interface{}, error) {
|
||||
|
||||
data := make(map[string]interface{})
|
||||
|
||||
// consul guarantees lexicographic order, so no need to sort
|
||||
for _, v := range kv {
|
||||
pathString := strings.TrimPrefix(strings.TrimPrefix(v.Key, strings.TrimPrefix(stripPrefix, "/")), "/")
|
||||
if pathString == "" {
|
||||
continue
|
||||
}
|
||||
var val configValue
|
||||
var err error
|
||||
|
||||
// ensure a valid value is stored at this location
|
||||
if len(v.Value) > 0 {
|
||||
// try to decode into map value or array value
|
||||
arrayV := &configArrayValue{v: []interface{}{}}
|
||||
mapV := &configMapValue{v: map[string]interface{}{}}
|
||||
switch {
|
||||
case arrayV.Decode(e, v.Value) == nil:
|
||||
val = arrayV
|
||||
case mapV.Decode(e, v.Value) == nil:
|
||||
val = mapV
|
||||
default:
|
||||
return nil, fmt.Errorf("faild decode value. path: %s, error: %s", pathString, err)
|
||||
}
|
||||
}
|
||||
|
||||
// set target at the root
|
||||
target := data
|
||||
path := strings.Split(pathString, "/")
|
||||
// find (or create) the leaf node we want to put this value at
|
||||
for _, dir := range path[:len(path)-1] {
|
||||
if _, ok := target[dir]; !ok {
|
||||
target[dir] = make(map[string]interface{})
|
||||
}
|
||||
target = target[dir].(map[string]interface{})
|
||||
}
|
||||
|
||||
leafDir := path[len(path)-1]
|
||||
|
||||
// copy over the keys from the value
|
||||
switch val.(type) {
|
||||
case *configArrayValue:
|
||||
target[leafDir] = val.Value()
|
||||
case *configMapValue:
|
||||
target[leafDir] = make(map[string]interface{})
|
||||
target = target[leafDir].(map[string]interface{})
|
||||
mapv := val.Value().(map[string]interface{})
|
||||
for k := range mapv {
|
||||
target[k] = mapv[k]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
@@ -1,95 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/api/watch"
|
||||
"github.com/micro/go-micro/config/encoder"
|
||||
"github.com/micro/go-micro/config/source"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
e encoder.Encoder
|
||||
name string
|
||||
stripPrefix string
|
||||
|
||||
wp *watch.Plan
|
||||
ch chan *source.ChangeSet
|
||||
exit chan bool
|
||||
}
|
||||
|
||||
func newWatcher(key, addr, name, stripPrefix string, e encoder.Encoder) (source.Watcher, error) {
|
||||
w := &watcher{
|
||||
e: e,
|
||||
name: name,
|
||||
stripPrefix: stripPrefix,
|
||||
ch: make(chan *source.ChangeSet),
|
||||
exit: make(chan bool),
|
||||
}
|
||||
|
||||
wp, err := watch.Parse(map[string]interface{}{"type": "keyprefix", "prefix": key})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wp.Handler = w.handle
|
||||
|
||||
// wp.Run is a blocking call and will prevent newWatcher from returning
|
||||
go wp.Run(addr)
|
||||
|
||||
w.wp = wp
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (w *watcher) handle(idx uint64, data interface{}) {
|
||||
if data == nil {
|
||||
return
|
||||
}
|
||||
|
||||
kvs, ok := data.(api.KVPairs)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
d, err := makeMap(w.e, kvs, w.stripPrefix)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b, err := w.e.Encode(d)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cs := &source.ChangeSet{
|
||||
Timestamp: time.Now(),
|
||||
Format: w.e.String(),
|
||||
Source: w.name,
|
||||
Data: b,
|
||||
}
|
||||
cs.Checksum = cs.Sum()
|
||||
|
||||
w.ch <- cs
|
||||
}
|
||||
|
||||
func (w *watcher) Next() (*source.ChangeSet, error) {
|
||||
select {
|
||||
case cs := <-w.ch:
|
||||
return cs, nil
|
||||
case <-w.exit:
|
||||
return nil, source.ErrWatcherStopped
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() error {
|
||||
select {
|
||||
case <-w.exit:
|
||||
return nil
|
||||
default:
|
||||
w.wp.Stop()
|
||||
close(w.exit)
|
||||
}
|
||||
return nil
|
||||
}
|
51
config/source/etcd/README.md
Normal file
51
config/source/etcd/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Etcd Source
|
||||
|
||||
The etcd source reads config from etcd key/values
|
||||
|
||||
This source supports etcd version 3 and beyond.
|
||||
|
||||
## Etcd Format
|
||||
|
||||
The etcd source expects keys under the default prefix `/micro/config` (prefix can be changed)
|
||||
|
||||
Values are expected to be JSON
|
||||
|
||||
```
|
||||
// set database
|
||||
etcdctl put /micro/config/database '{"address": "10.0.0.1", "port": 3306}'
|
||||
// set cache
|
||||
etcdctl put /micro/config/cache '{"address": "10.0.0.2", "port": 6379}'
|
||||
```
|
||||
|
||||
Keys are split on `/` so access becomes
|
||||
|
||||
```
|
||||
conf.Get("micro", "config", "database")
|
||||
```
|
||||
|
||||
## New Source
|
||||
|
||||
Specify source with data
|
||||
|
||||
```go
|
||||
etcdSource := etcd.NewSource(
|
||||
// optionally specify etcd address; default to localhost:8500
|
||||
etcd.WithAddress("10.0.0.10:8500"),
|
||||
// optionally specify prefix; defaults to /micro/config
|
||||
etcd.WithPrefix("/my/prefix"),
|
||||
// optionally strip the provided prefix from the keys, defaults to false
|
||||
etcd.StripPrefix(true),
|
||||
)
|
||||
```
|
||||
|
||||
## Load Source
|
||||
|
||||
Load the source into config
|
||||
|
||||
```go
|
||||
// Create new config
|
||||
conf := config.NewConfig()
|
||||
|
||||
// Load file source
|
||||
conf.Load(etcdSource)
|
||||
```
|
141
config/source/etcd/etcd.go
Normal file
141
config/source/etcd/etcd.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
cetcd "github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
"github.com/micro/go-micro/config/source"
|
||||
)
|
||||
|
||||
// Currently a single etcd reader
|
||||
type etcd struct {
|
||||
prefix string
|
||||
stripPrefix string
|
||||
opts source.Options
|
||||
client *cetcd.Client
|
||||
cerr error
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultPrefix = "/micro/config/"
|
||||
)
|
||||
|
||||
func (c *etcd) Read() (*source.ChangeSet, error) {
|
||||
if c.cerr != nil {
|
||||
return nil, c.cerr
|
||||
}
|
||||
|
||||
rsp, err := c.client.Get(context.Background(), c.prefix, cetcd.WithPrefix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rsp == nil || len(rsp.Kvs) == 0 {
|
||||
return nil, fmt.Errorf("source not found: %s", c.prefix)
|
||||
}
|
||||
|
||||
var kvs []*mvccpb.KeyValue
|
||||
for _, v := range rsp.Kvs {
|
||||
kvs = append(kvs, (*mvccpb.KeyValue)(v))
|
||||
}
|
||||
|
||||
data := makeMap(c.opts.Encoder, kvs, c.stripPrefix)
|
||||
|
||||
b, err := c.opts.Encoder.Encode(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading source: %v", err)
|
||||
}
|
||||
|
||||
cs := &source.ChangeSet{
|
||||
Timestamp: time.Now(),
|
||||
Source: c.String(),
|
||||
Data: b,
|
||||
Format: c.opts.Encoder.String(),
|
||||
}
|
||||
cs.Checksum = cs.Sum()
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
func (c *etcd) String() string {
|
||||
return "etcd"
|
||||
}
|
||||
|
||||
func (c *etcd) Watch() (source.Watcher, error) {
|
||||
if c.cerr != nil {
|
||||
return nil, c.cerr
|
||||
}
|
||||
cs, err := c.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newWatcher(c.prefix, c.stripPrefix, c.client.Watcher, cs, c.opts)
|
||||
}
|
||||
|
||||
func NewSource(opts ...source.Option) source.Source {
|
||||
options := source.NewOptions(opts...)
|
||||
|
||||
var endpoints []string
|
||||
|
||||
// check if there are any addrs
|
||||
addrs, ok := options.Context.Value(addressKey{}).([]string)
|
||||
if ok {
|
||||
for _, a := range addrs {
|
||||
addr, port, err := net.SplitHostPort(a)
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "2379"
|
||||
addr = a
|
||||
endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port))
|
||||
} else if err == nil {
|
||||
endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(endpoints) == 0 {
|
||||
endpoints = []string{"localhost:2379"}
|
||||
}
|
||||
|
||||
// check dial timeout option
|
||||
dialTimeout, ok := options.Context.Value(dialTimeoutKey{}).(time.Duration)
|
||||
if !ok {
|
||||
dialTimeout = 3 * time.Second // default dial timeout
|
||||
}
|
||||
|
||||
config := cetcd.Config{
|
||||
Endpoints: endpoints,
|
||||
DialTimeout: dialTimeout,
|
||||
}
|
||||
|
||||
u, ok := options.Context.Value(authKey{}).(*authCreds)
|
||||
if ok {
|
||||
config.Username = u.Username
|
||||
config.Password = u.Password
|
||||
}
|
||||
|
||||
// use default config
|
||||
client, err := cetcd.New(config)
|
||||
|
||||
prefix := DefaultPrefix
|
||||
sp := ""
|
||||
f, ok := options.Context.Value(prefixKey{}).(string)
|
||||
if ok {
|
||||
prefix = f
|
||||
}
|
||||
|
||||
if b, ok := options.Context.Value(stripPrefixKey{}).(bool); ok && b {
|
||||
sp = prefix
|
||||
}
|
||||
|
||||
return &etcd{
|
||||
prefix: prefix,
|
||||
stripPrefix: sp,
|
||||
opts: options,
|
||||
client: client,
|
||||
cerr: err,
|
||||
}
|
||||
}
|
@@ -1,21 +1,25 @@
|
||||
package consul
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/micro/go-micro/config/source"
|
||||
)
|
||||
|
||||
type addressKey struct{}
|
||||
type prefixKey struct{}
|
||||
type stripPrefixKey struct{}
|
||||
type dcKey struct{}
|
||||
type tokenKey struct{}
|
||||
type configKey struct{}
|
||||
type authKey struct{}
|
||||
type dialTimeoutKey struct{}
|
||||
|
||||
// WithAddress sets the consul address
|
||||
func WithAddress(a string) source.Option {
|
||||
type authCreds struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// WithAddress sets the etcd address
|
||||
func WithAddress(a ...string) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
@@ -45,31 +49,22 @@ func StripPrefix(strip bool) source.Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithDatacenter(p string) source.Option {
|
||||
// Auth allows you to specify username/password
|
||||
func Auth(username, password string) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, dcKey{}, p)
|
||||
o.Context = context.WithValue(o.Context, authKey{}, &authCreds{Username: username, Password: password})
|
||||
}
|
||||
}
|
||||
|
||||
// WithToken sets the key token to use
|
||||
func WithToken(p string) source.Option {
|
||||
// WithDialTimeout set the time out for dialing to etcd
|
||||
func WithDialTimeout(timeout time.Duration) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, tokenKey{}, p)
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfig set consul-specific options
|
||||
func WithConfig(c *api.Config) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, configKey{}, c)
|
||||
o.Context = context.WithValue(o.Context, dialTimeoutKey{}, timeout)
|
||||
}
|
||||
}
|
89
config/source/etcd/util.go
Normal file
89
config/source/etcd/util.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
"github.com/micro/go-micro/config/encoder"
|
||||
)
|
||||
|
||||
func makeEvMap(e encoder.Encoder, data map[string]interface{}, kv []*clientv3.Event, stripPrefix string) map[string]interface{} {
|
||||
if data == nil {
|
||||
data = make(map[string]interface{})
|
||||
}
|
||||
|
||||
for _, v := range kv {
|
||||
switch mvccpb.Event_EventType(v.Type) {
|
||||
case mvccpb.DELETE:
|
||||
data = update(e, data, (*mvccpb.KeyValue)(v.Kv), "delete", stripPrefix)
|
||||
default:
|
||||
data = update(e, data, (*mvccpb.KeyValue)(v.Kv), "insert", stripPrefix)
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func makeMap(e encoder.Encoder, kv []*mvccpb.KeyValue, stripPrefix string) map[string]interface{} {
|
||||
data := make(map[string]interface{})
|
||||
|
||||
for _, v := range kv {
|
||||
data = update(e, data, v, "put", stripPrefix)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func update(e encoder.Encoder, data map[string]interface{}, v *mvccpb.KeyValue, action, stripPrefix string) map[string]interface{} {
|
||||
// remove prefix if non empty, and ensure leading / is removed as well
|
||||
vkey := strings.TrimPrefix(strings.TrimPrefix(string(v.Key), stripPrefix), "/")
|
||||
// split on prefix
|
||||
haveSplit := strings.Contains(vkey, "/")
|
||||
keys := strings.Split(vkey, "/")
|
||||
|
||||
var vals interface{}
|
||||
e.Decode(v.Value, &vals)
|
||||
|
||||
if !haveSplit && len(keys) == 1 {
|
||||
switch action {
|
||||
case "delete":
|
||||
data = make(map[string]interface{})
|
||||
default:
|
||||
v, ok := vals.(map[string]interface{})
|
||||
if ok {
|
||||
data = v
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// set data for first iteration
|
||||
kvals := data
|
||||
// iterate the keys and make maps
|
||||
for i, k := range keys {
|
||||
kval, ok := kvals[k].(map[string]interface{})
|
||||
if !ok {
|
||||
// create next map
|
||||
kval = make(map[string]interface{})
|
||||
// set it
|
||||
kvals[k] = kval
|
||||
}
|
||||
|
||||
// last key: write vals
|
||||
if l := len(keys) - 1; i == l {
|
||||
switch action {
|
||||
case "delete":
|
||||
delete(kvals, k)
|
||||
default:
|
||||
kvals[k] = vals
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// set kvals for next iterator
|
||||
kvals = kval
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
113
config/source/etcd/watcher.go
Normal file
113
config/source/etcd/watcher.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
cetcd "github.com/coreos/etcd/clientv3"
|
||||
"github.com/micro/go-micro/config/source"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
opts source.Options
|
||||
name string
|
||||
stripPrefix string
|
||||
|
||||
sync.RWMutex
|
||||
cs *source.ChangeSet
|
||||
|
||||
ch chan *source.ChangeSet
|
||||
exit chan bool
|
||||
}
|
||||
|
||||
func newWatcher(key, strip string, wc cetcd.Watcher, cs *source.ChangeSet, opts source.Options) (source.Watcher, error) {
|
||||
w := &watcher{
|
||||
opts: opts,
|
||||
name: "etcd",
|
||||
stripPrefix: strip,
|
||||
cs: cs,
|
||||
ch: make(chan *source.ChangeSet),
|
||||
exit: make(chan bool),
|
||||
}
|
||||
|
||||
ch := wc.Watch(context.Background(), key, cetcd.WithPrefix())
|
||||
|
||||
go w.run(wc, ch)
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (w *watcher) handle(evs []*cetcd.Event) {
|
||||
w.RLock()
|
||||
data := w.cs.Data
|
||||
w.RUnlock()
|
||||
|
||||
var vals map[string]interface{}
|
||||
|
||||
// unpackage existing changeset
|
||||
if err := w.opts.Encoder.Decode(data, &vals); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// update base changeset
|
||||
d := makeEvMap(w.opts.Encoder, vals, evs, w.stripPrefix)
|
||||
|
||||
// pack the changeset
|
||||
b, err := w.opts.Encoder.Encode(d)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// create new changeset
|
||||
cs := &source.ChangeSet{
|
||||
Timestamp: time.Now(),
|
||||
Source: w.name,
|
||||
Data: b,
|
||||
Format: w.opts.Encoder.String(),
|
||||
}
|
||||
cs.Checksum = cs.Sum()
|
||||
|
||||
// set base change set
|
||||
w.Lock()
|
||||
w.cs = cs
|
||||
w.Unlock()
|
||||
|
||||
// send update
|
||||
w.ch <- cs
|
||||
}
|
||||
|
||||
func (w *watcher) run(wc cetcd.Watcher, ch cetcd.WatchChan) {
|
||||
for {
|
||||
select {
|
||||
case rsp, ok := <-ch:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
w.handle(rsp.Events)
|
||||
case <-w.exit:
|
||||
wc.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Next() (*source.ChangeSet, error) {
|
||||
select {
|
||||
case cs := <-w.ch:
|
||||
return cs, nil
|
||||
case <-w.exit:
|
||||
return nil, errors.New("watcher stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() error {
|
||||
select {
|
||||
case <-w.exit:
|
||||
return nil
|
||||
default:
|
||||
close(w.exit)
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -1,2 +0,0 @@
|
||||
// Package data is an interface for data access
|
||||
package data
|
@@ -1,96 +0,0 @@
|
||||
// Package consul is a consul implementation of kv
|
||||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/micro/go-micro/config/options"
|
||||
"github.com/micro/go-micro/data/store"
|
||||
)
|
||||
|
||||
type ckv struct {
|
||||
options.Options
|
||||
client *api.Client
|
||||
}
|
||||
|
||||
func (c *ckv) Read(key string) (*store.Record, error) {
|
||||
keyval, _, err := c.client.KV().Get(key, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if keyval == nil {
|
||||
return nil, store.ErrNotFound
|
||||
}
|
||||
|
||||
return &store.Record{
|
||||
Key: keyval.Key,
|
||||
Value: keyval.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *ckv) Delete(key string) error {
|
||||
_, err := c.client.KV().Delete(key, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ckv) Write(record *store.Record) error {
|
||||
_, err := c.client.KV().Put(&api.KVPair{
|
||||
Key: record.Key,
|
||||
Value: record.Value,
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ckv) Dump() ([]*store.Record, error) {
|
||||
keyval, _, err := c.client.KV().List("/", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if keyval == nil {
|
||||
return nil, store.ErrNotFound
|
||||
}
|
||||
var vals []*store.Record
|
||||
for _, keyv := range keyval {
|
||||
vals = append(vals, &store.Record{
|
||||
Key: keyv.Key,
|
||||
Value: keyv.Value,
|
||||
})
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
func (c *ckv) String() string {
|
||||
return "consul"
|
||||
}
|
||||
|
||||
func NewStore(opts ...options.Option) store.Store {
|
||||
options := options.NewOptions(opts...)
|
||||
config := api.DefaultConfig()
|
||||
|
||||
var nodes []string
|
||||
|
||||
if n, ok := options.Values().Get("store.nodes"); ok {
|
||||
nodes = n.([]string)
|
||||
}
|
||||
|
||||
// set host
|
||||
if len(nodes) > 0 {
|
||||
addr, port, err := net.SplitHostPort(nodes[0])
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "8500"
|
||||
config.Address = fmt.Sprintf("%s:%s", nodes[0], port)
|
||||
} else if err == nil {
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
}
|
||||
}
|
||||
|
||||
client, _ := api.NewClient(config)
|
||||
|
||||
return &ckv{
|
||||
Options: options,
|
||||
client: client,
|
||||
}
|
||||
}
|
81
go.mod
81
go.mod
@@ -1,78 +1,63 @@
|
||||
module github.com/micro/go-micro
|
||||
|
||||
go 1.12
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.44.0 // indirect
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/beevik/ntp v0.2.0
|
||||
github.com/bitly/go-simplejson v0.5.0
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
github.com/bwmarrin/discordgo v0.19.0
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.10.4
|
||||
github.com/coreos/bbolt v1.3.3 // indirect
|
||||
github.com/coreos/etcd v3.3.17+incompatible
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/fsouza/go-dockerclient v1.4.2
|
||||
github.com/fsouza/go-dockerclient v1.4.4
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/go-kit/kit v0.9.0 // indirect
|
||||
github.com/go-acme/lego/v3 v3.1.0
|
||||
github.com/go-log/log v0.1.0
|
||||
github.com/go-playground/locales v0.12.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.16.0 // indirect
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
||||
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc // indirect
|
||||
github.com/golang/protobuf v1.3.2
|
||||
github.com/google/go-cmp v0.3.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70 // indirect
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/handlers v1.4.2
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
github.com/hashicorp/consul/api v1.1.0
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0 // indirect
|
||||
github.com/hashicorp/go-msgpack v0.5.5 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.1 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||
github.com/hashicorp/go-version v1.2.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.3 // indirect
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/hashicorp/consul/api v1.2.0
|
||||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/hashicorp/mdns v1.0.1 // indirect
|
||||
github.com/hashicorp/memberlist v0.1.4
|
||||
github.com/hashicorp/serf v0.8.3 // indirect
|
||||
github.com/imdario/mergo v0.3.7
|
||||
github.com/imdario/mergo v0.3.8
|
||||
github.com/jonboulle/clockwork v0.1.0 // indirect
|
||||
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1
|
||||
github.com/json-iterator/go v1.1.7
|
||||
github.com/kisielk/errcheck v1.2.0 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/leodido/go-urn v1.1.0 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.12.0
|
||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.12.1
|
||||
github.com/mholt/certmagic v0.7.5
|
||||
github.com/micro/cli v0.2.0
|
||||
github.com/micro/mdns v0.3.0
|
||||
github.com/miekg/dns v1.1.15 // indirect
|
||||
github.com/mitchellh/gox v1.0.1 // indirect
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||
github.com/nats-io/nats-server/v2 v2.1.0 // indirect
|
||||
github.com/nats-io/nats.go v1.8.1
|
||||
github.com/nats-io/nkeys v0.1.0 // indirect
|
||||
github.com/nlopes/slack v0.5.0
|
||||
github.com/olekukonko/tablewriter v0.0.1
|
||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/nlopes/slack v0.6.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/posener/complete v1.2.1 // indirect
|
||||
github.com/prometheus/client_golang v1.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2 // indirect
|
||||
github.com/soheilhy/cmux v0.1.4 // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||
golang.org/x/mobile v0.0.0-20190806162312-597adff16ade // indirect
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
|
||||
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e // indirect
|
||||
golang.org/x/tools v0.0.0-20190809145639-6d4652c779c4 // indirect
|
||||
google.golang.org/grpc v1.22.1
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.etcd.io/bbolt v1.3.3 // indirect
|
||||
go.uber.org/multierr v1.2.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||
golang.org/x/net v0.0.0-20191011234655-491137f69257
|
||||
google.golang.org/grpc v1.24.0
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||
gopkg.in/go-playground/validator.v9 v9.30.0
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4
|
||||
honnef.co/go/tools v0.0.1-2019.2.2 // indirect
|
||||
sigs.k8s.io/yaml v1.1.0 // indirect
|
||||
)
|
||||
|
471
go.sum
471
go.sum
@@ -1,87 +1,130 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
|
||||
cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
|
||||
cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
|
||||
cloud.google.com/go v0.44.0/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
|
||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
|
||||
github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.13/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
|
||||
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY=
|
||||
github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.10.2 h1:VBodKICVPnwmDxstcW3biKcDSpFIfS/RELUXsZSBYK4=
|
||||
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
|
||||
github.com/cloudflare/cloudflare-go v0.10.4 h1:7C1D9mtcNFZLCqmhkHK2BlwKKm9fi4cBqY6qpYtQv5E=
|
||||
github.com/cloudflare/cloudflare-go v0.10.4/go.mod h1:4HgmUutVbZTRnHg91bS8lvlA0Wx+TgqttLDcwey2S6E=
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M=
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.17+incompatible h1:f/Z3EoDSx1yjaIjLQGo1diYUlQYSBrrAQ5vP8NjwXwo=
|
||||
github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 h1:dmUn0SuGx7unKFwxyeQ/oLUHhEfZosEDrpmYM+6MTuc=
|
||||
github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b h1:+Ga+YpCDpcY1fln6GI0fiiirpqHGcob5/Vk3oKNuGdU=
|
||||
github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
|
||||
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c h1:pBgVXWDXju1m8W4lnEeIqTHPOzhTUO81a7yknM/xQR4=
|
||||
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsouza/go-dockerclient v1.4.1 h1:W7wuJ3IB48WYZv/UBk9dCTIb9oX805+L9KIm65HcUYs=
|
||||
github.com/fsouza/go-dockerclient v1.4.1/go.mod h1:PUNHxbowDqRXfRgZqMz1OeGtbWC6VKyZvJ99hDjB0qs=
|
||||
github.com/fsouza/go-dockerclient v1.4.2 h1:dl6GfIWS5Qn4C6OfSnnoe6YuOV8lvKAE8W/YD1Q7udo=
|
||||
github.com/fsouza/go-dockerclient v1.4.2/go.mod h1:COunfLZrsdwX/j3YVDAG8gIw3KutrI0x1+vGEJ5zxdI=
|
||||
github.com/fsouza/go-dockerclient v1.4.4 h1:Sd5nD4wdAgiPxvrbYUzT2ZZNmPk3z+GGnZ+frvw8z04=
|
||||
github.com/fsouza/go-dockerclient v1.4.4/go.mod h1:PrwszSL5fbmsESocROrOGq/NULMXRw+bajY0ltzD6MA=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-acme/lego/v3 v3.1.0 h1:yanYFoYW8azFkCvJfIk7edWWfjkYkhDxe45ZsxoW4Xk=
|
||||
github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE=
|
||||
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-log/log v0.1.0 h1:wudGTNsiGzrD5ZjgIkVZ517ugi2XRe9Q/xRCzwEO4/U=
|
||||
github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
@@ -91,83 +134,83 @@ github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3yg
|
||||
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc h1:55rEp52jU6bkyslZ1+C/7NGfpQsEc6pxGLAGDOctqbw=
|
||||
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA=
|
||||
github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/handlers v1.4.1 h1:BHvcRGJe/TrL+OqFxoKQGddTgeibiOjaBssV5a/N9sw=
|
||||
github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/consul/api v1.2.0 h1:oPsuzLp2uk7I7rojPKuncWbZ+m5TMoD4Ivs+2Rkeh4Y=
|
||||
github.com/hashicorp/consul/api v1.2.0/go.mod h1:1SIkFYi2ZTXUE5Kgt179+4hH33djo11+0Eo2XgTAtkw=
|
||||
github.com/hashicorp/consul/sdk v0.2.0 h1:GWFYFmry/k4b1hEoy7kSkmU8e30GAyI4VZHk0fRxeL4=
|
||||
github.com/hashicorp/consul/sdk v0.2.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
@@ -175,97 +218,85 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/memberlist v0.1.4 h1:gkyML/r71w3FL8gUi74Vk76avkj/9lYAY9lvg0OcoGs=
|
||||
github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.8.3 h1:MWYcmct5EtKz0efYooPcL0yNkem+7kWxqXDi/UIh+8k=
|
||||
github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
|
||||
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:anPrsicrIi2ColgWTVPk+TrN42hJIWlfPHSBP9S0ZkM=
|
||||
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84=
|
||||
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1 h1:lnrOS18wZBYrzdDmnUeg1OVk+kQ3rxG8mZWU89DpMIA=
|
||||
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1/go.mod h1:DFXrEwSRX0p/aSvxE21319menCBFeQO0jXpRj7LEZUA=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c h1:VAx3LRNjVNvjtgO7KFRuT/3aye/0zJvwn01rHSfoolo=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=
|
||||
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
|
||||
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190710050138-1441923ab031 h1:wjcGvgllMOQw8wNYFH6acq/KlTAdjKMSo1EUYybHXto=
|
||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190710050138-1441923ab031/go.mod h1:lb5aAxL68VvhZ00e7yYuQVK/9FLggtYy4qo7oI5qzqA=
|
||||
github.com/lucas-clemente/quic-go v0.11.2 h1:Mop0ac3zALaBR3wGs6j8OYe/tcFvFsxTUFMkE/7yUOI=
|
||||
github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||
github.com/lucas-clemente/quic-go v0.12.0 h1:dYHUyB50gEQlK3KqytmNySzuyzAcaQ3iuI2ZReAfVrE=
|
||||
github.com/lucas-clemente/quic-go v0.12.0/go.mod h1:UXJJPE4RfFef/xPO5wQm0tITK8gNfqwTxjbE7s3Vb8s=
|
||||
github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA=
|
||||
github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ=
|
||||
github.com/lucas-clemente/quic-go v0.12.1 h1:BPITli+6KnKogtTxBk2aS4okr5dUHz2LtIDAP1b8UL4=
|
||||
github.com/lucas-clemente/quic-go v0.12.1/go.mod h1:UXJJPE4RfFef/xPO5wQm0tITK8gNfqwTxjbE7s3Vb8s=
|
||||
github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
|
||||
github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
|
||||
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/marten-seemann/qtls v0.2.4 h1:mCJ6i1jAqcsm9XODrSGvXECodoAb1STta+TkxJCwCnE=
|
||||
github.com/marten-seemann/qtls v0.2.4/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/marten-seemann/qtls v0.3.1 h1:ySYIvhFjFY2JsNHY6VACvomMEDy3EvdPA6yciUFAiHw=
|
||||
github.com/marten-seemann/qtls v0.3.1/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/marten-seemann/qtls v0.3.2 h1:O7awy4bHEzSX/K3h+fZig3/Vo03s/RxlxgsAk9sYamI=
|
||||
github.com/marten-seemann/qtls v0.3.2/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mholt/certmagic v0.7.5 h1:1ZGHwUI4+zg1S17tPUj5Xxb9Q1ghTjLcUZE5G4yV5SM=
|
||||
github.com/mholt/certmagic v0.7.5/go.mod h1:91uJzK5K8IWtYQqTi5R2tsxV1pCde+wdGfaRaOZi6aQ=
|
||||
github.com/micro/cli v0.2.0 h1:ut3rV5JWqZjsXIa2MvGF+qMUP8DAUTvHX9Br5gO4afA=
|
||||
github.com/micro/cli v0.2.0/go.mod h1:jRT9gmfVKWSS6pkKcXQ8YhUyj6bzwxK8Fp5b0Y7qNnk=
|
||||
github.com/micro/mdns v0.1.0 h1:fuLybUsfynbigJmCot/54i+gwe0hpc/vtCMvWt2WfDI=
|
||||
github.com/micro/mdns v0.1.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
|
||||
github.com/micro/mdns v0.1.1-0.20190729112526-ef68c9635478 h1:L6jnZZ763dMLlvst8P0dWHa1WbUu7ppUY1q3AY2hhIU=
|
||||
github.com/micro/mdns v0.1.1-0.20190729112526-ef68c9635478/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
|
||||
github.com/micro/mdns v0.2.0 h1:/+/n2PSiJURrXsBIGtfCz0hex/XYKqVsn51GAGdFrOE=
|
||||
github.com/micro/mdns v0.2.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
|
||||
github.com/micro/mdns v0.3.0 h1:bYycYe+98AXR3s8Nq5qvt6C573uFTDPIYzJemWON0QE=
|
||||
github.com/micro/mdns v0.3.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM=
|
||||
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.14 h1:wkQWn9wIp4mZbwW8XV6Km6owkvRPbOiV004ZM2CkGvA=
|
||||
github.com/miekg/dns v1.1.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI=
|
||||
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
||||
github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
|
||||
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
@@ -279,7 +310,11 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||
github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/nats-server/v2 v2.1.0 h1:Yi0+ZhRPtPAGeIxFn5erIeJIV9wXA+JznfSxK621Fbk=
|
||||
github.com/nats-io/nats-server/v2 v2.1.0/go.mod h1:r5y0WgCag0dTj/qiHkHrXAcKQ/f5GMOZaEGdoxxnJ4I=
|
||||
github.com/nats-io/nats.go v1.8.1 h1:6lF/f1/NN6kzUDBz6pyvQDEXO39jqXcWRLu/tKjtOUQ=
|
||||
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
|
||||
github.com/nats-io/nkeys v0.0.2 h1:+qM7QpgXnvDDixitZtQUBDY9w/s9mu1ghS+JIbsrx6M=
|
||||
@@ -288,57 +323,77 @@ github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4=
|
||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nlopes/slack v0.5.0 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0=
|
||||
github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
|
||||
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/nlopes/slack v0.6.0 h1:jt0jxVQGhssx1Ib7naAOZEZcGdtIhTzkP0nopK0AsRA=
|
||||
github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk=
|
||||
github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw=
|
||||
github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ=
|
||||
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -346,76 +401,82 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
|
||||
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
|
||||
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY=
|
||||
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4=
|
||||
go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 h1:IcSOAf4PyMp3U3XbIEj1/xJ2BjNN2jWv7JoyOsMxXUU=
|
||||
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190618124811-92942e4437e2/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20190806162312-597adff16ade/go.mod h1:AlhUtkH4DA4asiFC5RgK7ZKmauvtkAVcy9L0epCzlWo=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190607181551-461777fb6f67 h1:rJJxsykSlULwd2P2+pg/rtnwN2FrWp4IuCxOSyS0V00=
|
||||
golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191011234655-491137f69257 h1:ry8e2D+cwaV6hk7lb3aRTjjZo24shrbK0e11QEOkTIg=
|
||||
golang.org/x/net v0.0.0-20191011234655-491137f69257/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -423,138 +484,110 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190621062556-bf70e4678053 h1:T0MJjz97TtCXa3ZNW2Oenb3KQWB91K965zMEbIJ4ThA=
|
||||
golang.org/x/sys v0.0.0-20190621062556-bf70e4678053/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e h1:TsjK5I7fXk8f2FQrgu6NS7i5Qih3knl2FL1htyguLRE=
|
||||
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w=
|
||||
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190530171427-2b03ca6e44eb/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190620191750-1fa568393b23/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190710184609-286818132824/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190807201305-8be58fba6352/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190809145639-6d4652c779c4/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 h1:9VBRTdmgQxbs6HE0sUnMrSWNePppAJU07NYvX5dIB04=
|
||||
google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 h1:5pOB7se0B2+IssELuQUs6uoBgYJenkU2AQlvopc2sRw=
|
||||
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
|
||||
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
|
||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
|
||||
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.0 h1:5ofssLNYgAA/inWn6rTZ4juWpRJUwEnXc1LG2IeXwgQ=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo=
|
||||
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.1 h1:OkK1DmefDy1Z6Veu82wdNj/cLpYORhdX4qdaYCPwc7s=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v9 v9.30.0 h1:Wk0Z37oBmKj9/n+tPyBHZmeL19LaCoK3Qq48VwYENss=
|
||||
gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw=
|
||||
gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.11.0 h1:cJwWgJ0DXifrNrXM6RGN1Y2yR60Rr1zQ9Q5DX5S9qgU=
|
||||
gopkg.in/src-d/go-git.v4 v4.11.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0 h1:CKgvBCJCcdfNnyXPYI4Cp8PaDDAmAPEN0CtfEdEAbd8=
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0/go.mod h1:zjlNnzc1Wjn43v3Mtii7RVxiReNP0fIu9npcXKzuNp4=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4 h1:hpHWhzn4jTCsAJZZ2loNKfy2QWyPDRJVl3aTFXeMW8g=
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b/go.mod h1:JlmFZigtG9vBVR3QGIQ9g/Usz4BzH+Xm6Z8iHQWRYUw=
|
||||
honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
8
micro.go
8
micro.go
@@ -14,11 +14,19 @@ type serviceKey struct{}
|
||||
// within go-micro. Its a convenience method for building
|
||||
// and initialising services.
|
||||
type Service interface {
|
||||
// The service name
|
||||
Name() string
|
||||
// Init initialises options
|
||||
Init(...Option)
|
||||
// Options returns the current options
|
||||
Options() Options
|
||||
// Client is used to call services
|
||||
Client() client.Client
|
||||
// Server is for handling requests and events
|
||||
Server() server.Server
|
||||
// Run the service
|
||||
Run() error
|
||||
// The service implementation
|
||||
String() string
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/micro/go-micro/client"
|
||||
rtr "github.com/micro/go-micro/client/selector/router"
|
||||
pbNet "github.com/micro/go-micro/network/proto"
|
||||
"github.com/micro/go-micro/network/resolver/dns"
|
||||
"github.com/micro/go-micro/proxy"
|
||||
"github.com/micro/go-micro/router"
|
||||
pbRtr "github.com/micro/go-micro/router/proto"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/tunnel"
|
||||
tun "github.com/micro/go-micro/tunnel/transport"
|
||||
"github.com/micro/go-micro/util/backoff"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
@@ -28,6 +30,8 @@ var (
|
||||
ControlChannel = "control"
|
||||
// DefaultLink is default network link
|
||||
DefaultLink = "network"
|
||||
// MaxConnections is the max number of network client connections
|
||||
MaxConnections = 3
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -73,7 +77,6 @@ func newNetwork(opts ...Option) Network {
|
||||
// init tunnel address to the network bind address
|
||||
options.Tunnel.Init(
|
||||
tunnel.Address(options.Address),
|
||||
tunnel.Nodes(options.Peers...),
|
||||
)
|
||||
|
||||
// init router Id to the network id
|
||||
@@ -86,17 +89,28 @@ func newNetwork(opts ...Option) Network {
|
||||
tun.WithTunnel(options.Tunnel),
|
||||
)
|
||||
|
||||
// set the address to a hashed address
|
||||
hasher := fnv.New64()
|
||||
hasher.Write([]byte(options.Address + options.Id))
|
||||
address := fmt.Sprintf("%d", hasher.Sum64())
|
||||
|
||||
// set the address to advertise
|
||||
address := options.Address
|
||||
var advertise string
|
||||
var peerAddress string
|
||||
|
||||
if len(options.Advertise) > 0 {
|
||||
address = options.Advertise
|
||||
advertise = options.Advertise
|
||||
peerAddress = options.Advertise
|
||||
} else {
|
||||
advertise = options.Address
|
||||
peerAddress = address
|
||||
}
|
||||
|
||||
// server is network server
|
||||
server := server.NewServer(
|
||||
server.Id(options.Id),
|
||||
server.Address(options.Id),
|
||||
server.Advertise(address),
|
||||
server.Address(peerAddress),
|
||||
server.Advertise(advertise),
|
||||
server.Name(options.Name),
|
||||
server.Transport(tunTransport),
|
||||
)
|
||||
@@ -114,7 +128,7 @@ func newNetwork(opts ...Option) Network {
|
||||
network := &network{
|
||||
node: &node{
|
||||
id: options.Id,
|
||||
address: address,
|
||||
address: peerAddress,
|
||||
peers: make(map[string]*node),
|
||||
},
|
||||
options: options,
|
||||
@@ -131,6 +145,18 @@ func newNetwork(opts ...Option) Network {
|
||||
return network
|
||||
}
|
||||
|
||||
func (n *network) Init(opts ...Option) error {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
// TODO: maybe only allow reinit of certain opts
|
||||
for _, o := range opts {
|
||||
o(&n.options)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Options returns network options
|
||||
func (n *network) Options() Options {
|
||||
n.RLock()
|
||||
@@ -150,27 +176,50 @@ func (n *network) Name() string {
|
||||
func (n *network) resolveNodes() ([]string, error) {
|
||||
// resolve the network address to network nodes
|
||||
records, err := n.options.Resolver.Resolve(n.options.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeMap := make(map[string]bool)
|
||||
|
||||
// collect network node addresses
|
||||
var nodes []string
|
||||
for _, record := range records {
|
||||
nodes = append(nodes, record.Address)
|
||||
nodeMap[record.Address] = true
|
||||
}
|
||||
var i int
|
||||
|
||||
// append seed nodes if we have them
|
||||
for _, node := range n.options.Peers {
|
||||
if _, ok := nodeMap[node]; !ok {
|
||||
nodes = append(nodes, node)
|
||||
for _, record := range records {
|
||||
if _, ok := nodeMap[record.Address]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
nodeMap[record.Address] = true
|
||||
nodes = append(nodes, record.Address)
|
||||
|
||||
i++
|
||||
|
||||
// break once MaxConnection nodes has been reached
|
||||
if i == MaxConnections {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
// use the dns resolver to expand peers
|
||||
dns := &dns.Resolver{}
|
||||
|
||||
// append seed nodes if we have them
|
||||
for _, node := range n.options.Peers {
|
||||
// resolve anything that looks like a host name
|
||||
records, err := dns.Resolve(node)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to resolve %v %v", node, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// add to the node map
|
||||
for _, record := range records {
|
||||
if _, ok := nodeMap[record.Address]; !ok {
|
||||
nodes = append(nodes, record.Address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nodes, err
|
||||
}
|
||||
|
||||
// resolve continuously resolves network nodes and initializes network tunnel with resolved addresses
|
||||
@@ -202,6 +251,9 @@ func (n *network) handleNetConn(sess tunnel.Session, msg chan *transport.Message
|
||||
m := new(transport.Message)
|
||||
if err := sess.Recv(m); err != nil {
|
||||
log.Debugf("Network tunnel [%s] receive error: %v", NetworkChannel, err)
|
||||
if sessErr := sess.Close(); sessErr != nil {
|
||||
log.Debugf("Network tunnel [%s] closing connection error: %v", sessErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -215,17 +267,26 @@ func (n *network) handleNetConn(sess tunnel.Session, msg chan *transport.Message
|
||||
|
||||
// acceptNetConn accepts connections from NetworkChannel
|
||||
func (n *network) acceptNetConn(l tunnel.Listener, recv chan *transport.Message) {
|
||||
var i int
|
||||
for {
|
||||
// accept a connection
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
// TODO: handle this
|
||||
log.Debugf("Network tunnel [%s] accept error: %v", NetworkChannel, err)
|
||||
return
|
||||
sleep := backoff.Do(i)
|
||||
log.Debugf("Network tunnel [%s] accept error: %v, backing off for %v", ControlChannel, err, sleep)
|
||||
time.Sleep(sleep)
|
||||
if i > 5 {
|
||||
i = 0
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case <-n.closed:
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Debugf("Network tunnel [%s] failed to close connection: %v", NetworkChannel, err)
|
||||
}
|
||||
return
|
||||
default:
|
||||
// go handle NetworkChannel connection
|
||||
@@ -235,7 +296,7 @@ func (n *network) acceptNetConn(l tunnel.Listener, recv chan *transport.Message)
|
||||
}
|
||||
|
||||
// processNetChan processes messages received on NetworkChannel
|
||||
func (n *network) processNetChan(client transport.Client, listener tunnel.Listener) {
|
||||
func (n *network) processNetChan(listener tunnel.Listener) {
|
||||
// receive network message queue
|
||||
recv := make(chan *transport.Message, 128)
|
||||
|
||||
@@ -296,7 +357,7 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen
|
||||
if pbNetPeer.Node.Id == n.options.Id {
|
||||
continue
|
||||
}
|
||||
log.Debugf("Network received peer message from: %s", pbNetPeer.Node.Id)
|
||||
log.Debugf("Network received peer message from: %s %s", pbNetPeer.Node.Id, pbNetPeer.Node.Address)
|
||||
peer := &node{
|
||||
id: pbNetPeer.Node.Id,
|
||||
address: pbNetPeer.Node.Address,
|
||||
@@ -410,8 +471,8 @@ func (n *network) announce(client transport.Client) {
|
||||
}
|
||||
|
||||
// pruneRoutes prunes routes return by given query
|
||||
func (n *network) pruneRoutes(q router.Query) error {
|
||||
routes, err := n.router.Table().Query(q)
|
||||
func (n *network) pruneRoutes(q ...router.QueryOption) error {
|
||||
routes, err := n.router.Table().Query(q...)
|
||||
if err != nil && err != router.ErrRouteNotFound {
|
||||
return err
|
||||
}
|
||||
@@ -428,18 +489,18 @@ func (n *network) pruneRoutes(q router.Query) error {
|
||||
// pruneNodeRoutes prunes routes that were either originated by or routable via given node
|
||||
func (n *network) prunePeerRoutes(peer *node) error {
|
||||
// lookup all routes originated by router
|
||||
q := router.NewQuery(
|
||||
q := []router.QueryOption{
|
||||
router.QueryRouter(peer.id),
|
||||
)
|
||||
if err := n.pruneRoutes(q); err != nil {
|
||||
}
|
||||
if err := n.pruneRoutes(q...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// lookup all routes routable via gw
|
||||
q = router.NewQuery(
|
||||
router.QueryGateway(peer.id),
|
||||
)
|
||||
if err := n.pruneRoutes(q); err != nil {
|
||||
q = []router.QueryOption{
|
||||
router.QueryGateway(peer.address),
|
||||
}
|
||||
if err := n.pruneRoutes(q...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -464,6 +525,25 @@ func (n *network) prune() {
|
||||
log.Debugf("Network failed pruning peer %s routes: %v", id, err)
|
||||
}
|
||||
}
|
||||
// get a list of all routes
|
||||
routes, err := n.options.Router.Table().List()
|
||||
if err != nil {
|
||||
log.Debugf("Network failed listing routes: %v", err)
|
||||
continue
|
||||
}
|
||||
// collect all the router IDs in the routing table
|
||||
routers := make(map[string]bool)
|
||||
for _, route := range routes {
|
||||
if _, ok := routers[route.Router]; !ok {
|
||||
routers[route.Router] = true
|
||||
// if the router is NOT in our peer graph, delete all routes originated by it
|
||||
if peerNode := n.node.GetPeerNode(route.Router); peerNode == nil {
|
||||
if err := n.pruneRoutes(router.QueryRouter(route.Router)); err != nil {
|
||||
log.Debugf("Network failed deleting routes by %s: %v", route.Router, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -473,7 +553,6 @@ func (n *network) handleCtrlConn(sess tunnel.Session, msg chan *transport.Messag
|
||||
for {
|
||||
m := new(transport.Message)
|
||||
if err := sess.Recv(m); err != nil {
|
||||
// TODO: should we bail here?
|
||||
log.Debugf("Network tunnel advert receive error: %v", err)
|
||||
return
|
||||
}
|
||||
@@ -488,17 +567,27 @@ func (n *network) handleCtrlConn(sess tunnel.Session, msg chan *transport.Messag
|
||||
|
||||
// acceptCtrlConn accepts connections from ControlChannel
|
||||
func (n *network) acceptCtrlConn(l tunnel.Listener, recv chan *transport.Message) {
|
||||
var i int
|
||||
for {
|
||||
// accept a connection
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
// TODO: handle this
|
||||
log.Debugf("Network tunnel [%s] accept error: %v", ControlChannel, err)
|
||||
return
|
||||
sleep := backoff.Do(i)
|
||||
log.Debugf("Network tunnel [%s] accept error: %v, backing off for %v", ControlChannel, err, sleep)
|
||||
time.Sleep(sleep)
|
||||
if i > 5 {
|
||||
// reset the counter
|
||||
i = 0
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case <-n.closed:
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Debugf("Network tunnel [%s] failed to close connection: %v", ControlChannel, err)
|
||||
}
|
||||
return
|
||||
default:
|
||||
// go handle ControlChannel connection
|
||||
@@ -540,7 +629,7 @@ func (n *network) setRouteMetric(route *router.Route) {
|
||||
}
|
||||
|
||||
// processCtrlChan processes messages received on ControlChannel
|
||||
func (n *network) processCtrlChan(client transport.Client, listener tunnel.Listener) {
|
||||
func (n *network) processCtrlChan(listener tunnel.Listener) {
|
||||
// receive control message queue
|
||||
recv := make(chan *transport.Message, 128)
|
||||
|
||||
@@ -650,7 +739,7 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste
|
||||
}
|
||||
|
||||
// advertise advertises routes to the network
|
||||
func (n *network) advertise(client transport.Client, advertChan <-chan *router.Advert) {
|
||||
func (n *network) advertise(advertChan <-chan *router.Advert) {
|
||||
hasher := fnv.New64()
|
||||
for {
|
||||
select {
|
||||
@@ -675,7 +764,7 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A
|
||||
route := &pbRtr.Route{
|
||||
Service: event.Route.Service,
|
||||
Address: address,
|
||||
Gateway: n.node.id,
|
||||
Gateway: n.node.Address(),
|
||||
Network: event.Route.Network,
|
||||
Router: event.Route.Router,
|
||||
Link: DefaultLink,
|
||||
@@ -704,14 +793,25 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A
|
||||
}
|
||||
}
|
||||
|
||||
func (n *network) sendConnect() {
|
||||
// send connect message to NetworkChannel
|
||||
// NOTE: in theory we could do this as soon as
|
||||
// Dial to NetworkChannel succeeds, but instead
|
||||
// we initialize all other node resources first
|
||||
msg := &pbNet.Connect{
|
||||
Node: &pbNet.Node{
|
||||
Id: n.node.id,
|
||||
Address: n.node.address,
|
||||
},
|
||||
}
|
||||
if err := n.sendMsg("connect", msg, NetworkChannel); err != nil {
|
||||
log.Debugf("Network failed to send connect message: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Connect connects the network
|
||||
func (n *network) Connect() error {
|
||||
n.Lock()
|
||||
// return if already connected
|
||||
if n.connected {
|
||||
n.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// try to resolve network nodes
|
||||
nodes, err := n.resolveNodes()
|
||||
@@ -719,26 +819,34 @@ func (n *network) Connect() error {
|
||||
log.Debugf("Network failed to resolve nodes: %v", err)
|
||||
}
|
||||
|
||||
// initialize the tunnel to resolved nodes
|
||||
n.tunnel.Init(
|
||||
tunnel.Nodes(nodes...),
|
||||
)
|
||||
|
||||
// connect network tunnel
|
||||
if err := n.tunnel.Connect(); err != nil {
|
||||
n.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// return if already connected
|
||||
if n.connected {
|
||||
// unlock first
|
||||
n.Unlock()
|
||||
// send the connect message
|
||||
n.sendConnect()
|
||||
return nil
|
||||
}
|
||||
|
||||
// set our internal node address
|
||||
// if advertise address is not set
|
||||
if len(n.options.Advertise) == 0 {
|
||||
n.node.address = n.tunnel.Address()
|
||||
n.server.Init(server.Advertise(n.tunnel.Address()))
|
||||
}
|
||||
|
||||
// initialize the tunnel to resolved nodes
|
||||
n.tunnel.Init(
|
||||
tunnel.Nodes(nodes...),
|
||||
)
|
||||
|
||||
// dial into ControlChannel to send route adverts
|
||||
ctrlClient, err := n.tunnel.Dial(ControlChannel, tunnel.DialMulticast())
|
||||
ctrlClient, err := n.tunnel.Dial(ControlChannel, tunnel.DialMode(tunnel.Multicast))
|
||||
if err != nil {
|
||||
n.Unlock()
|
||||
return err
|
||||
@@ -747,14 +855,14 @@ func (n *network) Connect() error {
|
||||
n.tunClient[ControlChannel] = ctrlClient
|
||||
|
||||
// listen on ControlChannel
|
||||
ctrlListener, err := n.tunnel.Listen(ControlChannel)
|
||||
ctrlListener, err := n.tunnel.Listen(ControlChannel, tunnel.ListenMode(tunnel.Multicast))
|
||||
if err != nil {
|
||||
n.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// dial into NetworkChannel to send network messages
|
||||
netClient, err := n.tunnel.Dial(NetworkChannel, tunnel.DialMulticast())
|
||||
netClient, err := n.tunnel.Dial(NetworkChannel, tunnel.DialMode(tunnel.Multicast))
|
||||
if err != nil {
|
||||
n.Unlock()
|
||||
return err
|
||||
@@ -763,7 +871,7 @@ func (n *network) Connect() error {
|
||||
n.tunClient[NetworkChannel] = netClient
|
||||
|
||||
// listen on NetworkChannel
|
||||
netListener, err := n.tunnel.Listen(NetworkChannel)
|
||||
netListener, err := n.tunnel.Listen(NetworkChannel, tunnel.ListenMode(tunnel.Multicast))
|
||||
if err != nil {
|
||||
n.Unlock()
|
||||
return err
|
||||
@@ -792,19 +900,8 @@ func (n *network) Connect() error {
|
||||
}
|
||||
n.Unlock()
|
||||
|
||||
// send connect message to NetworkChannel
|
||||
// NOTE: in theory we could do this as soon as
|
||||
// Dial to NetworkChannel succeeds, but instead
|
||||
// we initialize all other node resources first
|
||||
msg := &pbNet.Connect{
|
||||
Node: &pbNet.Node{
|
||||
Id: n.node.id,
|
||||
Address: n.node.address,
|
||||
},
|
||||
}
|
||||
if err := n.sendMsg("connect", msg, NetworkChannel); err != nil {
|
||||
log.Debugf("Network failed to send connect message: %s", err)
|
||||
}
|
||||
// send the connect message
|
||||
n.sendConnect()
|
||||
|
||||
// go resolving network nodes
|
||||
go n.resolve()
|
||||
@@ -813,11 +910,11 @@ func (n *network) Connect() error {
|
||||
// prune stale nodes
|
||||
go n.prune()
|
||||
// listen to network messages
|
||||
go n.processNetChan(netClient, netListener)
|
||||
go n.processNetChan(netListener)
|
||||
// advertise service routes
|
||||
go n.advertise(ctrlClient, advertChan)
|
||||
go n.advertise(advertChan)
|
||||
// accept and process routes
|
||||
go n.processCtrlChan(ctrlClient, ctrlListener)
|
||||
go n.processCtrlChan(ctrlListener)
|
||||
|
||||
n.Lock()
|
||||
n.connected = true
|
||||
|
@@ -38,6 +38,8 @@ type Node interface {
|
||||
type Network interface {
|
||||
// Node is network node
|
||||
Node
|
||||
// Initialise options
|
||||
Init(...Option) error
|
||||
// Options returns the network options
|
||||
Options() Options
|
||||
// Name of the network
|
||||
|
@@ -322,6 +322,11 @@ func peerProtoTopology(peer Node, depth uint) *pb.Peer {
|
||||
Address: peer.Address(),
|
||||
}
|
||||
|
||||
// set the network name if network is not nil
|
||||
if peer.Network() != nil {
|
||||
node.Network = peer.Network().Name()
|
||||
}
|
||||
|
||||
pbPeers := &pb.Peer{
|
||||
Node: node,
|
||||
Peers: make([]*pb.Peer, 0),
|
||||
@@ -351,6 +356,12 @@ func PeersToProto(node Node, depth uint) *pb.Peer {
|
||||
Id: node.Id(),
|
||||
Address: node.Address(),
|
||||
}
|
||||
|
||||
// set the network name if network is not nil
|
||||
if node.Network() != nil {
|
||||
pbNode.Network = node.Network().Name()
|
||||
}
|
||||
|
||||
// we will build proto topology into this
|
||||
pbPeers := &pb.Peer{
|
||||
Node: pbNode,
|
||||
|
@@ -35,6 +35,8 @@ var _ server.Option
|
||||
// Client API for Network service
|
||||
|
||||
type NetworkService interface {
|
||||
// Connect to the network
|
||||
Connect(ctx context.Context, in *ConnectRequest, opts ...client.CallOption) (*ConnectResponse, error)
|
||||
// Returns the entire network graph
|
||||
Graph(ctx context.Context, in *GraphRequest, opts ...client.CallOption) (*GraphResponse, error)
|
||||
// Returns a list of known nodes in the network
|
||||
@@ -63,6 +65,16 @@ func NewNetworkService(name string, c client.Client) NetworkService {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *networkService) Connect(ctx context.Context, in *ConnectRequest, opts ...client.CallOption) (*ConnectResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Network.Connect", in)
|
||||
out := new(ConnectResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *networkService) Graph(ctx context.Context, in *GraphRequest, opts ...client.CallOption) (*GraphResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Network.Graph", in)
|
||||
out := new(GraphResponse)
|
||||
@@ -106,6 +118,8 @@ func (c *networkService) Services(ctx context.Context, in *ServicesRequest, opts
|
||||
// Server API for Network service
|
||||
|
||||
type NetworkHandler interface {
|
||||
// Connect to the network
|
||||
Connect(context.Context, *ConnectRequest, *ConnectResponse) error
|
||||
// Returns the entire network graph
|
||||
Graph(context.Context, *GraphRequest, *GraphResponse) error
|
||||
// Returns a list of known nodes in the network
|
||||
@@ -118,6 +132,7 @@ type NetworkHandler interface {
|
||||
|
||||
func RegisterNetworkHandler(s server.Server, hdlr NetworkHandler, opts ...server.HandlerOption) error {
|
||||
type network interface {
|
||||
Connect(ctx context.Context, in *ConnectRequest, out *ConnectResponse) error
|
||||
Graph(ctx context.Context, in *GraphRequest, out *GraphResponse) error
|
||||
Nodes(ctx context.Context, in *NodesRequest, out *NodesResponse) error
|
||||
Routes(ctx context.Context, in *RoutesRequest, out *RoutesResponse) error
|
||||
@@ -134,6 +149,10 @@ type networkHandler struct {
|
||||
NetworkHandler
|
||||
}
|
||||
|
||||
func (h *networkHandler) Connect(ctx context.Context, in *ConnectRequest, out *ConnectResponse) error {
|
||||
return h.NetworkHandler.Connect(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *networkHandler) Graph(ctx context.Context, in *GraphRequest, out *GraphResponse) error {
|
||||
return h.NetworkHandler.Graph(ctx, in, out)
|
||||
}
|
||||
|
@@ -23,6 +23,148 @@ var _ = math.Inf
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Query is passed in a LookupRequest
|
||||
type Query struct {
|
||||
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
|
||||
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Gateway string `protobuf:"bytes,3,opt,name=gateway,proto3" json:"gateway,omitempty"`
|
||||
Router string `protobuf:"bytes,4,opt,name=router,proto3" json:"router,omitempty"`
|
||||
Network string `protobuf:"bytes,5,opt,name=network,proto3" json:"network,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Query) Reset() { *m = Query{} }
|
||||
func (m *Query) String() string { return proto.CompactTextString(m) }
|
||||
func (*Query) ProtoMessage() {}
|
||||
func (*Query) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{0}
|
||||
}
|
||||
|
||||
func (m *Query) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Query.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Query) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Query.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Query) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Query.Merge(m, src)
|
||||
}
|
||||
func (m *Query) XXX_Size() int {
|
||||
return xxx_messageInfo_Query.Size(m)
|
||||
}
|
||||
func (m *Query) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Query.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Query proto.InternalMessageInfo
|
||||
|
||||
func (m *Query) GetService() string {
|
||||
if m != nil {
|
||||
return m.Service
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Query) GetAddress() string {
|
||||
if m != nil {
|
||||
return m.Address
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Query) GetGateway() string {
|
||||
if m != nil {
|
||||
return m.Gateway
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Query) GetRouter() string {
|
||||
if m != nil {
|
||||
return m.Router
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Query) GetNetwork() string {
|
||||
if m != nil {
|
||||
return m.Network
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ConnectRequest struct {
|
||||
Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ConnectRequest) Reset() { *m = ConnectRequest{} }
|
||||
func (m *ConnectRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ConnectRequest) ProtoMessage() {}
|
||||
func (*ConnectRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{1}
|
||||
}
|
||||
|
||||
func (m *ConnectRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ConnectRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ConnectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ConnectRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ConnectRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ConnectRequest.Merge(m, src)
|
||||
}
|
||||
func (m *ConnectRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_ConnectRequest.Size(m)
|
||||
}
|
||||
func (m *ConnectRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ConnectRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ConnectRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *ConnectRequest) GetNodes() []*Node {
|
||||
if m != nil {
|
||||
return m.Nodes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ConnectResponse struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ConnectResponse) Reset() { *m = ConnectResponse{} }
|
||||
func (m *ConnectResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ConnectResponse) ProtoMessage() {}
|
||||
func (*ConnectResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{2}
|
||||
}
|
||||
|
||||
func (m *ConnectResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ConnectResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ConnectResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ConnectResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ConnectResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ConnectResponse.Merge(m, src)
|
||||
}
|
||||
func (m *ConnectResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_ConnectResponse.Size(m)
|
||||
}
|
||||
func (m *ConnectResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ConnectResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ConnectResponse proto.InternalMessageInfo
|
||||
|
||||
// PeerRequest requests list of peers
|
||||
type NodesRequest struct {
|
||||
// node topology depth
|
||||
@@ -36,7 +178,7 @@ func (m *NodesRequest) Reset() { *m = NodesRequest{} }
|
||||
func (m *NodesRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*NodesRequest) ProtoMessage() {}
|
||||
func (*NodesRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{0}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{3}
|
||||
}
|
||||
|
||||
func (m *NodesRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -77,7 +219,7 @@ func (m *NodesResponse) Reset() { *m = NodesResponse{} }
|
||||
func (m *NodesResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*NodesResponse) ProtoMessage() {}
|
||||
func (*NodesResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{1}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{4}
|
||||
}
|
||||
|
||||
func (m *NodesResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -117,7 +259,7 @@ func (m *GraphRequest) Reset() { *m = GraphRequest{} }
|
||||
func (m *GraphRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*GraphRequest) ProtoMessage() {}
|
||||
func (*GraphRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{2}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{5}
|
||||
}
|
||||
|
||||
func (m *GraphRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -156,7 +298,7 @@ func (m *GraphResponse) Reset() { *m = GraphResponse{} }
|
||||
func (m *GraphResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*GraphResponse) ProtoMessage() {}
|
||||
func (*GraphResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{3}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{6}
|
||||
}
|
||||
|
||||
func (m *GraphResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -185,6 +327,8 @@ func (m *GraphResponse) GetRoot() *Peer {
|
||||
}
|
||||
|
||||
type RoutesRequest struct {
|
||||
// filter based on
|
||||
Query *Query `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -194,7 +338,7 @@ func (m *RoutesRequest) Reset() { *m = RoutesRequest{} }
|
||||
func (m *RoutesRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*RoutesRequest) ProtoMessage() {}
|
||||
func (*RoutesRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{4}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{7}
|
||||
}
|
||||
|
||||
func (m *RoutesRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -215,6 +359,13 @@ func (m *RoutesRequest) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_RoutesRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *RoutesRequest) GetQuery() *Query {
|
||||
if m != nil {
|
||||
return m.Query
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RoutesResponse struct {
|
||||
Routes []*proto1.Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
@@ -226,7 +377,7 @@ func (m *RoutesResponse) Reset() { *m = RoutesResponse{} }
|
||||
func (m *RoutesResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*RoutesResponse) ProtoMessage() {}
|
||||
func (*RoutesResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{5}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{8}
|
||||
}
|
||||
|
||||
func (m *RoutesResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -264,7 +415,7 @@ func (m *ServicesRequest) Reset() { *m = ServicesRequest{} }
|
||||
func (m *ServicesRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ServicesRequest) ProtoMessage() {}
|
||||
func (*ServicesRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{6}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{9}
|
||||
}
|
||||
|
||||
func (m *ServicesRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -296,7 +447,7 @@ func (m *ServicesResponse) Reset() { *m = ServicesResponse{} }
|
||||
func (m *ServicesResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ServicesResponse) ProtoMessage() {}
|
||||
func (*ServicesResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{7}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{10}
|
||||
}
|
||||
|
||||
func (m *ServicesResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -329,17 +480,21 @@ type Node struct {
|
||||
// node id
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
// node address
|
||||
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
|
||||
// the network
|
||||
Network string `protobuf:"bytes,3,opt,name=network,proto3" json:"network,omitempty"`
|
||||
// associated metadata
|
||||
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Node) Reset() { *m = Node{} }
|
||||
func (m *Node) String() string { return proto.CompactTextString(m) }
|
||||
func (*Node) ProtoMessage() {}
|
||||
func (*Node) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{8}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{11}
|
||||
}
|
||||
|
||||
func (m *Node) XXX_Unmarshal(b []byte) error {
|
||||
@@ -374,6 +529,20 @@ func (m *Node) GetAddress() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Node) GetNetwork() string {
|
||||
if m != nil {
|
||||
return m.Network
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Node) GetMetadata() map[string]string {
|
||||
if m != nil {
|
||||
return m.Metadata
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Connect is sent when the node connects to the network
|
||||
type Connect struct {
|
||||
// network mode
|
||||
@@ -387,7 +556,7 @@ func (m *Connect) Reset() { *m = Connect{} }
|
||||
func (m *Connect) String() string { return proto.CompactTextString(m) }
|
||||
func (*Connect) ProtoMessage() {}
|
||||
func (*Connect) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{9}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{12}
|
||||
}
|
||||
|
||||
func (m *Connect) XXX_Unmarshal(b []byte) error {
|
||||
@@ -428,7 +597,7 @@ func (m *Close) Reset() { *m = Close{} }
|
||||
func (m *Close) String() string { return proto.CompactTextString(m) }
|
||||
func (*Close) ProtoMessage() {}
|
||||
func (*Close) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{10}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{13}
|
||||
}
|
||||
|
||||
func (m *Close) XXX_Unmarshal(b []byte) error {
|
||||
@@ -471,7 +640,7 @@ func (m *Peer) Reset() { *m = Peer{} }
|
||||
func (m *Peer) String() string { return proto.CompactTextString(m) }
|
||||
func (*Peer) ProtoMessage() {}
|
||||
func (*Peer) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{11}
|
||||
return fileDescriptor_0b7953b26a7c4730, []int{14}
|
||||
}
|
||||
|
||||
func (m *Peer) XXX_Unmarshal(b []byte) error {
|
||||
@@ -507,6 +676,9 @@ func (m *Peer) GetPeers() []*Peer {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Query)(nil), "go.micro.network.Query")
|
||||
proto.RegisterType((*ConnectRequest)(nil), "go.micro.network.ConnectRequest")
|
||||
proto.RegisterType((*ConnectResponse)(nil), "go.micro.network.ConnectResponse")
|
||||
proto.RegisterType((*NodesRequest)(nil), "go.micro.network.NodesRequest")
|
||||
proto.RegisterType((*NodesResponse)(nil), "go.micro.network.NodesResponse")
|
||||
proto.RegisterType((*GraphRequest)(nil), "go.micro.network.GraphRequest")
|
||||
@@ -516,6 +688,7 @@ func init() {
|
||||
proto.RegisterType((*ServicesRequest)(nil), "go.micro.network.ServicesRequest")
|
||||
proto.RegisterType((*ServicesResponse)(nil), "go.micro.network.ServicesResponse")
|
||||
proto.RegisterType((*Node)(nil), "go.micro.network.Node")
|
||||
proto.RegisterMapType((map[string]string)(nil), "go.micro.network.Node.MetadataEntry")
|
||||
proto.RegisterType((*Connect)(nil), "go.micro.network.Connect")
|
||||
proto.RegisterType((*Close)(nil), "go.micro.network.Close")
|
||||
proto.RegisterType((*Peer)(nil), "go.micro.network.Peer")
|
||||
@@ -526,33 +699,43 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_0b7953b26a7c4730 = []byte{
|
||||
// 416 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x5d, 0x6f, 0xda, 0x30,
|
||||
0x14, 0x86, 0x21, 0x10, 0x3e, 0xce, 0x16, 0x60, 0xd6, 0x34, 0x45, 0xb9, 0xd8, 0x98, 0xb5, 0x0b,
|
||||
0x34, 0x6d, 0x66, 0x02, 0x71, 0x35, 0x4d, 0x9a, 0xc4, 0x45, 0xa5, 0x4a, 0x45, 0x95, 0xf9, 0x03,
|
||||
0x85, 0xc4, 0x82, 0xa8, 0x25, 0x4e, 0x1d, 0xd3, 0xfe, 0xc2, 0xfe, 0xaf, 0xca, 0x1f, 0xe1, 0x33,
|
||||
0x41, 0xed, 0x1d, 0xe7, 0xf0, 0xf8, 0x3d, 0x3e, 0xaf, 0xdf, 0xc0, 0x64, 0x15, 0xcb, 0xf5, 0x76,
|
||||
0x49, 0x42, 0xbe, 0x19, 0x6e, 0xe2, 0x50, 0xf0, 0xe1, 0x8a, 0xff, 0x36, 0x3f, 0x12, 0x26, 0x9f,
|
||||
0xb9, 0xb8, 0x1f, 0xa6, 0x82, 0xcb, 0x5d, 0x45, 0x74, 0x85, 0x7a, 0x2b, 0x4e, 0x34, 0x45, 0x6c,
|
||||
0x3f, 0x18, 0x97, 0x0b, 0x09, 0xbe, 0x95, 0x4c, 0x58, 0x1d, 0x53, 0x18, 0x19, 0xfc, 0x03, 0x3e,
|
||||
0xce, 0x78, 0xc4, 0x32, 0xca, 0x1e, 0xb7, 0x2c, 0x93, 0xe8, 0x33, 0xb8, 0x11, 0x4b, 0xe5, 0xda,
|
||||
0xaf, 0xf6, 0xab, 0x03, 0x8f, 0x9a, 0x02, 0xff, 0x03, 0xcf, 0x52, 0x59, 0xca, 0x93, 0x8c, 0xa1,
|
||||
0x5f, 0xe0, 0x26, 0xaa, 0xe1, 0x57, 0xfb, 0xb5, 0xc1, 0x87, 0xd1, 0x17, 0x72, 0x7a, 0x1b, 0xa2,
|
||||
0x78, 0x6a, 0x20, 0x35, 0xe4, 0x4a, 0x2c, 0xd2, 0xf5, 0xe5, 0x21, 0x7f, 0xc1, 0xb3, 0x94, 0x1d,
|
||||
0xf2, 0x13, 0xea, 0x82, 0x73, 0xa9, 0xa9, 0xc2, 0x19, 0xb7, 0x8c, 0x09, 0xaa, 0x19, 0xdc, 0x05,
|
||||
0x8f, 0xaa, 0xbd, 0xf2, 0x45, 0xf0, 0x7f, 0xe8, 0xe4, 0x0d, 0x2b, 0x47, 0xa0, 0xa1, 0x57, 0x2f,
|
||||
0xb8, 0xb4, 0xb5, 0x44, 0x1f, 0xa0, 0x96, 0xc2, 0x9f, 0xa0, 0x3b, 0x67, 0xe2, 0x29, 0x0e, 0xf7,
|
||||
0xa2, 0x04, 0x7a, 0xfb, 0x96, 0x95, 0x0d, 0xa0, 0x95, 0xd9, 0x9e, 0x16, 0x6e, 0xd3, 0x5d, 0x8d,
|
||||
0xff, 0x40, 0x5d, 0xf9, 0x80, 0x3a, 0xe0, 0xc4, 0x91, 0xde, 0xa3, 0x4d, 0x9d, 0x38, 0x42, 0x3e,
|
||||
0x34, 0x17, 0x51, 0x24, 0x58, 0x96, 0xf9, 0x8e, 0x6e, 0xe6, 0x25, 0x9e, 0x40, 0x73, 0xca, 0x93,
|
||||
0x84, 0x85, 0x52, 0xad, 0xaf, 0xec, 0x2b, 0x5f, 0x5f, 0x5b, 0xac, 0x19, 0x3c, 0x06, 0x77, 0xfa,
|
||||
0xc0, 0x8d, 0x67, 0x6f, 0x3e, 0x74, 0x07, 0x75, 0xe5, 0xe0, 0x7b, 0xce, 0xa8, 0x87, 0x4f, 0x19,
|
||||
0x13, 0xea, 0xde, 0xb5, 0x0b, 0x8f, 0x62, 0xa0, 0xd1, 0x8b, 0x03, 0xcd, 0x99, 0xe9, 0xa3, 0x6b,
|
||||
0x70, 0xf5, 0xf3, 0xa2, 0xaf, 0xe7, 0x67, 0x0e, 0xd3, 0x11, 0x7c, 0x2b, 0xfd, 0xdf, 0x38, 0x8e,
|
||||
0x2b, 0x4a, 0x4b, 0xe7, 0xb1, 0x48, 0xeb, 0x30, 0xce, 0x45, 0x5a, 0x47, 0x41, 0xc6, 0x15, 0x74,
|
||||
0x03, 0x0d, 0x13, 0x14, 0x54, 0x00, 0x1f, 0x65, 0x2a, 0xe8, 0x97, 0x03, 0x3b, 0xb9, 0x39, 0xb4,
|
||||
0xf2, 0x88, 0xa0, 0xef, 0xe7, 0xfc, 0x49, 0xa2, 0x02, 0x7c, 0x09, 0xc9, 0x45, 0x97, 0x0d, 0xfd,
|
||||
0xb1, 0x8e, 0x5f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x63, 0x7a, 0x7b, 0x2c, 0x04, 0x00, 0x00,
|
||||
// 573 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x61, 0x6a, 0xdb, 0x4c,
|
||||
0x10, 0x8d, 0x2c, 0xcb, 0x76, 0xe6, 0x8b, 0xfd, 0xb9, 0x4b, 0x49, 0x85, 0x7e, 0xb4, 0xee, 0xe2,
|
||||
0x1f, 0xa1, 0x34, 0x32, 0xc4, 0x04, 0x4a, 0x43, 0x43, 0x20, 0x94, 0x42, 0x21, 0x21, 0x55, 0x2e,
|
||||
0x50, 0xc5, 0x1a, 0x6c, 0x93, 0x58, 0xeb, 0xac, 0xd6, 0x09, 0x3e, 0x41, 0x8f, 0xd0, 0x33, 0xf5,
|
||||
0x56, 0x65, 0x77, 0x47, 0x8a, 0x1d, 0xcb, 0xa2, 0xf9, 0xe7, 0xd1, 0xbc, 0xf7, 0x66, 0x67, 0xe6,
|
||||
0x8d, 0xe1, 0x78, 0x3c, 0x55, 0x93, 0xc5, 0x4d, 0x38, 0x12, 0xb3, 0xc1, 0x6c, 0x3a, 0x92, 0x62,
|
||||
0x30, 0x16, 0x87, 0xf6, 0x47, 0x8a, 0xea, 0x51, 0xc8, 0xdb, 0xc1, 0x5c, 0x0a, 0x55, 0x44, 0xa1,
|
||||
0x89, 0x58, 0x77, 0x2c, 0x42, 0x83, 0x0a, 0xe9, 0x7b, 0x30, 0xdc, 0x2e, 0x24, 0xc5, 0x42, 0xa1,
|
||||
0x24, 0x1d, 0x1b, 0x58, 0x19, 0xfe, 0xcb, 0x01, 0xef, 0xc7, 0x02, 0xe5, 0x92, 0xf9, 0xd0, 0xcc,
|
||||
0x50, 0x3e, 0x4c, 0x47, 0xe8, 0x3b, 0x3d, 0xe7, 0x60, 0x37, 0xca, 0x43, 0x9d, 0x89, 0x93, 0x44,
|
||||
0x62, 0x96, 0xf9, 0x35, 0x9b, 0xa1, 0x50, 0x67, 0xc6, 0xb1, 0xc2, 0xc7, 0x78, 0xe9, 0xbb, 0x36,
|
||||
0x43, 0x21, 0xdb, 0x87, 0x86, 0xad, 0xe3, 0xd7, 0x4d, 0x82, 0x22, 0xcd, 0xa0, 0xf7, 0xfa, 0x9e,
|
||||
0x65, 0x50, 0xc8, 0x4f, 0xa1, 0x73, 0x2e, 0xd2, 0x14, 0x47, 0x2a, 0xc2, 0xfb, 0x05, 0x66, 0x8a,
|
||||
0x7d, 0x04, 0x2f, 0x15, 0x09, 0x66, 0xbe, 0xd3, 0x73, 0x0f, 0xfe, 0x3b, 0xda, 0x0f, 0x9f, 0xb7,
|
||||
0x1c, 0x5e, 0x8a, 0x04, 0x23, 0x0b, 0xe2, 0xaf, 0xe0, 0xff, 0x82, 0x9f, 0xcd, 0x45, 0x9a, 0x21,
|
||||
0xef, 0xc3, 0x9e, 0x46, 0x64, 0xb9, 0xe0, 0x6b, 0xf0, 0x12, 0x9c, 0xab, 0x89, 0x69, 0xb0, 0x1d,
|
||||
0xd9, 0x80, 0x7f, 0x81, 0x36, 0xa1, 0x2c, 0xed, 0x85, 0x75, 0xfb, 0xb0, 0xf7, 0x4d, 0xc6, 0xf3,
|
||||
0x49, 0x75, 0x91, 0x13, 0x68, 0x13, 0x8a, 0x8a, 0x7c, 0x80, 0xba, 0x14, 0x42, 0x19, 0x54, 0x69,
|
||||
0x8d, 0x2b, 0x44, 0x19, 0x19, 0x0c, 0x3f, 0x85, 0x76, 0xa4, 0xc7, 0x57, 0x34, 0x72, 0x08, 0xde,
|
||||
0xbd, 0x5e, 0x1a, 0xb1, 0xdf, 0x6c, 0xb2, 0xcd, 0x4e, 0x23, 0x8b, 0xe2, 0x67, 0xd0, 0xc9, 0xf9,
|
||||
0x54, 0x3d, 0xa4, 0xf5, 0x94, 0xf4, 0x48, 0xf6, 0x30, 0x04, 0x5a, 0x9b, 0x19, 0xee, 0xb5, 0x75,
|
||||
0x43, 0xfe, 0x06, 0x1e, 0x42, 0xf7, 0xe9, 0x13, 0xc9, 0x06, 0xd0, 0x22, 0xd3, 0x58, 0xe1, 0xdd,
|
||||
0xa8, 0x88, 0xf9, 0x1f, 0x07, 0xea, 0x7a, 0x6e, 0xac, 0x03, 0xb5, 0x69, 0x42, 0x1e, 0xab, 0x4d,
|
||||
0x93, 0x6a, 0x7b, 0xe5, 0x66, 0x71, 0xd7, 0xcc, 0xc2, 0xce, 0xa0, 0x35, 0x43, 0x15, 0x27, 0xb1,
|
||||
0x8a, 0xfd, 0xba, 0xe9, 0xa0, 0x5f, 0xbe, 0xa5, 0xf0, 0x82, 0x60, 0x5f, 0x53, 0x25, 0x97, 0x51,
|
||||
0xc1, 0x0a, 0x4e, 0xa0, 0xbd, 0x96, 0x62, 0x5d, 0x70, 0x6f, 0x71, 0x49, 0xef, 0xd2, 0x3f, 0xf5,
|
||||
0x26, 0x1f, 0xe2, 0xbb, 0x05, 0xd2, 0xb3, 0x6c, 0xf0, 0xb9, 0xf6, 0xc9, 0xe1, 0xc7, 0xd0, 0x24,
|
||||
0xaf, 0xe9, 0x3d, 0x6a, 0x1f, 0x6c, 0xdf, 0xa3, 0xf1, 0x8a, 0xc1, 0xf0, 0x21, 0x78, 0xe7, 0x77,
|
||||
0xc2, 0x2e, 0xff, 0x9f, 0x49, 0x3f, 0xa1, 0xae, 0xad, 0xf0, 0x12, 0x8e, 0x76, 0xf0, 0x1c, 0x51,
|
||||
0xea, 0x81, 0xba, 0x15, 0xee, 0xb2, 0xa0, 0xa3, 0xdf, 0x2e, 0x34, 0x2f, 0x69, 0xb0, 0x57, 0x4f,
|
||||
0x9d, 0xf5, 0x36, 0x59, 0xeb, 0x07, 0x1a, 0xbc, 0xaf, 0x40, 0xd0, 0x09, 0xee, 0xb0, 0xef, 0xe0,
|
||||
0x19, 0xe7, 0xb3, 0xb7, 0x9b, 0xe8, 0xd5, 0xc3, 0x09, 0xde, 0x6d, 0xcd, 0xaf, 0x6a, 0x99, 0x53,
|
||||
0x2d, 0xd3, 0x5a, 0xbd, 0xf4, 0x32, 0xad, 0xb5, 0x1b, 0xe7, 0x3b, 0xec, 0x02, 0x1a, 0xf6, 0x28,
|
||||
0x58, 0x09, 0x78, 0xed, 0xdc, 0x82, 0xde, 0x76, 0x40, 0x21, 0x77, 0x0d, 0xad, 0xfc, 0x1c, 0x58,
|
||||
0xc9, 0x5c, 0x9e, 0x5d, 0x4f, 0xc0, 0xab, 0x20, 0xb9, 0xe8, 0x4d, 0xc3, 0xfc, 0x49, 0x0f, 0xff,
|
||||
0x06, 0x00, 0x00, 0xff, 0xff, 0x79, 0x8a, 0x5f, 0xf0, 0x24, 0x06, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -567,6 +750,8 @@ const _ = grpc.SupportPackageIsVersion4
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type NetworkClient interface {
|
||||
// Connect to the network
|
||||
Connect(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (*ConnectResponse, error)
|
||||
// Returns the entire network graph
|
||||
Graph(ctx context.Context, in *GraphRequest, opts ...grpc.CallOption) (*GraphResponse, error)
|
||||
// Returns a list of known nodes in the network
|
||||
@@ -585,6 +770,15 @@ func NewNetworkClient(cc *grpc.ClientConn) NetworkClient {
|
||||
return &networkClient{cc}
|
||||
}
|
||||
|
||||
func (c *networkClient) Connect(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (*ConnectResponse, error) {
|
||||
out := new(ConnectResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Connect", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *networkClient) Graph(ctx context.Context, in *GraphRequest, opts ...grpc.CallOption) (*GraphResponse, error) {
|
||||
out := new(GraphResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Graph", in, out, opts...)
|
||||
@@ -623,6 +817,8 @@ func (c *networkClient) Services(ctx context.Context, in *ServicesRequest, opts
|
||||
|
||||
// NetworkServer is the server API for Network service.
|
||||
type NetworkServer interface {
|
||||
// Connect to the network
|
||||
Connect(context.Context, *ConnectRequest) (*ConnectResponse, error)
|
||||
// Returns the entire network graph
|
||||
Graph(context.Context, *GraphRequest) (*GraphResponse, error)
|
||||
// Returns a list of known nodes in the network
|
||||
@@ -637,6 +833,24 @@ func RegisterNetworkServer(s *grpc.Server, srv NetworkServer) {
|
||||
s.RegisterService(&_Network_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Network_Connect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ConnectRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NetworkServer).Connect(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.network.Network/Connect",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NetworkServer).Connect(ctx, req.(*ConnectRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Network_Graph_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GraphRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -713,6 +927,10 @@ var _Network_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.network.Network",
|
||||
HandlerType: (*NetworkServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Connect",
|
||||
Handler: _Network_Connect_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Graph",
|
||||
Handler: _Network_Graph_Handler,
|
||||
|
@@ -6,6 +6,8 @@ import "github.com/micro/go-micro/router/proto/router.proto";
|
||||
|
||||
// Network service is usesd to gain visibility into networks
|
||||
service Network {
|
||||
// Connect to the network
|
||||
rpc Connect(ConnectRequest) returns (ConnectResponse) {};
|
||||
// Returns the entire network graph
|
||||
rpc Graph(GraphRequest) returns (GraphResponse) {};
|
||||
// Returns a list of known nodes in the network
|
||||
@@ -16,6 +18,21 @@ service Network {
|
||||
rpc Services(ServicesRequest) returns (ServicesResponse) {};
|
||||
}
|
||||
|
||||
// Query is passed in a LookupRequest
|
||||
message Query {
|
||||
string service = 1;
|
||||
string address = 2;
|
||||
string gateway = 3;
|
||||
string router = 4;
|
||||
string network = 5;
|
||||
}
|
||||
|
||||
message ConnectRequest {
|
||||
repeated Node nodes = 1;
|
||||
}
|
||||
|
||||
message ConnectResponse {}
|
||||
|
||||
// PeerRequest requests list of peers
|
||||
message NodesRequest {
|
||||
// node topology depth
|
||||
@@ -38,6 +55,8 @@ message GraphResponse {
|
||||
}
|
||||
|
||||
message RoutesRequest {
|
||||
// filter based on
|
||||
Query query = 1;
|
||||
}
|
||||
|
||||
message RoutesResponse {
|
||||
@@ -56,6 +75,10 @@ message Node {
|
||||
string id = 1;
|
||||
// node address
|
||||
string address = 2;
|
||||
// the network
|
||||
string network = 3;
|
||||
// associated metadata
|
||||
map<string,string> metadata = 4;
|
||||
}
|
||||
|
||||
// Connect is sent when the node connects to the network
|
||||
|
@@ -1,8 +1,7 @@
|
||||
// Package dns resolves names to dns srv records
|
||||
// Package dns resolves names to dns records
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/micro/go-micro/network/resolver"
|
||||
@@ -13,19 +12,31 @@ type Resolver struct{}
|
||||
|
||||
// Resolve assumes ID is a domain name e.g micro.mu
|
||||
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
|
||||
_, addrs, err := net.LookupSRV("network", "udp", name)
|
||||
host, port, err := net.SplitHostPort(name)
|
||||
if err != nil {
|
||||
host = name
|
||||
port = "8085"
|
||||
}
|
||||
|
||||
if len(host) == 0 {
|
||||
host = "localhost"
|
||||
}
|
||||
|
||||
addrs, err := net.LookupHost(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var records []*resolver.Record
|
||||
|
||||
for _, addr := range addrs {
|
||||
address := addr.Target
|
||||
if addr.Port > 0 {
|
||||
address = fmt.Sprintf("%s:%d", addr.Target, addr.Port)
|
||||
}
|
||||
// join resolved record with port
|
||||
address := net.JoinHostPort(addr, port)
|
||||
// append to record set
|
||||
records = append(records, &resolver.Record{
|
||||
Address: address,
|
||||
})
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
31
network/resolver/dnssrv/dnssrv.go
Normal file
31
network/resolver/dnssrv/dnssrv.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Package dns srv resolves names to dns srv records
|
||||
package dnssrv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/micro/go-micro/network/resolver"
|
||||
)
|
||||
|
||||
// Resolver is a DNS network resolve
|
||||
type Resolver struct{}
|
||||
|
||||
// Resolve assumes ID is a domain name e.g micro.mu
|
||||
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
|
||||
_, addrs, err := net.LookupSRV("network", "udp", name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var records []*resolver.Record
|
||||
for _, addr := range addrs {
|
||||
address := addr.Target
|
||||
if addr.Port > 0 {
|
||||
address = fmt.Sprintf("%s:%d", addr.Target, addr.Port)
|
||||
}
|
||||
records = append(records, &resolver.Record{
|
||||
Address: address,
|
||||
})
|
||||
}
|
||||
return records, nil
|
||||
}
|
@@ -7,7 +7,9 @@ import (
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/network"
|
||||
pbNet "github.com/micro/go-micro/network/proto"
|
||||
"github.com/micro/go-micro/router"
|
||||
pbRtr "github.com/micro/go-micro/router/proto"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
// Network implements network handler
|
||||
@@ -26,16 +28,14 @@ func flatten(n network.Node, visited map[string]bool) []network.Node {
|
||||
visited = make(map[string]bool)
|
||||
}
|
||||
|
||||
// check if already visited
|
||||
if visited[n.Id()] == true {
|
||||
return nil
|
||||
}
|
||||
|
||||
// create new list of nodes
|
||||
var nodes []network.Node
|
||||
|
||||
// append the current node
|
||||
nodes = append(nodes, n)
|
||||
// check if already visited
|
||||
if visited[n.Id()] == false {
|
||||
// append the current node
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
|
||||
// set to visited
|
||||
visited[n.Id()] = true
|
||||
@@ -48,6 +48,50 @@ func flatten(n network.Node, visited map[string]bool) []network.Node {
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (n *Network) Connect(ctx context.Context, req *pbNet.ConnectRequest, resp *pbNet.ConnectResponse) error {
|
||||
if len(req.Nodes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get list of existing nodes
|
||||
nodes := n.Network.Options().Peers
|
||||
|
||||
// generate a node map
|
||||
nodeMap := make(map[string]bool)
|
||||
|
||||
for _, node := range nodes {
|
||||
nodeMap[node] = true
|
||||
}
|
||||
|
||||
for _, node := range req.Nodes {
|
||||
// TODO: we may have been provided a network only
|
||||
// so process anad resolve node.Network
|
||||
if len(node.Address) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// already exists
|
||||
if _, ok := nodeMap[node.Address]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
nodeMap[node.Address] = true
|
||||
nodes = append(nodes, node.Address)
|
||||
}
|
||||
|
||||
log.Infof("Network.Connect setting peers: %v", nodes)
|
||||
|
||||
// reinitialise the peers
|
||||
n.Network.Init(
|
||||
network.Peers(nodes...),
|
||||
)
|
||||
|
||||
// call the connect method
|
||||
n.Network.Connect()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Nodes returns the list of nodes
|
||||
func (n *Network) Nodes(ctx context.Context, req *pbNet.NodesRequest, resp *pbNet.NodesResponse) error {
|
||||
depth := uint(req.Depth)
|
||||
@@ -100,7 +144,29 @@ func (n *Network) Graph(ctx context.Context, req *pbNet.GraphRequest, resp *pbNe
|
||||
|
||||
// Routes returns a list of routing table routes
|
||||
func (n *Network) Routes(ctx context.Context, req *pbNet.RoutesRequest, resp *pbNet.RoutesResponse) error {
|
||||
routes, err := n.Network.Options().Router.Table().List()
|
||||
// build query
|
||||
|
||||
var qOpts []router.QueryOption
|
||||
|
||||
if q := req.Query; q != nil {
|
||||
if len(q.Service) > 0 {
|
||||
qOpts = append(qOpts, router.QueryService(q.Service))
|
||||
}
|
||||
if len(q.Address) > 0 {
|
||||
qOpts = append(qOpts, router.QueryAddress(q.Address))
|
||||
}
|
||||
if len(q.Gateway) > 0 {
|
||||
qOpts = append(qOpts, router.QueryGateway(q.Gateway))
|
||||
}
|
||||
if len(q.Router) > 0 {
|
||||
qOpts = append(qOpts, router.QueryRouter(q.Router))
|
||||
}
|
||||
if len(q.Network) > 0 {
|
||||
qOpts = append(qOpts, router.QueryNetwork(q.Network))
|
||||
}
|
||||
}
|
||||
|
||||
routes, err := n.Network.Options().Router.Table().Query(qOpts...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.network", "failed to list routes: %s", err)
|
||||
}
|
@@ -128,7 +128,7 @@ func (p *Proxy) getRoute(service string) ([]router.Route, error) {
|
||||
p.Unlock()
|
||||
|
||||
// lookup the routes in the router
|
||||
results, err := p.Router.Lookup(router.NewQuery(router.QueryService(service)))
|
||||
results, err := p.Router.Lookup(router.QueryService(service))
|
||||
if err != nil {
|
||||
// check the status of the router
|
||||
if status := p.Router.Status(); status.Code == router.Error {
|
||||
|
@@ -100,7 +100,7 @@ func (c *cache) quit() bool {
|
||||
|
||||
func (c *cache) del(service string) {
|
||||
// don't blow away cache in error state
|
||||
if err := c.getStatus(); err != nil {
|
||||
if err := c.status; err != nil {
|
||||
return
|
||||
}
|
||||
// otherwise delete entries
|
||||
@@ -116,14 +116,13 @@ func (c *cache) get(service string) ([]*registry.Service, error) {
|
||||
services := c.cache[service]
|
||||
// get cache ttl
|
||||
ttl := c.ttls[service]
|
||||
// make a copy
|
||||
cp := registry.Copy(services)
|
||||
|
||||
// got services && within ttl so return cache
|
||||
if c.isValid(services, ttl) {
|
||||
// make a copy
|
||||
cp := registry.Copy(services)
|
||||
// unlock the read
|
||||
if c.isValid(cp, ttl) {
|
||||
c.RUnlock()
|
||||
// return servics
|
||||
// return services
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
@@ -136,15 +135,16 @@ func (c *cache) get(service string) ([]*registry.Service, error) {
|
||||
if len(cached) > 0 {
|
||||
// set the error status
|
||||
c.setStatus(err)
|
||||
|
||||
// return the stale cache
|
||||
return registry.Copy(cached), nil
|
||||
return cached, nil
|
||||
}
|
||||
// otherwise return error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// reset the status
|
||||
if c.getStatus(); err != nil {
|
||||
if err := c.getStatus(); err != nil {
|
||||
c.setStatus(nil)
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ func (c *cache) get(service string) ([]*registry.Service, error) {
|
||||
c.RUnlock()
|
||||
|
||||
// get and return services
|
||||
return get(service, services)
|
||||
return get(service, cp)
|
||||
}
|
||||
|
||||
func (c *cache) set(service string, services []*registry.Service) {
|
||||
@@ -423,7 +423,7 @@ func (c *cache) Stop() {
|
||||
}
|
||||
|
||||
func (c *cache) String() string {
|
||||
return "rcache"
|
||||
return "cache"
|
||||
}
|
||||
|
||||
// New returns a new cache
|
@@ -107,9 +107,9 @@ func configure(c *consulRegistry, opts ...registry.Option) {
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "8500"
|
||||
addr = address
|
||||
addrs = append(addrs, fmt.Sprintf("%s:%s", addr, port))
|
||||
addrs = append(addrs, net.JoinHostPort(addr, port))
|
||||
} else if err == nil {
|
||||
addrs = append(addrs, fmt.Sprintf("%s:%s", addr, port))
|
||||
addrs = append(addrs, net.JoinHostPort(addr, port))
|
||||
}
|
||||
}
|
||||
|
||||
|
398
registry/etcd/etcd.go
Normal file
398
registry/etcd/etcd.go
Normal file
@@ -0,0 +1,398 @@
|
||||
// Package etcd provides an etcd service registry
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
hash "github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
var (
|
||||
prefix = "/micro/registry/"
|
||||
)
|
||||
|
||||
type etcdRegistry struct {
|
||||
client *clientv3.Client
|
||||
options registry.Options
|
||||
|
||||
sync.RWMutex
|
||||
register map[string]uint64
|
||||
leases map[string]clientv3.LeaseID
|
||||
}
|
||||
|
||||
func NewRegistry(opts ...registry.Option) registry.Registry {
|
||||
e := &etcdRegistry{
|
||||
options: registry.Options{},
|
||||
register: make(map[string]uint64),
|
||||
leases: make(map[string]clientv3.LeaseID),
|
||||
}
|
||||
configure(e, opts...)
|
||||
return e
|
||||
}
|
||||
|
||||
func configure(e *etcdRegistry, opts ...registry.Option) error {
|
||||
config := clientv3.Config{
|
||||
Endpoints: []string{"127.0.0.1:2379"},
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&e.options)
|
||||
}
|
||||
|
||||
if e.options.Timeout == 0 {
|
||||
e.options.Timeout = 5 * time.Second
|
||||
}
|
||||
|
||||
if e.options.Secure || e.options.TLSConfig != nil {
|
||||
tlsConfig := e.options.TLSConfig
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
}
|
||||
|
||||
config.TLS = tlsConfig
|
||||
}
|
||||
|
||||
if e.options.Context != nil {
|
||||
u, ok := e.options.Context.Value(authKey{}).(*authCreds)
|
||||
if ok {
|
||||
config.Username = u.Username
|
||||
config.Password = u.Password
|
||||
}
|
||||
}
|
||||
|
||||
var cAddrs []string
|
||||
|
||||
for _, address := range e.options.Addrs {
|
||||
if len(address) == 0 {
|
||||
continue
|
||||
}
|
||||
addr, port, err := net.SplitHostPort(address)
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "2379"
|
||||
addr = address
|
||||
cAddrs = append(cAddrs, net.JoinHostPort(addr, port))
|
||||
} else if err == nil {
|
||||
cAddrs = append(cAddrs, net.JoinHostPort(addr, port))
|
||||
}
|
||||
}
|
||||
|
||||
// if we got addrs then we'll update
|
||||
if len(cAddrs) > 0 {
|
||||
config.Endpoints = cAddrs
|
||||
}
|
||||
|
||||
cli, err := clientv3.New(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.client = cli
|
||||
return nil
|
||||
}
|
||||
|
||||
func encode(s *registry.Service) string {
|
||||
b, _ := json.Marshal(s)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func decode(ds []byte) *registry.Service {
|
||||
var s *registry.Service
|
||||
json.Unmarshal(ds, &s)
|
||||
return s
|
||||
}
|
||||
|
||||
func nodePath(s, id string) string {
|
||||
service := strings.Replace(s, "/", "-", -1)
|
||||
node := strings.Replace(id, "/", "-", -1)
|
||||
return path.Join(prefix, service, node)
|
||||
}
|
||||
|
||||
func servicePath(s string) string {
|
||||
return path.Join(prefix, strings.Replace(s, "/", "-", -1))
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) Init(opts ...registry.Option) error {
|
||||
return configure(e, opts...)
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) Options() registry.Options {
|
||||
return e.options
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) registerNode(s *registry.Service, node *registry.Node, opts ...registry.RegisterOption) error {
|
||||
if len(s.Nodes) == 0 {
|
||||
return errors.New("Require at least one node")
|
||||
}
|
||||
|
||||
// check existing lease cache
|
||||
e.RLock()
|
||||
leaseID, ok := e.leases[s.Name+node.Id]
|
||||
e.RUnlock()
|
||||
|
||||
if !ok {
|
||||
// missing lease, check if the key exists
|
||||
ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
|
||||
defer cancel()
|
||||
|
||||
// look for the existing key
|
||||
rsp, err := e.client.Get(ctx, nodePath(s.Name, node.Id), clientv3.WithSerializable())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the existing lease
|
||||
for _, kv := range rsp.Kvs {
|
||||
if kv.Lease > 0 {
|
||||
leaseID = clientv3.LeaseID(kv.Lease)
|
||||
|
||||
// decode the existing node
|
||||
srv := decode(kv.Value)
|
||||
if srv == nil || len(srv.Nodes) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// create hash of service; uint64
|
||||
h, err := hash.Hash(srv.Nodes[0], nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// save the info
|
||||
e.Lock()
|
||||
e.leases[s.Name+node.Id] = leaseID
|
||||
e.register[s.Name+node.Id] = h
|
||||
e.Unlock()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var leaseNotFound bool
|
||||
|
||||
// renew the lease if it exists
|
||||
if leaseID > 0 {
|
||||
log.Tracef("Renewing existing lease for %s %d", s.Name, leaseID)
|
||||
if _, err := e.client.KeepAliveOnce(context.TODO(), leaseID); err != nil {
|
||||
if err != rpctypes.ErrLeaseNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Tracef("Lease not found for %s %d", s.Name, leaseID)
|
||||
// lease not found do register
|
||||
leaseNotFound = true
|
||||
}
|
||||
}
|
||||
|
||||
// create hash of service; uint64
|
||||
h, err := hash.Hash(node, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get existing hash for the service node
|
||||
e.Lock()
|
||||
v, ok := e.register[s.Name+node.Id]
|
||||
e.Unlock()
|
||||
|
||||
// the service is unchanged, skip registering
|
||||
if ok && v == h && !leaseNotFound {
|
||||
log.Tracef("Service %s node %s unchanged skipping registration", s.Name, node.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
service := ®istry.Service{
|
||||
Name: s.Name,
|
||||
Version: s.Version,
|
||||
Metadata: s.Metadata,
|
||||
Endpoints: s.Endpoints,
|
||||
Nodes: []*registry.Node{node},
|
||||
}
|
||||
|
||||
var options registry.RegisterOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
|
||||
defer cancel()
|
||||
|
||||
var lgr *clientv3.LeaseGrantResponse
|
||||
if options.TTL.Seconds() > 0 {
|
||||
// get a lease used to expire keys since we have a ttl
|
||||
lgr, err = e.client.Grant(ctx, int64(options.TTL.Seconds()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Tracef("Registering %s id %s with lease %v and ttl %v", service.Name, node.Id, lgr, options.TTL)
|
||||
// create an entry for the node
|
||||
if lgr != nil {
|
||||
_, err = e.client.Put(ctx, nodePath(service.Name, node.Id), encode(service), clientv3.WithLease(lgr.ID))
|
||||
} else {
|
||||
_, err = e.client.Put(ctx, nodePath(service.Name, node.Id), encode(service))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Lock()
|
||||
// save our hash of the service
|
||||
e.register[s.Name+node.Id] = h
|
||||
// save our leaseID of the service
|
||||
if lgr != nil {
|
||||
e.leases[s.Name+node.Id] = lgr.ID
|
||||
}
|
||||
e.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) Deregister(s *registry.Service) error {
|
||||
if len(s.Nodes) == 0 {
|
||||
return errors.New("Require at least one node")
|
||||
}
|
||||
|
||||
for _, node := range s.Nodes {
|
||||
e.Lock()
|
||||
// delete our hash of the service
|
||||
delete(e.register, s.Name+node.Id)
|
||||
// delete our lease of the service
|
||||
delete(e.leases, s.Name+node.Id)
|
||||
e.Unlock()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
|
||||
defer cancel()
|
||||
|
||||
log.Tracef("Deregistering %s id %s", s.Name, node.Id)
|
||||
_, err := e.client.Delete(ctx, nodePath(s.Name, node.Id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
|
||||
if len(s.Nodes) == 0 {
|
||||
return errors.New("Require at least one node")
|
||||
}
|
||||
|
||||
var gerr error
|
||||
|
||||
// register each node individually
|
||||
for _, node := range s.Nodes {
|
||||
err := e.registerNode(s, node, opts...)
|
||||
if err != nil {
|
||||
gerr = err
|
||||
}
|
||||
}
|
||||
|
||||
return gerr
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) GetService(name string) ([]*registry.Service, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
|
||||
defer cancel()
|
||||
|
||||
rsp, err := e.client.Get(ctx, servicePath(name)+"/", clientv3.WithPrefix(), clientv3.WithSerializable())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(rsp.Kvs) == 0 {
|
||||
return nil, registry.ErrNotFound
|
||||
}
|
||||
|
||||
serviceMap := map[string]*registry.Service{}
|
||||
|
||||
for _, n := range rsp.Kvs {
|
||||
if sn := decode(n.Value); sn != nil {
|
||||
s, ok := serviceMap[sn.Version]
|
||||
if !ok {
|
||||
s = ®istry.Service{
|
||||
Name: sn.Name,
|
||||
Version: sn.Version,
|
||||
Metadata: sn.Metadata,
|
||||
Endpoints: sn.Endpoints,
|
||||
}
|
||||
serviceMap[s.Version] = s
|
||||
}
|
||||
|
||||
for _, node := range sn.Nodes {
|
||||
s.Nodes = append(s.Nodes, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var services []*registry.Service
|
||||
for _, service := range serviceMap {
|
||||
services = append(services, service)
|
||||
}
|
||||
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) ListServices() ([]*registry.Service, error) {
|
||||
var services []*registry.Service
|
||||
versions := make(map[string]*registry.Service)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
|
||||
defer cancel()
|
||||
|
||||
rsp, err := e.client.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithSerializable())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(rsp.Kvs) == 0 {
|
||||
return []*registry.Service{}, nil
|
||||
}
|
||||
|
||||
for _, n := range rsp.Kvs {
|
||||
sn := decode(n.Value)
|
||||
if sn == nil {
|
||||
continue
|
||||
}
|
||||
v, ok := versions[sn.Name+sn.Version]
|
||||
if !ok {
|
||||
versions[sn.Name+sn.Version] = sn
|
||||
continue
|
||||
}
|
||||
// append to service:version nodes
|
||||
v.Nodes = append(v.Nodes, sn.Nodes...)
|
||||
}
|
||||
|
||||
for _, service := range versions {
|
||||
services = append(services, service)
|
||||
}
|
||||
|
||||
// sort the services
|
||||
sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })
|
||||
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
|
||||
return newEtcdWatcher(e, e.options.Timeout, opts...)
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) String() string {
|
||||
return "etcd"
|
||||
}
|
24
registry/etcd/options.go
Normal file
24
registry/etcd/options.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
type authKey struct{}
|
||||
|
||||
type authCreds struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Auth allows you to specify username/password
|
||||
func Auth(username, password string) registry.Option {
|
||||
return func(o *registry.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, authKey{}, &authCreds{Username: username, Password: password})
|
||||
}
|
||||
}
|
88
registry/etcd/watcher.go
Normal file
88
registry/etcd/watcher.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
type etcdWatcher struct {
|
||||
stop chan bool
|
||||
w clientv3.WatchChan
|
||||
client *clientv3.Client
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func newEtcdWatcher(r *etcdRegistry, timeout time.Duration, opts ...registry.WatchOption) (registry.Watcher, error) {
|
||||
var wo registry.WatchOptions
|
||||
for _, o := range opts {
|
||||
o(&wo)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
stop := make(chan bool, 1)
|
||||
|
||||
go func() {
|
||||
<-stop
|
||||
cancel()
|
||||
}()
|
||||
|
||||
watchPath := prefix
|
||||
if len(wo.Service) > 0 {
|
||||
watchPath = servicePath(wo.Service) + "/"
|
||||
}
|
||||
|
||||
return &etcdWatcher{
|
||||
stop: stop,
|
||||
w: r.client.Watch(ctx, watchPath, clientv3.WithPrefix(), clientv3.WithPrevKV()),
|
||||
client: r.client,
|
||||
timeout: timeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ew *etcdWatcher) Next() (*registry.Result, error) {
|
||||
for wresp := range ew.w {
|
||||
if wresp.Err() != nil {
|
||||
return nil, wresp.Err()
|
||||
}
|
||||
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() {
|
||||
select {
|
||||
case <-ew.stop:
|
||||
return
|
||||
default:
|
||||
close(ew.stop)
|
||||
}
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
# Gossip Registry
|
||||
|
||||
Gossip is a zero dependency registry which uses github.com/hashicorp/memberlist to broadcast registry information
|
||||
via the SWIM protocol.
|
||||
|
||||
## Usage
|
||||
|
||||
Start with the registry flag or env var
|
||||
|
||||
```bash
|
||||
MICRO_REGISTRY=gossip go run service.go
|
||||
```
|
||||
|
||||
On startup you'll see something like
|
||||
|
||||
```bash
|
||||
2018/12/06 18:17:48 Registry Listening on 192.168.1.65:56390
|
||||
```
|
||||
|
||||
To join this gossip ring set the registry address using flag or env var
|
||||
|
||||
```bash
|
||||
MICRO_REGISTRY_ADDRESS=192.168.1.65:56390
|
||||
```
|
@@ -1,843 +0,0 @@
|
||||
// Package gossip provides a gossip registry based on hashicorp/memberlist
|
||||
package gossip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/memberlist"
|
||||
"github.com/micro/go-micro/registry"
|
||||
pb "github.com/micro/go-micro/registry/gossip/proto"
|
||||
log "github.com/micro/go-micro/util/log"
|
||||
"github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
// use registry.Result int32 values after it switches from string to int32 types
|
||||
// type actionType int32
|
||||
// type updateType int32
|
||||
|
||||
const (
|
||||
actionTypeInvalid int32 = iota
|
||||
actionTypeCreate
|
||||
actionTypeDelete
|
||||
actionTypeUpdate
|
||||
actionTypeSync
|
||||
)
|
||||
|
||||
const (
|
||||
nodeActionUnknown int32 = iota
|
||||
nodeActionJoin
|
||||
nodeActionLeave
|
||||
nodeActionUpdate
|
||||
)
|
||||
|
||||
func actionTypeString(t int32) string {
|
||||
switch t {
|
||||
case actionTypeCreate:
|
||||
return "create"
|
||||
case actionTypeDelete:
|
||||
return "delete"
|
||||
case actionTypeUpdate:
|
||||
return "update"
|
||||
case actionTypeSync:
|
||||
return "sync"
|
||||
}
|
||||
return "invalid"
|
||||
}
|
||||
|
||||
const (
|
||||
updateTypeInvalid int32 = iota
|
||||
updateTypeService
|
||||
)
|
||||
|
||||
type broadcast struct {
|
||||
update *pb.Update
|
||||
notify chan<- struct{}
|
||||
}
|
||||
|
||||
type delegate struct {
|
||||
queue *memberlist.TransmitLimitedQueue
|
||||
updates chan *update
|
||||
}
|
||||
|
||||
type event struct {
|
||||
action int32
|
||||
node string
|
||||
}
|
||||
|
||||
type eventDelegate struct {
|
||||
events chan *event
|
||||
}
|
||||
|
||||
func (ed *eventDelegate) NotifyJoin(n *memberlist.Node) {
|
||||
ed.events <- &event{action: nodeActionJoin, node: n.Address()}
|
||||
}
|
||||
func (ed *eventDelegate) NotifyLeave(n *memberlist.Node) {
|
||||
ed.events <- &event{action: nodeActionLeave, node: n.Address()}
|
||||
}
|
||||
func (ed *eventDelegate) NotifyUpdate(n *memberlist.Node) {
|
||||
ed.events <- &event{action: nodeActionUpdate, node: n.Address()}
|
||||
}
|
||||
|
||||
type gossipRegistry struct {
|
||||
queue *memberlist.TransmitLimitedQueue
|
||||
updates chan *update
|
||||
events chan *event
|
||||
options registry.Options
|
||||
member *memberlist.Memberlist
|
||||
interval time.Duration
|
||||
tcpInterval time.Duration
|
||||
|
||||
connectRetry bool
|
||||
connectTimeout time.Duration
|
||||
sync.RWMutex
|
||||
services map[string][]*registry.Service
|
||||
|
||||
watchers map[string]chan *registry.Result
|
||||
|
||||
mtu int
|
||||
addrs []string
|
||||
members map[string]int32
|
||||
done chan bool
|
||||
}
|
||||
|
||||
type update struct {
|
||||
Update *pb.Update
|
||||
Service *registry.Service
|
||||
sync chan *registry.Service
|
||||
}
|
||||
|
||||
type updates struct {
|
||||
sync.RWMutex
|
||||
services map[uint64]*update
|
||||
}
|
||||
|
||||
var (
|
||||
// You should change this if using secure
|
||||
DefaultSecret = []byte("micro-gossip-key") // exactly 16 bytes
|
||||
ExpiryTick = time.Second * 1 // needs to be smaller than registry.RegisterTTL
|
||||
MaxPacketSize = 512
|
||||
)
|
||||
|
||||
func configure(g *gossipRegistry, opts ...registry.Option) error {
|
||||
// loop through address list and get valid entries
|
||||
addrs := func(curAddrs []string) []string {
|
||||
var newAddrs []string
|
||||
for _, addr := range curAddrs {
|
||||
if trimAddr := strings.TrimSpace(addr); len(trimAddr) > 0 {
|
||||
newAddrs = append(newAddrs, trimAddr)
|
||||
}
|
||||
}
|
||||
return newAddrs
|
||||
}
|
||||
|
||||
// current address list
|
||||
curAddrs := addrs(g.options.Addrs)
|
||||
|
||||
// parse options
|
||||
for _, o := range opts {
|
||||
o(&g.options)
|
||||
}
|
||||
|
||||
// new address list
|
||||
newAddrs := addrs(g.options.Addrs)
|
||||
|
||||
// no new nodes and existing member. no configure
|
||||
if (len(newAddrs) == len(curAddrs)) && g.member != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// shutdown old member
|
||||
g.Stop()
|
||||
|
||||
// lock internals
|
||||
g.Lock()
|
||||
|
||||
// new done chan
|
||||
g.done = make(chan bool)
|
||||
|
||||
// replace addresses
|
||||
curAddrs = newAddrs
|
||||
|
||||
// create a new default config
|
||||
c := memberlist.DefaultLocalConfig()
|
||||
|
||||
// sane good default options
|
||||
c.LogOutput = ioutil.Discard // log to /dev/null
|
||||
c.PushPullInterval = 0 // disable expensive tcp push/pull
|
||||
c.ProtocolVersion = 4 // suport latest stable features
|
||||
|
||||
// set config from options
|
||||
if config, ok := g.options.Context.Value(configKey{}).(*memberlist.Config); ok && config != nil {
|
||||
c = config
|
||||
}
|
||||
|
||||
// set address
|
||||
if address, ok := g.options.Context.Value(addressKey{}).(string); ok {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err == nil {
|
||||
p, err := strconv.Atoi(port)
|
||||
if err == nil {
|
||||
c.BindPort = p
|
||||
}
|
||||
c.BindAddr = host
|
||||
}
|
||||
} else {
|
||||
// set bind to random port
|
||||
c.BindPort = 0
|
||||
}
|
||||
|
||||
// set the advertise address
|
||||
if advertise, ok := g.options.Context.Value(advertiseKey{}).(string); ok {
|
||||
host, port, err := net.SplitHostPort(advertise)
|
||||
if err == nil {
|
||||
p, err := strconv.Atoi(port)
|
||||
if err == nil {
|
||||
c.AdvertisePort = p
|
||||
}
|
||||
c.AdvertiseAddr = host
|
||||
}
|
||||
}
|
||||
|
||||
// machine hostname
|
||||
hostname, _ := os.Hostname()
|
||||
|
||||
// set the name
|
||||
c.Name = strings.Join([]string{"micro", hostname, uuid.New().String()}, "-")
|
||||
|
||||
// set a secret key if secure
|
||||
if g.options.Secure {
|
||||
k, ok := g.options.Context.Value(secretKey{}).([]byte)
|
||||
if !ok {
|
||||
// use the default secret
|
||||
k = DefaultSecret
|
||||
}
|
||||
c.SecretKey = k
|
||||
}
|
||||
|
||||
// set connect retry
|
||||
if v, ok := g.options.Context.Value(connectRetryKey{}).(bool); ok && v {
|
||||
g.connectRetry = true
|
||||
}
|
||||
|
||||
// set connect timeout
|
||||
if td, ok := g.options.Context.Value(connectTimeoutKey{}).(time.Duration); ok {
|
||||
g.connectTimeout = td
|
||||
}
|
||||
|
||||
// create a queue
|
||||
queue := &memberlist.TransmitLimitedQueue{
|
||||
NumNodes: func() int {
|
||||
return len(curAddrs)
|
||||
},
|
||||
RetransmitMult: 3,
|
||||
}
|
||||
|
||||
// set the delegate
|
||||
c.Delegate = &delegate{
|
||||
updates: g.updates,
|
||||
queue: queue,
|
||||
}
|
||||
|
||||
if g.connectRetry {
|
||||
c.Events = &eventDelegate{
|
||||
events: g.events,
|
||||
}
|
||||
}
|
||||
// create the memberlist
|
||||
m, err := memberlist.Create(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(curAddrs) > 0 {
|
||||
for _, addr := range curAddrs {
|
||||
g.members[addr] = nodeActionUnknown
|
||||
}
|
||||
}
|
||||
|
||||
g.tcpInterval = c.PushPullInterval
|
||||
g.addrs = curAddrs
|
||||
g.queue = queue
|
||||
g.member = m
|
||||
g.interval = c.GossipInterval
|
||||
|
||||
g.Unlock()
|
||||
|
||||
log.Logf("[gossip] Registry Listening on %s", m.LocalNode().Address())
|
||||
|
||||
// try connect
|
||||
return g.connect(curAddrs)
|
||||
}
|
||||
|
||||
func (*broadcast) UniqueBroadcast() {}
|
||||
|
||||
func (b *broadcast) Invalidates(other memberlist.Broadcast) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *broadcast) Message() []byte {
|
||||
up, err := proto.Marshal(b.update)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if l := len(up); l > MaxPacketSize {
|
||||
log.Logf("[gossip] broadcast message size %d bigger then MaxPacketSize %d", l, MaxPacketSize)
|
||||
}
|
||||
return up
|
||||
}
|
||||
|
||||
func (b *broadcast) Finished() {
|
||||
if b.notify != nil {
|
||||
close(b.notify)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *delegate) NodeMeta(limit int) []byte {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
func (d *delegate) NotifyMsg(b []byte) {
|
||||
if len(b) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
up := new(pb.Update)
|
||||
if err := proto.Unmarshal(b, up); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// only process service action
|
||||
if up.Type != updateTypeService {
|
||||
return
|
||||
}
|
||||
|
||||
var service *registry.Service
|
||||
|
||||
switch up.Metadata["Content-Type"] {
|
||||
case "application/json":
|
||||
if err := json.Unmarshal(up.Data, &service); err != nil {
|
||||
return
|
||||
}
|
||||
// no other content type
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
// send update
|
||||
d.updates <- &update{
|
||||
Update: up,
|
||||
Service: service,
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte {
|
||||
return d.queue.GetBroadcasts(overhead, limit)
|
||||
}
|
||||
|
||||
func (d *delegate) LocalState(join bool) []byte {
|
||||
if !join {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
syncCh := make(chan *registry.Service, 1)
|
||||
services := map[string][]*registry.Service{}
|
||||
|
||||
d.updates <- &update{
|
||||
Update: &pb.Update{
|
||||
Action: actionTypeSync,
|
||||
},
|
||||
sync: syncCh,
|
||||
}
|
||||
|
||||
for srv := range syncCh {
|
||||
services[srv.Name] = append(services[srv.Name], srv)
|
||||
}
|
||||
|
||||
b, _ := json.Marshal(services)
|
||||
return b
|
||||
}
|
||||
|
||||
func (d *delegate) MergeRemoteState(buf []byte, join bool) {
|
||||
if len(buf) == 0 {
|
||||
return
|
||||
}
|
||||
if !join {
|
||||
return
|
||||
}
|
||||
|
||||
var services map[string][]*registry.Service
|
||||
if err := json.Unmarshal(buf, &services); err != nil {
|
||||
return
|
||||
}
|
||||
for _, service := range services {
|
||||
for _, srv := range service {
|
||||
d.updates <- &update{
|
||||
Update: &pb.Update{Action: actionTypeCreate},
|
||||
Service: srv,
|
||||
sync: nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) connect(addrs []string) error {
|
||||
if len(addrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
timeout := make(<-chan time.Time)
|
||||
|
||||
if g.connectTimeout > 0 {
|
||||
timeout = time.After(g.connectTimeout)
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
fn := func() (int, error) {
|
||||
return g.member.Join(addrs)
|
||||
}
|
||||
|
||||
// don't wait for first try
|
||||
if _, err := fn(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// wait loop
|
||||
for {
|
||||
select {
|
||||
// context closed
|
||||
case <-g.options.Context.Done():
|
||||
return nil
|
||||
// call close, don't wait anymore
|
||||
case <-g.done:
|
||||
return nil
|
||||
// in case of timeout fail with a timeout error
|
||||
case <-timeout:
|
||||
return fmt.Errorf("[gossip] connect timeout %v", g.addrs)
|
||||
// got a tick, try to connect
|
||||
case <-ticker.C:
|
||||
if _, err := fn(); err == nil {
|
||||
log.Logf("[gossip] connect success for %v", g.addrs)
|
||||
return nil
|
||||
} else {
|
||||
log.Logf("[gossip] connect failed for %v", g.addrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) publish(action string, services []*registry.Service) {
|
||||
g.RLock()
|
||||
for _, sub := range g.watchers {
|
||||
go func(sub chan *registry.Result) {
|
||||
for _, service := range services {
|
||||
sub <- ®istry.Result{Action: action, Service: service}
|
||||
}
|
||||
}(sub)
|
||||
}
|
||||
g.RUnlock()
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) subscribe() (chan *registry.Result, chan bool) {
|
||||
next := make(chan *registry.Result, 10)
|
||||
exit := make(chan bool)
|
||||
|
||||
id := uuid.New().String()
|
||||
|
||||
g.Lock()
|
||||
g.watchers[id] = next
|
||||
g.Unlock()
|
||||
|
||||
go func() {
|
||||
<-exit
|
||||
g.Lock()
|
||||
delete(g.watchers, id)
|
||||
close(next)
|
||||
g.Unlock()
|
||||
}()
|
||||
|
||||
return next, exit
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) Stop() error {
|
||||
select {
|
||||
case <-g.done:
|
||||
return nil
|
||||
default:
|
||||
close(g.done)
|
||||
g.Lock()
|
||||
if g.member != nil {
|
||||
g.member.Leave(g.interval * 2)
|
||||
g.member.Shutdown()
|
||||
g.member = nil
|
||||
}
|
||||
g.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// connectLoop attempts to reconnect to the memberlist
|
||||
func (g *gossipRegistry) connectLoop() {
|
||||
// try every second
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-g.done:
|
||||
return
|
||||
case <-g.options.Context.Done():
|
||||
g.Stop()
|
||||
return
|
||||
case <-ticker.C:
|
||||
var addrs []string
|
||||
|
||||
g.RLock()
|
||||
|
||||
// only process if we have a memberlist
|
||||
if g.member == nil {
|
||||
g.RUnlock()
|
||||
continue
|
||||
}
|
||||
|
||||
// self
|
||||
local := g.member.LocalNode().Address()
|
||||
|
||||
// operate on each member
|
||||
for node, action := range g.members {
|
||||
switch action {
|
||||
// process leave event
|
||||
case nodeActionLeave:
|
||||
// don't process self
|
||||
if node == local {
|
||||
continue
|
||||
}
|
||||
addrs = append(addrs, node)
|
||||
}
|
||||
}
|
||||
|
||||
g.RUnlock()
|
||||
|
||||
// connect to all the members
|
||||
// TODO: only connect to new members
|
||||
if len(addrs) > 0 {
|
||||
g.connect(addrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) expiryLoop(updates *updates) {
|
||||
ticker := time.NewTicker(ExpiryTick)
|
||||
defer ticker.Stop()
|
||||
|
||||
g.RLock()
|
||||
done := g.done
|
||||
g.RUnlock()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
now := uint64(time.Now().UnixNano())
|
||||
|
||||
updates.Lock()
|
||||
|
||||
// process all the updates
|
||||
for k, v := range updates.services {
|
||||
// check if expiry time has passed
|
||||
if d := (v.Update.Expires); d < now {
|
||||
// delete from records
|
||||
delete(updates.services, k)
|
||||
// set to delete
|
||||
v.Update.Action = actionTypeDelete
|
||||
// fire a new update
|
||||
g.updates <- v
|
||||
}
|
||||
}
|
||||
|
||||
updates.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process member events
|
||||
func (g *gossipRegistry) eventLoop() {
|
||||
g.RLock()
|
||||
done := g.done
|
||||
g.RUnlock()
|
||||
for {
|
||||
select {
|
||||
// return when done
|
||||
case <-done:
|
||||
return
|
||||
case ev := <-g.events:
|
||||
// TODO: nonblocking update
|
||||
g.Lock()
|
||||
if _, ok := g.members[ev.node]; ok {
|
||||
g.members[ev.node] = ev.action
|
||||
}
|
||||
g.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) run() {
|
||||
updates := &updates{
|
||||
services: make(map[uint64]*update),
|
||||
}
|
||||
|
||||
// expiry loop
|
||||
go g.expiryLoop(updates)
|
||||
|
||||
// event loop
|
||||
go g.eventLoop()
|
||||
|
||||
g.RLock()
|
||||
// connect loop
|
||||
if g.connectRetry {
|
||||
go g.connectLoop()
|
||||
}
|
||||
g.RUnlock()
|
||||
|
||||
// process the updates
|
||||
for u := range g.updates {
|
||||
switch u.Update.Action {
|
||||
case actionTypeCreate:
|
||||
g.Lock()
|
||||
if service, ok := g.services[u.Service.Name]; !ok {
|
||||
g.services[u.Service.Name] = []*registry.Service{u.Service}
|
||||
|
||||
} else {
|
||||
g.services[u.Service.Name] = registry.Merge(service, []*registry.Service{u.Service})
|
||||
}
|
||||
g.Unlock()
|
||||
|
||||
// publish update to watchers
|
||||
go g.publish(actionTypeString(actionTypeCreate), []*registry.Service{u.Service})
|
||||
|
||||
// we need to expire the node at some point in the future
|
||||
if u.Update.Expires > 0 {
|
||||
// create a hash of this service
|
||||
if hash, err := hashstructure.Hash(u.Service, nil); err == nil {
|
||||
updates.Lock()
|
||||
updates.services[hash] = u
|
||||
updates.Unlock()
|
||||
}
|
||||
}
|
||||
case actionTypeDelete:
|
||||
g.Lock()
|
||||
if service, ok := g.services[u.Service.Name]; ok {
|
||||
if services := registry.Remove(service, []*registry.Service{u.Service}); len(services) == 0 {
|
||||
delete(g.services, u.Service.Name)
|
||||
} else {
|
||||
g.services[u.Service.Name] = services
|
||||
}
|
||||
}
|
||||
g.Unlock()
|
||||
|
||||
// publish update to watchers
|
||||
go g.publish(actionTypeString(actionTypeDelete), []*registry.Service{u.Service})
|
||||
|
||||
// delete from expiry checks
|
||||
if hash, err := hashstructure.Hash(u.Service, nil); err == nil {
|
||||
updates.Lock()
|
||||
delete(updates.services, hash)
|
||||
updates.Unlock()
|
||||
}
|
||||
case actionTypeSync:
|
||||
// no sync channel provided
|
||||
if u.sync == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
g.RLock()
|
||||
|
||||
// push all services through the sync chan
|
||||
for _, service := range g.services {
|
||||
for _, srv := range service {
|
||||
u.sync <- srv
|
||||
}
|
||||
|
||||
// publish to watchers
|
||||
go g.publish(actionTypeString(actionTypeCreate), service)
|
||||
}
|
||||
|
||||
g.RUnlock()
|
||||
|
||||
// close the sync chan
|
||||
close(u.sync)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) Init(opts ...registry.Option) error {
|
||||
return configure(g, opts...)
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) Options() registry.Options {
|
||||
return g.options
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
|
||||
b, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.Lock()
|
||||
if service, ok := g.services[s.Name]; !ok {
|
||||
g.services[s.Name] = []*registry.Service{s}
|
||||
} else {
|
||||
g.services[s.Name] = registry.Merge(service, []*registry.Service{s})
|
||||
}
|
||||
g.Unlock()
|
||||
|
||||
var options registry.RegisterOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
if options.TTL == 0 && g.tcpInterval == 0 {
|
||||
return fmt.Errorf("Require register TTL or interval for memberlist.Config")
|
||||
}
|
||||
|
||||
up := &pb.Update{
|
||||
Expires: uint64(time.Now().Add(options.TTL).UnixNano()),
|
||||
Action: actionTypeCreate,
|
||||
Type: updateTypeService,
|
||||
Metadata: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
Data: b,
|
||||
}
|
||||
|
||||
g.queue.QueueBroadcast(&broadcast{
|
||||
update: up,
|
||||
notify: nil,
|
||||
})
|
||||
|
||||
// send update to local watchers
|
||||
g.updates <- &update{
|
||||
Update: up,
|
||||
Service: s,
|
||||
}
|
||||
|
||||
// wait
|
||||
<-time.After(g.interval * 2)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) Deregister(s *registry.Service) error {
|
||||
b, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.Lock()
|
||||
if service, ok := g.services[s.Name]; ok {
|
||||
if services := registry.Remove(service, []*registry.Service{s}); len(services) == 0 {
|
||||
delete(g.services, s.Name)
|
||||
} else {
|
||||
g.services[s.Name] = services
|
||||
}
|
||||
}
|
||||
g.Unlock()
|
||||
|
||||
up := &pb.Update{
|
||||
Action: actionTypeDelete,
|
||||
Type: updateTypeService,
|
||||
Metadata: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
Data: b,
|
||||
}
|
||||
|
||||
g.queue.QueueBroadcast(&broadcast{
|
||||
update: up,
|
||||
notify: nil,
|
||||
})
|
||||
|
||||
// send update to local watchers
|
||||
g.updates <- &update{
|
||||
Update: up,
|
||||
Service: s,
|
||||
}
|
||||
|
||||
// wait
|
||||
<-time.After(g.interval * 2)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) GetService(name string) ([]*registry.Service, error) {
|
||||
g.RLock()
|
||||
service, ok := g.services[name]
|
||||
g.RUnlock()
|
||||
if !ok {
|
||||
return nil, registry.ErrNotFound
|
||||
}
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) ListServices() ([]*registry.Service, error) {
|
||||
var services []*registry.Service
|
||||
g.RLock()
|
||||
for _, service := range g.services {
|
||||
services = append(services, service...)
|
||||
}
|
||||
g.RUnlock()
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
|
||||
n, e := g.subscribe()
|
||||
return newGossipWatcher(n, e, opts...)
|
||||
}
|
||||
|
||||
func (g *gossipRegistry) String() string {
|
||||
return "gossip"
|
||||
}
|
||||
|
||||
func NewRegistry(opts ...registry.Option) registry.Registry {
|
||||
g := &gossipRegistry{
|
||||
options: registry.Options{
|
||||
Context: context.Background(),
|
||||
},
|
||||
done: make(chan bool),
|
||||
events: make(chan *event, 100),
|
||||
updates: make(chan *update, 100),
|
||||
services: make(map[string][]*registry.Service),
|
||||
watchers: make(map[string]chan *registry.Result),
|
||||
members: make(map[string]int32),
|
||||
}
|
||||
// run the updater
|
||||
go g.run()
|
||||
|
||||
// configure the gossiper
|
||||
if err := configure(g, opts...); err != nil {
|
||||
log.Fatalf("[gossip] Error configuring registry: %v", err)
|
||||
}
|
||||
// wait for setup
|
||||
<-time.After(g.interval * 2)
|
||||
|
||||
return g
|
||||
}
|
@@ -1,214 +0,0 @@
|
||||
package gossip
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/memberlist"
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
func newMemberlistConfig() *memberlist.Config {
|
||||
mc := memberlist.DefaultLANConfig()
|
||||
mc.DisableTcpPings = false
|
||||
mc.GossipVerifyIncoming = false
|
||||
mc.GossipVerifyOutgoing = false
|
||||
mc.EnableCompression = false
|
||||
mc.PushPullInterval = 3 * time.Second
|
||||
mc.LogOutput = os.Stderr
|
||||
mc.ProtocolVersion = 4
|
||||
mc.Name = uuid.New().String()
|
||||
return mc
|
||||
}
|
||||
|
||||
func newRegistry(opts ...registry.Option) registry.Registry {
|
||||
options := []registry.Option{
|
||||
ConnectRetry(true),
|
||||
ConnectTimeout(60 * time.Second),
|
||||
}
|
||||
|
||||
options = append(options, opts...)
|
||||
r := NewRegistry(options...)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestGossipRegistryBroadcast(t *testing.T) {
|
||||
mc1 := newMemberlistConfig()
|
||||
r1 := newRegistry(Config(mc1), Address("127.0.0.1:54321"))
|
||||
|
||||
mc2 := newMemberlistConfig()
|
||||
r2 := newRegistry(Config(mc2), Address("127.0.0.1:54322"), registry.Addrs("127.0.0.1:54321"))
|
||||
|
||||
defer r1.(*gossipRegistry).Stop()
|
||||
defer r2.(*gossipRegistry).Stop()
|
||||
|
||||
svc1 := ®istry.Service{Name: "service.1", Version: "0.0.0.1"}
|
||||
svc2 := ®istry.Service{Name: "service.2", Version: "0.0.0.2"}
|
||||
|
||||
if err := r1.Register(svc1, registry.RegisterTTL(10*time.Second)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := r2.Register(svc2, registry.RegisterTTL(10*time.Second)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var found bool
|
||||
svcs, err := r1.ListServices()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
if svc.Name == "service.2" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("[gossip registry] service.2 not found in r1, broadcast not work")
|
||||
}
|
||||
|
||||
found = false
|
||||
|
||||
svcs, err = r2.ListServices()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
if svc.Name == "service.1" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("[gossip registry] broadcast failed: service.1 not found in r2")
|
||||
}
|
||||
|
||||
if err := r1.Deregister(svc1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := r2.Deregister(svc2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
func TestGossipRegistryRetry(t *testing.T) {
|
||||
mc1 := newMemberlistConfig()
|
||||
r1 := newRegistry(Config(mc1), Address("127.0.0.1:54321"))
|
||||
|
||||
mc2 := newMemberlistConfig()
|
||||
r2 := newRegistry(Config(mc2), Address("127.0.0.1:54322"), registry.Addrs("127.0.0.1:54321"))
|
||||
|
||||
defer r1.(*gossipRegistry).Stop()
|
||||
defer r2.(*gossipRegistry).Stop()
|
||||
|
||||
svc1 := ®istry.Service{Name: "service.1", Version: "0.0.0.1"}
|
||||
svc2 := ®istry.Service{Name: "service.2", Version: "0.0.0.2"}
|
||||
|
||||
var mu sync.Mutex
|
||||
ch := make(chan struct{})
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
mu.Lock()
|
||||
if r1 != nil {
|
||||
r1.Register(svc1, registry.RegisterTTL(2*time.Second))
|
||||
}
|
||||
if r2 != nil {
|
||||
r2.Register(svc2, registry.RegisterTTL(2*time.Second))
|
||||
}
|
||||
if ch != nil {
|
||||
close(ch)
|
||||
ch = nil
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
<-ch
|
||||
var found bool
|
||||
|
||||
svcs, err := r2.ListServices()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
if svc.Name == "service.1" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("[gossip registry] broadcast failed: service.1 not found in r2")
|
||||
}
|
||||
|
||||
if err = r1.(*gossipRegistry).Stop(); err != nil {
|
||||
t.Fatalf("[gossip registry] failed to stop registry: %v", err)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
r1 = nil
|
||||
mu.Unlock()
|
||||
|
||||
<-time.After(3 * time.Second)
|
||||
|
||||
found = false
|
||||
svcs, err = r2.ListServices()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
if svc.Name == "service.1" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatalf("[gossip registry] service.1 found in r2")
|
||||
}
|
||||
|
||||
if tr := os.Getenv("TRAVIS"); len(tr) > 0 {
|
||||
t.Logf("[gossip registry] skip test on travis")
|
||||
t.Skip()
|
||||
return
|
||||
}
|
||||
|
||||
r1 = newRegistry(Config(mc1), Address("127.0.0.1:54321"))
|
||||
<-time.After(2 * time.Second)
|
||||
|
||||
found = false
|
||||
svcs, err = r2.ListServices()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
if svc.Name == "service.1" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("[gossip registry] connect retry failed: service.1 not found in r2")
|
||||
}
|
||||
|
||||
if err := r1.Deregister(svc1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := r2.Deregister(svc2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r1.(*gossipRegistry).Stop()
|
||||
r2.(*gossipRegistry).Stop()
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
package gossip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/memberlist"
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
type secretKey struct{}
|
||||
type addressKey struct{}
|
||||
type configKey struct{}
|
||||
type advertiseKey struct{}
|
||||
type connectTimeoutKey struct{}
|
||||
type connectRetryKey struct{}
|
||||
|
||||
// helper for setting registry options
|
||||
func setRegistryOption(k, v interface{}) registry.Option {
|
||||
return func(o *registry.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Secret specifies an encryption key. The value should be either
|
||||
// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
|
||||
func Secret(k []byte) registry.Option {
|
||||
return setRegistryOption(secretKey{}, k)
|
||||
}
|
||||
|
||||
// Address to bind to - host:port
|
||||
func Address(a string) registry.Option {
|
||||
return setRegistryOption(addressKey{}, a)
|
||||
}
|
||||
|
||||
// Config sets *memberlist.Config for configuring gossip
|
||||
func Config(c *memberlist.Config) registry.Option {
|
||||
return setRegistryOption(configKey{}, c)
|
||||
}
|
||||
|
||||
// The address to advertise for other gossip members to connect to - host:port
|
||||
func Advertise(a string) registry.Option {
|
||||
return setRegistryOption(advertiseKey{}, a)
|
||||
}
|
||||
|
||||
// ConnectTimeout sets the registry connect timeout. Use -1 to specify infinite timeout
|
||||
func ConnectTimeout(td time.Duration) registry.Option {
|
||||
return setRegistryOption(connectTimeoutKey{}, td)
|
||||
}
|
||||
|
||||
// ConnectRetry enables reconnect to registry then connection closed,
|
||||
// use with ConnectTimeout to specify how long retry
|
||||
func ConnectRetry(v bool) registry.Option {
|
||||
return setRegistryOption(connectRetryKey{}, v)
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: github.com/micro/go-micro/registry/gossip/proto/gossip.proto
|
||||
|
||||
/*
|
||||
Package gossip is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
github.com/micro/go-micro/registry/gossip/proto/gossip.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Update
|
||||
*/
|
||||
package gossip
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
@@ -1,127 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/micro/go-micro/registry/gossip/proto/gossip.proto
|
||||
|
||||
package gossip
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
math "math"
|
||||
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Update is the message broadcast
|
||||
type Update struct {
|
||||
// time to live for entry
|
||||
Expires uint64 `protobuf:"varint,1,opt,name=expires,proto3" json:"expires,omitempty"`
|
||||
// type of update
|
||||
Type int32 `protobuf:"varint,2,opt,name=type,proto3" json:"type,omitempty"`
|
||||
// what action is taken
|
||||
Action int32 `protobuf:"varint,3,opt,name=action,proto3" json:"action,omitempty"`
|
||||
// any other associated metadata about the data
|
||||
Metadata map[string]string `protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
// the payload data;
|
||||
Data []byte `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Update) Reset() { *m = Update{} }
|
||||
func (m *Update) String() string { return proto.CompactTextString(m) }
|
||||
func (*Update) ProtoMessage() {}
|
||||
func (*Update) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_18cba623e76e57f3, []int{0}
|
||||
}
|
||||
|
||||
func (m *Update) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Update.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Update) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Update.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Update) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Update.Merge(m, src)
|
||||
}
|
||||
func (m *Update) XXX_Size() int {
|
||||
return xxx_messageInfo_Update.Size(m)
|
||||
}
|
||||
func (m *Update) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Update.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Update proto.InternalMessageInfo
|
||||
|
||||
func (m *Update) GetExpires() uint64 {
|
||||
if m != nil {
|
||||
return m.Expires
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Update) GetType() int32 {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Update) GetAction() int32 {
|
||||
if m != nil {
|
||||
return m.Action
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Update) GetMetadata() map[string]string {
|
||||
if m != nil {
|
||||
return m.Metadata
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Update) GetData() []byte {
|
||||
if m != nil {
|
||||
return m.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Update)(nil), "gossip.Update")
|
||||
proto.RegisterMapType((map[string]string)(nil), "gossip.Update.MetadataEntry")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/micro/go-micro/registry/gossip/proto/gossip.proto", fileDescriptor_18cba623e76e57f3)
|
||||
}
|
||||
|
||||
var fileDescriptor_18cba623e76e57f3 = []byte{
|
||||
// 227 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x8f, 0xc1, 0x4a, 0x03, 0x31,
|
||||
0x14, 0x45, 0x49, 0xa7, 0x4d, 0xed, 0x53, 0x41, 0x1e, 0x22, 0x41, 0x5c, 0x0c, 0xae, 0x66, 0xe3,
|
||||
0x0c, 0xe8, 0xa6, 0xa8, 0x5b, 0x97, 0x6e, 0x02, 0x7e, 0x40, 0x3a, 0x0d, 0x31, 0xe8, 0x34, 0x21,
|
||||
0x79, 0x15, 0xf3, 0xa9, 0xfe, 0x8d, 0x34, 0x89, 0x42, 0x77, 0xe7, 0x24, 0x37, 0xdc, 0x1b, 0x78,
|
||||
0x36, 0x96, 0xde, 0xf7, 0x9b, 0x7e, 0x74, 0xd3, 0x30, 0xd9, 0x31, 0xb8, 0xc1, 0xb8, 0xbb, 0x02,
|
||||
0x41, 0x1b, 0x1b, 0x29, 0xa4, 0xc1, 0xb8, 0x18, 0xad, 0x1f, 0x7c, 0x70, 0xe4, 0xaa, 0xf4, 0x59,
|
||||
0x90, 0x17, 0xbb, 0xfd, 0x61, 0xc0, 0xdf, 0xfc, 0x56, 0x91, 0x46, 0x01, 0x4b, 0xfd, 0xed, 0x6d,
|
||||
0xd0, 0x51, 0xb0, 0x96, 0x75, 0x73, 0xf9, 0xa7, 0x88, 0x30, 0xa7, 0xe4, 0xb5, 0x98, 0xb5, 0xac,
|
||||
0x5b, 0xc8, 0xcc, 0x78, 0x05, 0x5c, 0x8d, 0x64, 0xdd, 0x4e, 0x34, 0xf9, 0xb4, 0x1a, 0xae, 0xe1,
|
||||
0x64, 0xd2, 0xa4, 0xb6, 0x8a, 0x94, 0xe0, 0x6d, 0xd3, 0x9d, 0xde, 0xdf, 0xf4, 0xb5, 0xb9, 0xf4,
|
||||
0xf4, 0xaf, 0xf5, 0xfa, 0x65, 0x47, 0x21, 0xc9, 0xff, 0xf4, 0xa1, 0x25, 0xbf, 0x5a, 0xb6, 0xac,
|
||||
0x3b, 0x93, 0x99, 0xaf, 0x9f, 0xe0, 0xfc, 0x28, 0x8e, 0x17, 0xd0, 0x7c, 0xe8, 0x94, 0x07, 0xae,
|
||||
0xe4, 0x01, 0xf1, 0x12, 0x16, 0x5f, 0xea, 0x73, 0x5f, 0xd6, 0xad, 0x64, 0x91, 0xc7, 0xd9, 0x9a,
|
||||
0x6d, 0x78, 0xfe, 0xea, 0xc3, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd6, 0x63, 0x7b, 0x1b, 0x2a,
|
||||
0x01, 0x00, 0x00,
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package gossip;
|
||||
|
||||
// Update is the message broadcast
|
||||
message Update {
|
||||
// time to live for entry
|
||||
uint64 expires = 1;
|
||||
// type of update
|
||||
int32 type = 2;
|
||||
// what action is taken
|
||||
int32 action = 3;
|
||||
// any other associated metadata about the data
|
||||
map<string, string> metadata = 6;
|
||||
// the payload data;
|
||||
bytes data = 7;
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
package gossip
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
type gossipWatcher struct {
|
||||
wo registry.WatchOptions
|
||||
next chan *registry.Result
|
||||
stop chan bool
|
||||
}
|
||||
|
||||
func newGossipWatcher(ch chan *registry.Result, stop chan bool, opts ...registry.WatchOption) (registry.Watcher, error) {
|
||||
var wo registry.WatchOptions
|
||||
for _, o := range opts {
|
||||
o(&wo)
|
||||
}
|
||||
|
||||
return &gossipWatcher{
|
||||
wo: wo,
|
||||
next: ch,
|
||||
stop: stop,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *gossipWatcher) Next() (*registry.Result, error) {
|
||||
for {
|
||||
select {
|
||||
case r, ok := <-m.next:
|
||||
if !ok {
|
||||
return nil, registry.ErrWatcherStopped
|
||||
}
|
||||
// check watch options
|
||||
if len(m.wo.Service) > 0 && r.Service.Name != m.wo.Service {
|
||||
continue
|
||||
}
|
||||
nr := ®istry.Result{}
|
||||
*nr = *r
|
||||
return nr, nil
|
||||
case <-m.stop:
|
||||
return nil, registry.ErrWatcherStopped
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *gossipWatcher) Stop() {
|
||||
select {
|
||||
case <-m.stop:
|
||||
return
|
||||
default:
|
||||
close(m.stop)
|
||||
}
|
||||
}
|
@@ -78,7 +78,7 @@ func (m *Registry) ttlPrune() {
|
||||
// split nodeTrackID into service Name, Version and Node Id
|
||||
trackIdSplit := strings.Split(nodeTrackId, "+")
|
||||
svcName, svcVersion, nodeId := trackIdSplit[0], trackIdSplit[1], trackIdSplit[2]
|
||||
log.Debugf("Registry TTL expired for service %s, node %s", svcName, nodeId)
|
||||
log.Debugf("[memory] Registry TTL expired for service %s, node %s", svcName, nodeId)
|
||||
// we need to find a node that expired and delete it from service nodes
|
||||
if _, ok := m.Services[svcName]; ok {
|
||||
for _, service := range m.Services[svcName] {
|
||||
@@ -178,7 +178,7 @@ func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
log.Debugf("Registry deregistering service: %s", s.Name)
|
||||
log.Debugf("[memory] Registry registering service: %s", s.Name)
|
||||
|
||||
var options registry.RegisterOptions
|
||||
for _, o := range opts {
|
||||
@@ -189,7 +189,7 @@ func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption
|
||||
m.Services[s.Name] = []*registry.Service{s}
|
||||
// add all nodes into nodes map to track their TTL
|
||||
for _, n := range s.Nodes {
|
||||
log.Debugf("Registry tracking new service: %s, node %s", s.Name, n.Id)
|
||||
log.Debugf("[memory] Registry tracking new service: %s, node %s", s.Name, n.Id)
|
||||
m.nodes[nodeTrackId(s.Name, s.Version, n.Id)] = &node{
|
||||
lastSeen: time.Now(),
|
||||
ttl: options.TTL,
|
||||
@@ -228,7 +228,7 @@ func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
log.Debugf("Registry tracking new node: %s for service %s", n.Id, s.Name)
|
||||
log.Debugf("[memory] Registry tracking new node: %s for service %s", n.Id, s.Name)
|
||||
m.nodes[nodeTrackId(s.Name, s.Version, n.Id)] = &node{
|
||||
lastSeen: time.Now(),
|
||||
ttl: options.TTL,
|
||||
@@ -242,7 +242,7 @@ func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption
|
||||
// refresh the timestamp and TTL of the service node
|
||||
for _, n := range s.Nodes {
|
||||
trackId := nodeTrackId(s.Name, s.Version, n.Id)
|
||||
log.Debugf("Registry refreshing TTL for node %s for service %s", n.Id, s.Name)
|
||||
log.Debugf("[memory] Registry refreshing TTL for node %s for service %s", n.Id, s.Name)
|
||||
if trackedNode, ok := m.nodes[trackId]; ok {
|
||||
trackedNode.lastSeen = time.Now()
|
||||
trackedNode.ttl = options.TTL
|
||||
@@ -258,7 +258,7 @@ func (m *Registry) Deregister(s *registry.Service) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
log.Debugf("Registry deregistering service: %s", s.Name)
|
||||
log.Debugf("[memory] Registry deregistering service: %s", s.Name)
|
||||
|
||||
if service, ok := m.Services[s.Name]; ok {
|
||||
// svcNodes collects the list of all node Ids for each service version
|
||||
@@ -277,11 +277,11 @@ func (m *Registry) Deregister(s *registry.Service) error {
|
||||
// if there are no more services we know we have either removed all nodes or there were no nodes
|
||||
if updatedService := registry.Remove(service, []*registry.Service{s}); len(updatedService) == 0 {
|
||||
for _, id := range svcNodes[s.Name][s.Version] {
|
||||
log.Debugf("Registry stopped tracking node %s for service %s", id, s.Name)
|
||||
log.Debugf("[memory] Registry stopped tracking node %s for service %s", id, s.Name)
|
||||
delete(m.nodes, nodeTrackId(s.Name, s.Version, id))
|
||||
go m.sendEvent(®istry.Result{Action: "delete", Service: s})
|
||||
}
|
||||
log.Debugf("Registry deleting service %s: no service nodes", s.Name)
|
||||
log.Debugf("[memory] Registry deleting service %s: no service nodes", s.Name)
|
||||
delete(m.Services, s.Name)
|
||||
return nil
|
||||
} else {
|
||||
@@ -296,7 +296,7 @@ func (m *Registry) Deregister(s *registry.Service) error {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
log.Debugf("Registry stopped tracking node %s for service %s", id, s.Name)
|
||||
log.Debugf("[memory] Registry stopped tracking node %s for service %s", id, s.Name)
|
||||
delete(m.nodes, nodeTrackId(s.Name, s.Version, id))
|
||||
go m.sendEvent(®istry.Result{Action: "delete", Service: s})
|
||||
}
|
||||
|
@@ -6,8 +6,8 @@ import (
|
||||
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/registry"
|
||||
pb "github.com/micro/go-micro/registry/proto"
|
||||
"github.com/micro/go-micro/registry/service"
|
||||
pb "github.com/micro/go-micro/registry/service/proto"
|
||||
)
|
||||
|
||||
type Registry struct {
|
@@ -1,5 +1,5 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: registry.proto
|
||||
// source: micro/go-micro/registry/service/proto/registry.proto
|
||||
|
||||
package go_micro_registry
|
||||
|
@@ -1,11 +1,13 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: registry.proto
|
||||
// source: micro/go-micro/registry/service/proto/registry.proto
|
||||
|
||||
package go_micro_registry
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
@@ -46,7 +48,7 @@ func (x EventType) String() string {
|
||||
}
|
||||
|
||||
func (EventType) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{0}
|
||||
return fileDescriptor_2f73432195c6499a, []int{0}
|
||||
}
|
||||
|
||||
// Service represents a go-micro service
|
||||
@@ -66,7 +68,7 @@ func (m *Service) Reset() { *m = Service{} }
|
||||
func (m *Service) String() string { return proto.CompactTextString(m) }
|
||||
func (*Service) ProtoMessage() {}
|
||||
func (*Service) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{0}
|
||||
return fileDescriptor_2f73432195c6499a, []int{0}
|
||||
}
|
||||
|
||||
func (m *Service) XXX_Unmarshal(b []byte) error {
|
||||
@@ -144,7 +146,7 @@ func (m *Node) Reset() { *m = Node{} }
|
||||
func (m *Node) String() string { return proto.CompactTextString(m) }
|
||||
func (*Node) ProtoMessage() {}
|
||||
func (*Node) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{1}
|
||||
return fileDescriptor_2f73432195c6499a, []int{1}
|
||||
}
|
||||
|
||||
func (m *Node) XXX_Unmarshal(b []byte) error {
|
||||
@@ -208,7 +210,7 @@ func (m *Endpoint) Reset() { *m = Endpoint{} }
|
||||
func (m *Endpoint) String() string { return proto.CompactTextString(m) }
|
||||
func (*Endpoint) ProtoMessage() {}
|
||||
func (*Endpoint) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{2}
|
||||
return fileDescriptor_2f73432195c6499a, []int{2}
|
||||
}
|
||||
|
||||
func (m *Endpoint) XXX_Unmarshal(b []byte) error {
|
||||
@@ -271,7 +273,7 @@ func (m *Value) Reset() { *m = Value{} }
|
||||
func (m *Value) String() string { return proto.CompactTextString(m) }
|
||||
func (*Value) ProtoMessage() {}
|
||||
func (*Value) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{3}
|
||||
return fileDescriptor_2f73432195c6499a, []int{3}
|
||||
}
|
||||
|
||||
func (m *Value) XXX_Unmarshal(b []byte) error {
|
||||
@@ -325,7 +327,7 @@ func (m *Options) Reset() { *m = Options{} }
|
||||
func (m *Options) String() string { return proto.CompactTextString(m) }
|
||||
func (*Options) ProtoMessage() {}
|
||||
func (*Options) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{4}
|
||||
return fileDescriptor_2f73432195c6499a, []int{4}
|
||||
}
|
||||
|
||||
func (m *Options) XXX_Unmarshal(b []byte) error {
|
||||
@@ -367,7 +369,7 @@ func (m *Result) Reset() { *m = Result{} }
|
||||
func (m *Result) String() string { return proto.CompactTextString(m) }
|
||||
func (*Result) ProtoMessage() {}
|
||||
func (*Result) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{5}
|
||||
return fileDescriptor_2f73432195c6499a, []int{5}
|
||||
}
|
||||
|
||||
func (m *Result) XXX_Unmarshal(b []byte) error {
|
||||
@@ -419,7 +421,7 @@ func (m *EmptyResponse) Reset() { *m = EmptyResponse{} }
|
||||
func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*EmptyResponse) ProtoMessage() {}
|
||||
func (*EmptyResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{6}
|
||||
return fileDescriptor_2f73432195c6499a, []int{6}
|
||||
}
|
||||
|
||||
func (m *EmptyResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -451,7 +453,7 @@ func (m *GetRequest) Reset() { *m = GetRequest{} }
|
||||
func (m *GetRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetRequest) ProtoMessage() {}
|
||||
func (*GetRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{7}
|
||||
return fileDescriptor_2f73432195c6499a, []int{7}
|
||||
}
|
||||
|
||||
func (m *GetRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -490,7 +492,7 @@ func (m *GetResponse) Reset() { *m = GetResponse{} }
|
||||
func (m *GetResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetResponse) ProtoMessage() {}
|
||||
func (*GetResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{8}
|
||||
return fileDescriptor_2f73432195c6499a, []int{8}
|
||||
}
|
||||
|
||||
func (m *GetResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -528,7 +530,7 @@ func (m *ListRequest) Reset() { *m = ListRequest{} }
|
||||
func (m *ListRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ListRequest) ProtoMessage() {}
|
||||
func (*ListRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{9}
|
||||
return fileDescriptor_2f73432195c6499a, []int{9}
|
||||
}
|
||||
|
||||
func (m *ListRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -560,7 +562,7 @@ func (m *ListResponse) Reset() { *m = ListResponse{} }
|
||||
func (m *ListResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ListResponse) ProtoMessage() {}
|
||||
func (*ListResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{10}
|
||||
return fileDescriptor_2f73432195c6499a, []int{10}
|
||||
}
|
||||
|
||||
func (m *ListResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -600,7 +602,7 @@ func (m *WatchRequest) Reset() { *m = WatchRequest{} }
|
||||
func (m *WatchRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*WatchRequest) ProtoMessage() {}
|
||||
func (*WatchRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{11}
|
||||
return fileDescriptor_2f73432195c6499a, []int{11}
|
||||
}
|
||||
|
||||
func (m *WatchRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -647,7 +649,7 @@ func (m *Event) Reset() { *m = Event{} }
|
||||
func (m *Event) String() string { return proto.CompactTextString(m) }
|
||||
func (*Event) ProtoMessage() {}
|
||||
func (*Event) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_41af05d40a615591, []int{12}
|
||||
return fileDescriptor_2f73432195c6499a, []int{12}
|
||||
}
|
||||
|
||||
func (m *Event) XXX_Unmarshal(b []byte) error {
|
||||
@@ -716,50 +718,285 @@ func init() {
|
||||
proto.RegisterType((*Event)(nil), "go.micro.registry.Event")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("registry.proto", fileDescriptor_41af05d40a615591) }
|
||||
func init() {
|
||||
proto.RegisterFile("micro/go-micro/registry/service/proto/registry.proto", fileDescriptor_2f73432195c6499a)
|
||||
}
|
||||
|
||||
var fileDescriptor_41af05d40a615591 = []byte{
|
||||
// 667 bytes of a gzipped FileDescriptorProto
|
||||
var fileDescriptor_2f73432195c6499a = []byte{
|
||||
// 681 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xdd, 0x6e, 0xd3, 0x4c,
|
||||
0x10, 0x8d, 0xed, 0xfc, 0x4e, 0xda, 0x7e, 0xfd, 0x46, 0x08, 0x8c, 0x5b, 0x20, 0xb2, 0x04, 0x0a,
|
||||
0x48, 0x84, 0x2a, 0x54, 0x88, 0x9f, 0x2b, 0x44, 0x43, 0x25, 0xd4, 0x82, 0x58, 0xfe, 0xae, 0x4d,
|
||||
0x3c, 0x2a, 0x16, 0x89, 0x6d, 0x76, 0xb7, 0x91, 0xf2, 0x0e, 0x48, 0x3c, 0x01, 0x6f, 0xc3, 0x53,
|
||||
0xf0, 0x34, 0x68, 0xd7, 0xeb, 0x24, 0x55, 0xd7, 0x01, 0xa9, 0x70, 0x37, 0xe3, 0x3d, 0x33, 0x3b,
|
||||
0x73, 0xce, 0x59, 0x19, 0xb6, 0x38, 0x9d, 0x24, 0x42, 0xf2, 0xf9, 0x20, 0xe7, 0x99, 0xcc, 0xf0,
|
||||
0xff, 0x93, 0x6c, 0x30, 0x4d, 0xc6, 0x3c, 0x1b, 0x94, 0x07, 0xe1, 0x4f, 0x17, 0x5a, 0x6f, 0x88,
|
||||
0xcf, 0x92, 0x31, 0x21, 0x42, 0x3d, 0x8d, 0xa6, 0xe4, 0x3b, 0x3d, 0xa7, 0xdf, 0x61, 0x3a, 0x46,
|
||||
0x1f, 0x5a, 0x33, 0xe2, 0x22, 0xc9, 0x52, 0xdf, 0xd5, 0x9f, 0xcb, 0x14, 0x0f, 0xa0, 0x3d, 0x25,
|
||||
0x19, 0xc5, 0x91, 0x8c, 0x7c, 0xaf, 0xe7, 0xf5, 0xbb, 0xc3, 0xfe, 0xe0, 0x5c, 0xff, 0x81, 0xe9,
|
||||
0x3d, 0x38, 0x36, 0xd0, 0x51, 0x2a, 0xf9, 0x9c, 0x2d, 0x2a, 0xf1, 0x11, 0x74, 0x28, 0x8d, 0xf3,
|
||||
0x2c, 0x49, 0xa5, 0xf0, 0xeb, 0xba, 0xcd, 0x8e, 0xa5, 0xcd, 0xc8, 0x60, 0xd8, 0x12, 0x8d, 0x77,
|
||||
0xa1, 0x91, 0x66, 0x31, 0x09, 0xbf, 0xa1, 0xcb, 0xae, 0x58, 0xca, 0x5e, 0x66, 0x31, 0xb1, 0x02,
|
||||
0x85, 0xfb, 0xd0, 0xca, 0x72, 0x99, 0x64, 0xa9, 0xf0, 0x9b, 0x3d, 0xa7, 0xdf, 0x1d, 0x06, 0x96,
|
||||
0x82, 0x57, 0x05, 0x82, 0x95, 0xd0, 0xe0, 0x09, 0x6c, 0x9e, 0x19, 0x1d, 0xb7, 0xc1, 0xfb, 0x4c,
|
||||
0x73, 0xc3, 0x91, 0x0a, 0xf1, 0x12, 0x34, 0x66, 0xd1, 0xe4, 0x94, 0x0c, 0x41, 0x45, 0xf2, 0xd8,
|
||||
0x7d, 0xe8, 0x84, 0x3f, 0x1c, 0xa8, 0xab, 0x11, 0x70, 0x0b, 0xdc, 0x24, 0x36, 0x35, 0x6e, 0x12,
|
||||
0x2b, 0x56, 0xa3, 0x38, 0xe6, 0x24, 0x44, 0xc9, 0xaa, 0x49, 0x95, 0x06, 0x79, 0xc6, 0xa5, 0xef,
|
||||
0xf5, 0x9c, 0xbe, 0xc7, 0x74, 0x8c, 0x4f, 0x57, 0x98, 0x2e, 0x28, 0xba, 0x59, 0xb1, 0x6b, 0x15,
|
||||
0xcd, 0x17, 0x5b, 0xe3, 0xab, 0x0b, 0xed, 0x52, 0x00, 0xab, 0x49, 0x86, 0xd0, 0xe2, 0xf4, 0xe5,
|
||||
0x94, 0x84, 0xd4, 0xc5, 0xdd, 0xa1, 0x6f, 0x99, 0xef, 0xbd, 0xea, 0xc7, 0x4a, 0x20, 0xee, 0x43,
|
||||
0x9b, 0x93, 0xc8, 0xb3, 0x54, 0x90, 0x5e, 0x76, 0x5d, 0xd1, 0x02, 0x89, 0xa3, 0x73, 0x54, 0xdc,
|
||||
0x5e, 0xe3, 0x96, 0x7f, 0x43, 0x47, 0x04, 0x0d, 0x3d, 0x96, 0x95, 0x0a, 0x84, 0xba, 0x9c, 0xe7,
|
||||
0x65, 0x95, 0x8e, 0x71, 0x0f, 0x9a, 0xba, 0x5a, 0x98, 0x77, 0x52, 0xbd, 0xa8, 0xc1, 0x85, 0x3b,
|
||||
0xd0, 0x32, 0x4e, 0x54, 0x93, 0x49, 0x39, 0xd1, 0x77, 0x78, 0x4c, 0x85, 0xa1, 0x84, 0x26, 0x23,
|
||||
0x71, 0x3a, 0x91, 0x78, 0x19, 0x9a, 0xd1, 0x58, 0xc1, 0xcc, 0x08, 0x26, 0x53, 0x56, 0x17, 0xc5,
|
||||
0xbb, 0x33, 0x7a, 0x04, 0xd5, 0x2f, 0x93, 0x95, 0x50, 0xdc, 0x85, 0x8e, 0x4c, 0xa6, 0x24, 0x64,
|
||||
0x34, 0xcd, 0x8d, 0xff, 0x96, 0x1f, 0xc2, 0xff, 0x60, 0x73, 0x34, 0xcd, 0xe5, 0x9c, 0x19, 0x29,
|
||||
0xc2, 0x5b, 0x00, 0x87, 0x24, 0x99, 0x91, 0xd3, 0x5f, 0x5e, 0x59, 0xcc, 0x52, 0xa6, 0xe1, 0x08,
|
||||
0xba, 0x1a, 0x67, 0x14, 0x7c, 0x00, 0x6d, 0x73, 0x22, 0x7c, 0x47, 0xd3, 0xb1, 0x6e, 0xb8, 0x05,
|
||||
0x36, 0xdc, 0x84, 0xee, 0x51, 0x22, 0xca, 0xfb, 0xc2, 0xe7, 0xb0, 0x51, 0xa4, 0x17, 0x6c, 0xdb,
|
||||
0x87, 0x8d, 0x0f, 0x91, 0x1c, 0x7f, 0xfa, 0xfd, 0x1e, 0xdf, 0x1d, 0x68, 0x8c, 0x66, 0x94, 0xca,
|
||||
0x73, 0xaf, 0x79, 0x6f, 0x45, 0xf3, 0xad, 0xe1, 0xae, 0xcd, 0x90, 0xaa, 0xee, 0xed, 0x3c, 0x27,
|
||||
0xe3, 0x88, 0xb5, 0x54, 0xaf, 0xca, 0x57, 0xff, 0x63, 0xf9, 0xee, 0xdc, 0x83, 0xce, 0xe2, 0x1a,
|
||||
0x04, 0x68, 0x3e, 0xe3, 0x14, 0x49, 0xda, 0xae, 0xa9, 0xf8, 0x80, 0x26, 0x24, 0x69, 0xdb, 0x51,
|
||||
0xf1, 0xbb, 0x3c, 0x56, 0xdf, 0xdd, 0xe1, 0x37, 0x0f, 0xda, 0xcc, 0xb4, 0xc3, 0x63, 0xad, 0x66,
|
||||
0xf9, 0x27, 0xb8, 0x66, 0xb9, 0x70, 0x29, 0x76, 0x70, 0xbd, 0xea, 0xd8, 0x58, 0xa3, 0x86, 0x2f,
|
||||
0xca, 0xd6, 0xc4, 0x71, 0xcd, 0xf4, 0x41, 0xcf, 0x46, 0xd6, 0x19, 0x9b, 0xd5, 0xf0, 0x08, 0xe0,
|
||||
0x80, 0xf8, 0xdf, 0xea, 0xf6, 0xba, 0x30, 0x8e, 0x29, 0x11, 0x68, 0xdb, 0x65, 0xc5, 0x68, 0xc1,
|
||||
0x8d, 0xca, 0xf3, 0x45, 0xcb, 0x43, 0x68, 0x68, 0x0f, 0xa1, 0x0d, 0xbb, 0xea, 0xae, 0xe0, 0xaa,
|
||||
0x05, 0x50, 0xbc, 0xe5, 0xb0, 0xb6, 0xe7, 0x7c, 0x6c, 0xea, 0xdf, 0xf4, 0xfd, 0x5f, 0x01, 0x00,
|
||||
0x00, 0xff, 0xff, 0xfb, 0x3e, 0x7d, 0xa4, 0xb8, 0x07, 0x00, 0x00,
|
||||
0x48, 0x4d, 0xaa, 0x50, 0x21, 0x7e, 0xae, 0x10, 0x0d, 0x95, 0x50, 0x0b, 0x62, 0xf9, 0xbb, 0x36,
|
||||
0xf1, 0xa8, 0x58, 0x24, 0xb6, 0xd9, 0xdd, 0x46, 0xca, 0x3b, 0x20, 0xf1, 0x04, 0xbc, 0x0d, 0x4f,
|
||||
0xc1, 0xd3, 0xa0, 0x5d, 0xaf, 0x93, 0x54, 0xdd, 0x04, 0xa4, 0xc2, 0xdd, 0xcc, 0xee, 0x39, 0xb3,
|
||||
0xb3, 0x67, 0xce, 0xda, 0x70, 0x30, 0x49, 0x46, 0x3c, 0xeb, 0x9f, 0x66, 0x7b, 0x45, 0xc0, 0xe9,
|
||||
0x34, 0x11, 0x92, 0xcf, 0xfa, 0x82, 0xf8, 0x34, 0x19, 0x51, 0x3f, 0xe7, 0x99, 0x5c, 0x2c, 0xf7,
|
||||
0x74, 0x8a, 0xff, 0x9f, 0x66, 0x3d, 0x8d, 0xef, 0x95, 0x1b, 0xe1, 0x4f, 0x17, 0x1a, 0x6f, 0x0a,
|
||||
0x0e, 0x22, 0x54, 0xd3, 0x68, 0x42, 0xbe, 0xd3, 0x71, 0xba, 0x2d, 0xa6, 0x63, 0xf4, 0xa1, 0x31,
|
||||
0x25, 0x2e, 0x92, 0x2c, 0xf5, 0x5d, 0xbd, 0x5c, 0xa6, 0x78, 0x08, 0xcd, 0x09, 0xc9, 0x28, 0x8e,
|
||||
0x64, 0xe4, 0x7b, 0x1d, 0xaf, 0xdb, 0x1e, 0x74, 0x7b, 0x17, 0xea, 0xf7, 0x4c, 0xed, 0xde, 0x89,
|
||||
0x81, 0x0e, 0x53, 0xc9, 0x67, 0x6c, 0xce, 0xc4, 0x47, 0xd0, 0xa2, 0x34, 0xce, 0xb3, 0x24, 0x95,
|
||||
0xc2, 0xaf, 0xea, 0x32, 0x3b, 0x96, 0x32, 0x43, 0x83, 0x61, 0x0b, 0x34, 0xee, 0x41, 0x2d, 0xcd,
|
||||
0x62, 0x12, 0x7e, 0x4d, 0xd3, 0xae, 0x59, 0x68, 0x2f, 0xb3, 0x98, 0x58, 0x81, 0xc2, 0x03, 0x68,
|
||||
0x64, 0xb9, 0x4c, 0xb2, 0x54, 0xf8, 0xf5, 0x8e, 0xd3, 0x6d, 0x0f, 0x02, 0x0b, 0xe1, 0x55, 0x81,
|
||||
0x60, 0x25, 0x34, 0x78, 0x02, 0x9b, 0xe7, 0x5a, 0xc7, 0x6d, 0xf0, 0x3e, 0xd3, 0xcc, 0x68, 0xa4,
|
||||
0x42, 0xbc, 0x02, 0xb5, 0x69, 0x34, 0x3e, 0x23, 0x23, 0x50, 0x91, 0x3c, 0x76, 0x1f, 0x3a, 0xe1,
|
||||
0x0f, 0x07, 0xaa, 0xaa, 0x05, 0xdc, 0x02, 0x37, 0x89, 0x0d, 0xc7, 0x4d, 0x62, 0xa5, 0x6a, 0x14,
|
||||
0xc7, 0x9c, 0x84, 0x28, 0x55, 0x35, 0xa9, 0x9a, 0x41, 0x9e, 0x71, 0xe9, 0x7b, 0x1d, 0xa7, 0xeb,
|
||||
0x31, 0x1d, 0xe3, 0xd3, 0x25, 0xa5, 0x0b, 0x89, 0x6e, 0xaf, 0xb8, 0xeb, 0x2a, 0x99, 0x2f, 0x77,
|
||||
0x8d, 0xaf, 0x2e, 0x34, 0xcb, 0x01, 0x58, 0x4d, 0x32, 0x80, 0x06, 0xa7, 0x2f, 0x67, 0x24, 0xa4,
|
||||
0x26, 0xb7, 0x07, 0xbe, 0xa5, 0xbf, 0xf7, 0xaa, 0x1e, 0x2b, 0x81, 0x78, 0x00, 0x4d, 0x4e, 0x22,
|
||||
0xcf, 0x52, 0x41, 0xfa, 0xb2, 0xeb, 0x48, 0x73, 0x24, 0x0e, 0x2f, 0x48, 0x71, 0x77, 0x8d, 0x5b,
|
||||
0xfe, 0x8d, 0x1c, 0x11, 0xd4, 0x74, 0x5b, 0x56, 0x29, 0x10, 0xaa, 0x72, 0x96, 0x97, 0x2c, 0x1d,
|
||||
0xe3, 0x3e, 0xd4, 0x35, 0x5b, 0x98, 0x77, 0xb2, 0xfa, 0xa2, 0x06, 0x17, 0xee, 0x40, 0xc3, 0x38,
|
||||
0x51, 0x75, 0x26, 0xe5, 0x58, 0x9f, 0xe1, 0x31, 0x15, 0x86, 0x12, 0xea, 0x8c, 0xc4, 0xd9, 0x58,
|
||||
0xe2, 0x55, 0xa8, 0x47, 0x23, 0x05, 0x33, 0x2d, 0x98, 0x4c, 0x59, 0xdd, 0x7c, 0x07, 0xcc, 0x3c,
|
||||
0x82, 0xd5, 0x2f, 0x93, 0x95, 0x50, 0xdc, 0x85, 0x96, 0x4c, 0x26, 0x24, 0x64, 0x34, 0xc9, 0x8d,
|
||||
0xff, 0x16, 0x0b, 0xe1, 0x7f, 0xb0, 0x39, 0x9c, 0xe4, 0x72, 0xc6, 0xcc, 0x28, 0xc2, 0x3b, 0x00,
|
||||
0x47, 0x24, 0x99, 0x19, 0xa7, 0xbf, 0x38, 0xb2, 0xe8, 0xa5, 0x4c, 0xc3, 0x21, 0xb4, 0x35, 0xce,
|
||||
0x4c, 0xf0, 0x01, 0x34, 0xcd, 0x8e, 0xf0, 0x1d, 0x2d, 0xc7, 0xba, 0xe6, 0xe6, 0xd8, 0x70, 0x13,
|
||||
0xda, 0xc7, 0x89, 0x28, 0xcf, 0x0b, 0x9f, 0xc3, 0x46, 0x91, 0x5e, 0xb2, 0x6c, 0x17, 0x36, 0x3e,
|
||||
0x44, 0x72, 0xf4, 0xe9, 0xf7, 0xf7, 0xf8, 0xee, 0x40, 0x6d, 0x38, 0xa5, 0x54, 0x5e, 0x78, 0xcd,
|
||||
0xfb, 0x4b, 0x33, 0xdf, 0x1a, 0xec, 0xda, 0x0c, 0xa9, 0x78, 0x6f, 0x67, 0x39, 0x19, 0x47, 0xac,
|
||||
0x95, 0x7a, 0x79, 0x7c, 0xd5, 0x3f, 0x1e, 0xdf, 0xbd, 0x3e, 0xb4, 0xe6, 0xc7, 0x20, 0x40, 0xfd,
|
||||
0x19, 0xa7, 0x48, 0xd2, 0x76, 0x45, 0xc5, 0x87, 0x34, 0x26, 0x49, 0xdb, 0x8e, 0x8a, 0xdf, 0xe5,
|
||||
0xb1, 0x5a, 0x77, 0x07, 0xdf, 0x3c, 0x68, 0x32, 0x53, 0x0e, 0x4f, 0xf4, 0x34, 0xcb, 0x3f, 0xc1,
|
||||
0x0d, 0xcb, 0x81, 0x8b, 0x61, 0x07, 0x37, 0x57, 0x6d, 0x1b, 0x6b, 0x54, 0xf0, 0x45, 0x59, 0x9a,
|
||||
0x38, 0xae, 0xe9, 0x3e, 0xe8, 0xd8, 0xc4, 0x3a, 0x67, 0xb3, 0x0a, 0x1e, 0x03, 0x1c, 0x12, 0xff,
|
||||
0x5b, 0xd5, 0x5e, 0x17, 0xc6, 0x31, 0x14, 0x81, 0xb6, 0xbb, 0x2c, 0x19, 0x2d, 0xb8, 0xb5, 0x72,
|
||||
0x7f, 0x5e, 0xf2, 0x08, 0x6a, 0xda, 0x43, 0x68, 0xc3, 0x2e, 0xbb, 0x2b, 0xb8, 0x6e, 0x01, 0x14,
|
||||
0x6f, 0x39, 0xac, 0xec, 0x3b, 0x1f, 0xeb, 0xfa, 0x37, 0x7d, 0xff, 0x57, 0x00, 0x00, 0x00, 0xff,
|
||||
0xff, 0x69, 0x33, 0x08, 0xdb, 0xde, 0x07, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// RegistryClient is the client API for Registry service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type RegistryClient interface {
|
||||
GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error)
|
||||
Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error)
|
||||
Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error)
|
||||
ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
|
||||
Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error)
|
||||
}
|
||||
|
||||
type registryClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewRegistryClient(cc *grpc.ClientConn) RegistryClient {
|
||||
return ®istryClient{cc}
|
||||
}
|
||||
|
||||
func (c *registryClient) GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
|
||||
out := new(GetResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/GetService", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *registryClient) Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) {
|
||||
out := new(EmptyResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Register", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *registryClient) Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) {
|
||||
out := new(EmptyResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Deregister", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *registryClient) ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
|
||||
out := new(ListResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/ListServices", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *registryClient) Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_Registry_serviceDesc.Streams[0], "/go.micro.registry.Registry/Watch", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := ®istryWatchClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Registry_WatchClient interface {
|
||||
Recv() (*Result, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type registryWatchClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *registryWatchClient) Recv() (*Result, error) {
|
||||
m := new(Result)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// RegistryServer is the server API for Registry service.
|
||||
type RegistryServer interface {
|
||||
GetService(context.Context, *GetRequest) (*GetResponse, error)
|
||||
Register(context.Context, *Service) (*EmptyResponse, error)
|
||||
Deregister(context.Context, *Service) (*EmptyResponse, error)
|
||||
ListServices(context.Context, *ListRequest) (*ListResponse, error)
|
||||
Watch(*WatchRequest, Registry_WatchServer) error
|
||||
}
|
||||
|
||||
func RegisterRegistryServer(s *grpc.Server, srv RegistryServer) {
|
||||
s.RegisterService(&_Registry_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Registry_GetService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RegistryServer).GetService(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.registry.Registry/GetService",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RegistryServer).GetService(ctx, req.(*GetRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Registry_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Service)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RegistryServer).Register(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.registry.Registry/Register",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RegistryServer).Register(ctx, req.(*Service))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Registry_Deregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Service)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RegistryServer).Deregister(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.registry.Registry/Deregister",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RegistryServer).Deregister(ctx, req.(*Service))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Registry_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RegistryServer).ListServices(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.registry.Registry/ListServices",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RegistryServer).ListServices(ctx, req.(*ListRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Registry_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(WatchRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(RegistryServer).Watch(m, ®istryWatchServer{stream})
|
||||
}
|
||||
|
||||
type Registry_WatchServer interface {
|
||||
Send(*Result) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type registryWatchServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *registryWatchServer) Send(m *Result) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
var _Registry_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.registry.Registry",
|
||||
HandlerType: (*RegistryServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetService",
|
||||
Handler: _Registry_GetService_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Register",
|
||||
Handler: _Registry_Register_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Deregister",
|
||||
Handler: _Registry_Deregister_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListServices",
|
||||
Handler: _Registry_ListServices_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Watch",
|
||||
Handler: _Registry_Watch_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "micro/go-micro/registry/service/proto/registry.proto",
|
||||
}
|
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/registry"
|
||||
pb "github.com/micro/go-micro/registry/proto"
|
||||
pb "github.com/micro/go-micro/registry/service/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@@ -2,7 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/registry"
|
||||
pb "github.com/micro/go-micro/registry/proto"
|
||||
pb "github.com/micro/go-micro/registry/service/proto"
|
||||
)
|
||||
|
||||
func values(v []*registry.Value) []*pb.Value {
|
||||
|
@@ -2,7 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/registry"
|
||||
pb "github.com/micro/go-micro/registry/proto"
|
||||
pb "github.com/micro/go-micro/registry/service/proto"
|
||||
)
|
||||
|
||||
type serviceWatcher struct {
|
||||
|
@@ -319,23 +319,13 @@ func (r *router) advertiseTable() error {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// list routing table routes to announce
|
||||
routes, err := r.table.List()
|
||||
// do full table flush
|
||||
events, err := r.flushRouteEvents(Update)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed listing routes: %s", err)
|
||||
}
|
||||
// collect all the added routes before we attempt to add default gateway
|
||||
events := make([]*Event, len(routes))
|
||||
for i, route := range routes {
|
||||
event := &Event{
|
||||
Type: Update,
|
||||
Timestamp: time.Now(),
|
||||
Route: route,
|
||||
}
|
||||
events[i] = event
|
||||
return fmt.Errorf("failed flushing routes: %s", err)
|
||||
}
|
||||
|
||||
// advertise all routes as Update events to subscribers
|
||||
// advertise routes to subscribers
|
||||
if len(events) > 0 {
|
||||
log.Debugf("Router flushing table with %d events: %s", len(events), r.options.Id)
|
||||
r.advertWg.Add(1)
|
||||
@@ -692,15 +682,58 @@ func (r *router) flushRouteEvents(evType EventType) ([]*Event, error) {
|
||||
return nil, fmt.Errorf("failed listing routes: %s", err)
|
||||
}
|
||||
|
||||
if r.options.Advertise == AdvertiseAll {
|
||||
// build a list of events to advertise
|
||||
events := make([]*Event, len(routes))
|
||||
for i, route := range routes {
|
||||
event := &Event{
|
||||
Type: evType,
|
||||
Timestamp: time.Now(),
|
||||
Route: route,
|
||||
}
|
||||
events[i] = event
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
// routeMap stores optimal routes per service
|
||||
bestRoutes := make(map[string]Route)
|
||||
|
||||
// go through all routes found in the routing table and collapse them to optimal routes
|
||||
for _, route := range routes {
|
||||
routeKey := route.Service + "@" + route.Network
|
||||
optimal, ok := bestRoutes[routeKey]
|
||||
if !ok {
|
||||
bestRoutes[routeKey] = route
|
||||
continue
|
||||
}
|
||||
// if the current optimal route metric is higher than routing table route, replace it
|
||||
if optimal.Metric > route.Metric {
|
||||
bestRoutes[routeKey] = route
|
||||
continue
|
||||
}
|
||||
// if the metrics are the same, prefer advertising your own route
|
||||
if optimal.Metric == route.Metric {
|
||||
if route.Router == r.options.Id {
|
||||
bestRoutes[routeKey] = route
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Router advertising %d best routes out of %d", len(bestRoutes), len(routes))
|
||||
|
||||
// build a list of events to advertise
|
||||
events := make([]*Event, len(routes))
|
||||
for i, route := range routes {
|
||||
events := make([]*Event, len(bestRoutes))
|
||||
i := 0
|
||||
for _, route := range bestRoutes {
|
||||
event := &Event{
|
||||
Type: evType,
|
||||
Timestamp: time.Now(),
|
||||
Route: route,
|
||||
}
|
||||
events[i] = event
|
||||
i++
|
||||
}
|
||||
|
||||
return events, nil
|
||||
@@ -722,8 +755,8 @@ func (r *router) Solicit() error {
|
||||
}
|
||||
|
||||
// Lookup routes in the routing table
|
||||
func (r *router) Lookup(q Query) ([]Route, error) {
|
||||
return r.table.Query(q)
|
||||
func (r *router) Lookup(q ...QueryOption) ([]Route, error) {
|
||||
return r.table.Query(q...)
|
||||
}
|
||||
|
||||
// Watch routes
|
||||
|
@@ -17,11 +17,7 @@ type Router struct {
|
||||
|
||||
// Lookup looks up routes in the routing table and returns them
|
||||
func (r *Router) Lookup(ctx context.Context, req *pb.LookupRequest, resp *pb.LookupResponse) error {
|
||||
query := router.NewQuery(
|
||||
router.QueryService(req.Query.Service),
|
||||
)
|
||||
|
||||
routes, err := r.Router.Lookup(query)
|
||||
routes, err := r.Router.Lookup(router.QueryService(req.Query.Service))
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.router", "failed to lookup routes: %v", err)
|
||||
}
|
||||
|
@@ -90,11 +90,7 @@ func (t *Table) List(ctx context.Context, req *pb.Request, resp *pb.ListResponse
|
||||
}
|
||||
|
||||
func (t *Table) Query(ctx context.Context, req *pb.QueryRequest, resp *pb.QueryResponse) error {
|
||||
query := router.NewQuery(
|
||||
router.QueryService(req.Query.Service),
|
||||
)
|
||||
|
||||
routes, err := t.Router.Table().Query(query)
|
||||
routes, err := t.Router.Table().Query(router.QueryService(req.Query.Service))
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.router", "failed to lookup routes: %s", err)
|
||||
}
|
||||
|
@@ -18,6 +18,8 @@ type Options struct {
|
||||
Network string
|
||||
// Registry is the local registry
|
||||
Registry registry.Registry
|
||||
// Advertise is the advertising strategy
|
||||
Advertise Strategy
|
||||
// Client for calling router
|
||||
Client client.Client
|
||||
}
|
||||
@@ -64,12 +66,20 @@ func Registry(r registry.Registry) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy sets route advertising strategy
|
||||
func Advertise(a Strategy) Option {
|
||||
return func(o *Options) {
|
||||
o.Advertise = a
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultOptions returns router default options
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
Id: uuid.New().String(),
|
||||
Address: DefaultAddress,
|
||||
Network: DefaultNetwork,
|
||||
Registry: registry.DefaultRegistry,
|
||||
Id: uuid.New().String(),
|
||||
Address: DefaultAddress,
|
||||
Network: DefaultNetwork,
|
||||
Registry: registry.DefaultRegistry,
|
||||
Advertise: AdvertiseBest,
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@ type QueryOption func(*QueryOptions)
|
||||
type QueryOptions struct {
|
||||
// Service is destination service name
|
||||
Service string
|
||||
// Address of the service
|
||||
Address string
|
||||
// Gateway is route gateway
|
||||
Gateway string
|
||||
// Network is network address
|
||||
@@ -22,6 +24,13 @@ func QueryService(s string) QueryOption {
|
||||
}
|
||||
}
|
||||
|
||||
// QueryAddress sets service to query
|
||||
func QueryAddress(a string) QueryOption {
|
||||
return func(o *QueryOptions) {
|
||||
o.Address = a
|
||||
}
|
||||
}
|
||||
|
||||
// QueryGateway sets gateway address to query
|
||||
func QueryGateway(g string) QueryOption {
|
||||
return func(o *QueryOptions) {
|
||||
@@ -43,22 +52,12 @@ func QueryRouter(r string) QueryOption {
|
||||
}
|
||||
}
|
||||
|
||||
// Query is routing table query
|
||||
type Query interface {
|
||||
// Options returns query options
|
||||
Options() QueryOptions
|
||||
}
|
||||
|
||||
// query is a basic implementation of Query
|
||||
type query struct {
|
||||
opts QueryOptions
|
||||
}
|
||||
|
||||
// NewQuery creates new query and returns it
|
||||
func NewQuery(opts ...QueryOption) Query {
|
||||
func NewQuery(opts ...QueryOption) QueryOptions {
|
||||
// default options
|
||||
qopts := QueryOptions{
|
||||
Service: "*",
|
||||
Address: "*",
|
||||
Gateway: "*",
|
||||
Network: "*",
|
||||
Router: "*",
|
||||
@@ -68,12 +67,5 @@ func NewQuery(opts ...QueryOption) Query {
|
||||
o(&qopts)
|
||||
}
|
||||
|
||||
return &query{
|
||||
opts: qopts,
|
||||
}
|
||||
}
|
||||
|
||||
// Options returns query options
|
||||
func (q *query) Options() QueryOptions {
|
||||
return q.opts
|
||||
return qopts
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ type Router interface {
|
||||
// Solicit advertises the whole routing table to the network
|
||||
Solicit() error
|
||||
// Lookup queries routes in the routing table
|
||||
Lookup(Query) ([]Route, error)
|
||||
Lookup(...QueryOption) ([]Route, error)
|
||||
// Watch returns a watcher which tracks updates to the routing table
|
||||
Watch(opts ...WatchOption) (Watcher, error)
|
||||
// Start starts the router
|
||||
@@ -55,7 +55,7 @@ type Table interface {
|
||||
// List all routes in the table
|
||||
List() ([]Route, error)
|
||||
// Query routes in the routing table
|
||||
Query(Query) ([]Route, error)
|
||||
Query(...QueryOption) ([]Route, error)
|
||||
}
|
||||
|
||||
// Option used by the router
|
||||
@@ -139,6 +139,28 @@ type Advert struct {
|
||||
Events []*Event
|
||||
}
|
||||
|
||||
// Strategy is route advertisement strategy
|
||||
type Strategy int
|
||||
|
||||
const (
|
||||
// AdvertiseAll advertises all routes to the network
|
||||
AdvertiseAll Strategy = iota
|
||||
// AdvertiseBest advertises optimal routes to the network
|
||||
AdvertiseBest
|
||||
)
|
||||
|
||||
// String returns human readable Strategy
|
||||
func (s Strategy) String() string {
|
||||
switch s {
|
||||
case AdvertiseAll:
|
||||
return "all"
|
||||
case AdvertiseBest:
|
||||
return "best"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// NewRouter creates new Router and returns it
|
||||
func NewRouter(opts ...Option) Router {
|
||||
return newRouter(opts...)
|
||||
|
@@ -321,13 +321,15 @@ func (s *svc) Stop() error {
|
||||
}
|
||||
|
||||
// Lookup looks up routes in the routing table and returns them
|
||||
func (s *svc) Lookup(q router.Query) ([]router.Route, error) {
|
||||
func (s *svc) Lookup(q ...router.QueryOption) ([]router.Route, error) {
|
||||
// call the router
|
||||
query := router.NewQuery(q...)
|
||||
|
||||
resp, err := s.router.Lookup(context.Background(), &pb.LookupRequest{
|
||||
Query: &pb.Query{
|
||||
Service: q.Options().Service,
|
||||
Gateway: q.Options().Gateway,
|
||||
Network: q.Options().Network,
|
||||
Service: query.Service,
|
||||
Gateway: query.Gateway,
|
||||
Network: query.Network,
|
||||
},
|
||||
}, s.callOpts...)
|
||||
|
||||
|
@@ -90,13 +90,15 @@ func (t *table) List() ([]router.Route, error) {
|
||||
}
|
||||
|
||||
// Lookup looks up routes in the routing table and returns them
|
||||
func (t *table) Query(q router.Query) ([]router.Route, error) {
|
||||
func (t *table) Query(q ...router.QueryOption) ([]router.Route, error) {
|
||||
query := router.NewQuery(q...)
|
||||
|
||||
// call the router
|
||||
resp, err := t.table.Query(context.Background(), &pb.QueryRequest{
|
||||
Query: &pb.Query{
|
||||
Service: q.Options().Service,
|
||||
Gateway: q.Options().Gateway,
|
||||
Network: q.Options().Network,
|
||||
Service: query.Service,
|
||||
Gateway: query.Gateway,
|
||||
Network: query.Network,
|
||||
},
|
||||
}, t.callOpts...)
|
||||
|
||||
|
@@ -135,22 +135,44 @@ func (t *table) List() ([]Route, error) {
|
||||
}
|
||||
|
||||
// isMatch checks if the route matches given query options
|
||||
func isMatch(route Route, gateway, network, router string) bool {
|
||||
if gateway == "*" || gateway == route.Gateway {
|
||||
if network == "*" || network == route.Network {
|
||||
if router == "*" || router == route.Router {
|
||||
return true
|
||||
}
|
||||
func isMatch(route Route, address, gateway, network, router string) bool {
|
||||
// matches the values provided
|
||||
match := func(a, b string) bool {
|
||||
if a == "*" || a == b {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// a simple struct to hold our values
|
||||
type compare struct {
|
||||
a string
|
||||
b string
|
||||
}
|
||||
|
||||
// compare the following values
|
||||
values := []compare{
|
||||
{gateway, route.Gateway},
|
||||
{network, route.Network},
|
||||
{router, route.Router},
|
||||
{address, route.Address},
|
||||
}
|
||||
|
||||
for _, v := range values {
|
||||
// attempt to match each value
|
||||
if !match(v.a, v.b) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// findRoutes finds all the routes for given network and router and returns them
|
||||
func findRoutes(routes map[uint64]Route, gateway, network, router string) []Route {
|
||||
func findRoutes(routes map[uint64]Route, address, gateway, network, router string) []Route {
|
||||
var results []Route
|
||||
for _, route := range routes {
|
||||
if isMatch(route, gateway, network, router) {
|
||||
if isMatch(route, address, gateway, network, router) {
|
||||
results = append(results, route)
|
||||
}
|
||||
}
|
||||
@@ -158,21 +180,24 @@ func findRoutes(routes map[uint64]Route, gateway, network, router string) []Rout
|
||||
}
|
||||
|
||||
// Lookup queries routing table and returns all routes that match the lookup query
|
||||
func (t *table) Query(q Query) ([]Route, error) {
|
||||
func (t *table) Query(q ...QueryOption) ([]Route, error) {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
|
||||
if q.Options().Service != "*" {
|
||||
if _, ok := t.routes[q.Options().Service]; !ok {
|
||||
// create new query options
|
||||
opts := NewQuery(q...)
|
||||
|
||||
if opts.Service != "*" {
|
||||
if _, ok := t.routes[opts.Service]; !ok {
|
||||
return nil, ErrRouteNotFound
|
||||
}
|
||||
return findRoutes(t.routes[q.Options().Service], q.Options().Gateway, q.Options().Network, q.Options().Router), nil
|
||||
return findRoutes(t.routes[opts.Service], opts.Address, opts.Gateway, opts.Network, opts.Router), nil
|
||||
}
|
||||
|
||||
var results []Route
|
||||
// search through all destinations
|
||||
for _, routes := range t.routes {
|
||||
results = append(results, findRoutes(routes, q.Options().Gateway, q.Options().Network, q.Options().Router)...)
|
||||
results = append(results, findRoutes(routes, opts.Address, opts.Gateway, opts.Network, opts.Router)...)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
@@ -123,18 +123,15 @@ func TestQuery(t *testing.T) {
|
||||
}
|
||||
|
||||
// return all routes
|
||||
query := NewQuery()
|
||||
|
||||
routes, err := table.Query(query)
|
||||
routes, err := table.Query()
|
||||
if err != nil {
|
||||
t.Errorf("error looking up routes: %s", err)
|
||||
}
|
||||
|
||||
// query routes particular network
|
||||
network := "net1"
|
||||
query = NewQuery(QueryNetwork(network))
|
||||
|
||||
routes, err = table.Query(query)
|
||||
routes, err = table.Query(QueryNetwork(network))
|
||||
if err != nil {
|
||||
t.Errorf("error looking up routes: %s", err)
|
||||
}
|
||||
@@ -151,9 +148,8 @@ func TestQuery(t *testing.T) {
|
||||
|
||||
// query routes for particular gateway
|
||||
gateway := "gw1"
|
||||
query = NewQuery(QueryGateway(gateway))
|
||||
|
||||
routes, err = table.Query(query)
|
||||
routes, err = table.Query(QueryGateway(gateway))
|
||||
if err != nil {
|
||||
t.Errorf("error looking up routes: %s", err)
|
||||
}
|
||||
@@ -168,9 +164,8 @@ func TestQuery(t *testing.T) {
|
||||
|
||||
// query routes for particular router
|
||||
router := "rtr1"
|
||||
query = NewQuery(QueryRouter(router))
|
||||
|
||||
routes, err = table.Query(query)
|
||||
routes, err = table.Query(QueryRouter(router))
|
||||
if err != nil {
|
||||
t.Errorf("error looking up routes: %s", err)
|
||||
}
|
||||
@@ -184,13 +179,13 @@ func TestQuery(t *testing.T) {
|
||||
}
|
||||
|
||||
// query particular gateway and network
|
||||
query = NewQuery(
|
||||
query := []QueryOption{
|
||||
QueryGateway(gateway),
|
||||
QueryNetwork(network),
|
||||
QueryRouter(router),
|
||||
)
|
||||
}
|
||||
|
||||
routes, err = table.Query(query)
|
||||
routes, err = table.Query(query...)
|
||||
if err != nil {
|
||||
t.Errorf("error looking up routes: %s", err)
|
||||
}
|
||||
@@ -212,9 +207,7 @@ func TestQuery(t *testing.T) {
|
||||
}
|
||||
|
||||
// non-existen route query
|
||||
query = NewQuery(QueryService("foobar"))
|
||||
|
||||
routes, err = table.Query(query)
|
||||
routes, err = table.Query(QueryService("foobar"))
|
||||
if err != ErrRouteNotFound {
|
||||
t.Errorf("error looking up routes. Expected: %s, found: %s", ErrRouteNotFound, err)
|
||||
}
|
||||
|
@@ -37,6 +37,10 @@ func newService(opts ...Option) Service {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) Name() string {
|
||||
return s.opts.Server.Options().Name
|
||||
}
|
||||
|
||||
// Init initialises options. Additionally it calls cmd.Init
|
||||
// which parses command line flags. cmd.Init is only called
|
||||
// on first Init.
|
||||
|
@@ -6,11 +6,20 @@ import (
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// Service is an interface for a micro service
|
||||
type Service interface {
|
||||
// The service name
|
||||
Name() string
|
||||
// Init initialises options
|
||||
Init(...Option)
|
||||
// Options returns the current options
|
||||
Options() Options
|
||||
// Client is used to call services
|
||||
Client() client.Client
|
||||
// Server is for handling requests and events
|
||||
Server() server.Server
|
||||
// Run the service
|
||||
Run() error
|
||||
// The service implementation
|
||||
String() string
|
||||
}
|
||||
|
119
store/cloudflare/cloudflare.go
Normal file
119
store/cloudflare/cloudflare.go
Normal file
@@ -0,0 +1,119 @@
|
||||
// Package cloudflare is a store implementation backed by cloudflare workers kv
|
||||
// Note that the cloudflare workers KV API is eventually consistent.
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cloudflare-go"
|
||||
"github.com/micro/go-micro/config/options"
|
||||
"github.com/micro/go-micro/store"
|
||||
)
|
||||
|
||||
var namespaceUUID string
|
||||
|
||||
type workersKV struct {
|
||||
options.Options
|
||||
api *cloudflare.API
|
||||
}
|
||||
|
||||
// New returns a cloudflare Store implementation.
|
||||
// Options expects CF_API_TOKEN to a cloudflare API token scoped to Workers KV,
|
||||
// CF_ACCOUNT_ID to contain a string with your cloudflare account ID and
|
||||
// KV_NAMESPACE_ID to contain the namespace UUID for your KV storage.
|
||||
func New(opts ...options.Option) (store.Store, error) {
|
||||
// Validate Options
|
||||
options := options.NewOptions(opts...)
|
||||
apiToken, ok := options.Values().Get("CF_API_TOKEN")
|
||||
if !ok {
|
||||
log.Fatal("Store: No CF_API_TOKEN passed as an option")
|
||||
}
|
||||
apiTokenString, ok := apiToken.(string)
|
||||
if !ok {
|
||||
log.Fatal("Store: Option CF_API_TOKEN contains a non-string")
|
||||
}
|
||||
accountID, ok := options.Values().Get("CF_ACCOUNT_ID")
|
||||
if !ok {
|
||||
log.Fatal("Store: No CF_ACCOUNT_ID passed as an option")
|
||||
}
|
||||
accountIDString, ok := accountID.(string)
|
||||
if !ok {
|
||||
log.Fatal("Store: Option CF_ACCOUNT_ID contains a non-string")
|
||||
}
|
||||
uuid, ok := options.Values().Get("KV_NAMESPACE_ID")
|
||||
if !ok {
|
||||
log.Fatal("Store: No KV_NAMESPACE_ID passed as an option")
|
||||
}
|
||||
namespaceUUID, ok = uuid.(string)
|
||||
if !ok {
|
||||
log.Fatal("Store: Option KV_NAMESPACE_ID contains a non-string")
|
||||
}
|
||||
|
||||
// Create API client
|
||||
api, err := cloudflare.NewWithAPIToken(apiTokenString, cloudflare.UsingAccount(accountIDString))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &workersKV{
|
||||
Options: options,
|
||||
api: api,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// In the cloudflare workers KV implemention, Sync() doesn't guarantee
|
||||
// anything as the workers API is eventually consistent.
|
||||
func (w *workersKV) Sync() ([]*store.Record, error) {
|
||||
response, err := w.api.ListWorkersKVs(context.Background(), namespaceUUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var keys []string
|
||||
for _, r := range response.Result {
|
||||
keys = append(keys, r.Name)
|
||||
}
|
||||
return w.Read(keys...)
|
||||
}
|
||||
|
||||
func (w *workersKV) Read(keys ...string) ([]*store.Record, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var records []*store.Record
|
||||
for _, k := range keys {
|
||||
v, err := w.api.ReadWorkersKV(ctx, namespaceUUID, k)
|
||||
if err != nil {
|
||||
return records, err
|
||||
}
|
||||
records = append(records, &store.Record{
|
||||
Key: k,
|
||||
Value: v,
|
||||
})
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (w *workersKV) Write(records ...*store.Record) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
for _, r := range records {
|
||||
if _, err := w.api.WriteWorkersKV(ctx, namespaceUUID, r.Key, r.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *workersKV) Delete(keys ...string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
for _, k := range keys {
|
||||
if _, err := w.api.DeleteWorkersKV(ctx, namespaceUUID, k); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
66
store/cloudflare/cloudflare_test.go
Normal file
66
store/cloudflare/cloudflare_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/config/options"
|
||||
"github.com/micro/go-micro/store"
|
||||
)
|
||||
|
||||
func TestCloudflare(t *testing.T) {
|
||||
apiToken, accountID := os.Getenv("CF_API_TOKEN"), os.Getenv("CF_ACCOUNT_ID")
|
||||
kvID := os.Getenv("KV_NAMESPACE_ID")
|
||||
if len(apiToken) == 0 || len(accountID) == 0 || len(kvID) == 0 {
|
||||
t.Skip("No Cloudflare API keys available, skipping test")
|
||||
}
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
randomK := strconv.Itoa(rand.Int())
|
||||
randomV := strconv.Itoa(rand.Int())
|
||||
|
||||
wkv, err := New(
|
||||
options.WithValue("CF_API_TOKEN", apiToken),
|
||||
options.WithValue("CF_ACCOUNT_ID", accountID),
|
||||
options.WithValue("KV_NAMESPACE_ID", kvID),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
_, err = wkv.Sync()
|
||||
if err != nil {
|
||||
t.Fatalf("Sync: %s\n", err.Error())
|
||||
}
|
||||
|
||||
err = wkv.Write(&store.Record{
|
||||
Key: randomK,
|
||||
Value: []byte(randomV),
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Write: %s", err.Error())
|
||||
}
|
||||
|
||||
// This might be needed for cloudflare eventual consistency
|
||||
time.Sleep(1 * time.Minute)
|
||||
|
||||
r, err := wkv.Read(randomK)
|
||||
if err != nil {
|
||||
t.Errorf("Read: %s\n", err.Error())
|
||||
}
|
||||
if len(r) != 1 {
|
||||
t.Errorf("Expected to read 1 key, got %d keys\n", len(r))
|
||||
}
|
||||
if string(r[0].Value) != randomV {
|
||||
t.Errorf("Read: expected %s, got %s\n", randomK, string(r[0].Value))
|
||||
}
|
||||
|
||||
err = wkv.Delete(randomK)
|
||||
if err != nil {
|
||||
t.Errorf("Delete: %s\n", err.Error())
|
||||
}
|
||||
|
||||
}
|
118
store/etcd/etcd.go
Normal file
118
store/etcd/etcd.go
Normal file
@@ -0,0 +1,118 @@
|
||||
// Package etcd is an etcd v3 implementation of kv
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
client "github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
"github.com/micro/go-micro/config/options"
|
||||
"github.com/micro/go-micro/store"
|
||||
)
|
||||
|
||||
type ekv struct {
|
||||
options.Options
|
||||
kv client.KV
|
||||
}
|
||||
|
||||
func (e *ekv) Read(keys ...string) ([]*store.Record, error) {
|
||||
var values []*mvccpb.KeyValue
|
||||
|
||||
for _, key := range keys {
|
||||
keyval, err := e.kv.Get(context.Background(), key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if keyval == nil || len(keyval.Kvs) == 0 {
|
||||
return nil, store.ErrNotFound
|
||||
}
|
||||
|
||||
values = append(values, keyval.Kvs...)
|
||||
}
|
||||
|
||||
var records []*store.Record
|
||||
|
||||
for _, kv := range values {
|
||||
records = append(records, &store.Record{
|
||||
Key: string(kv.Key),
|
||||
Value: kv.Value,
|
||||
// TODO: implement expiry
|
||||
})
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (e *ekv) Delete(keys ...string) error {
|
||||
var gerr error
|
||||
for _, key := range keys {
|
||||
_, err := e.kv.Delete(context.Background(), key)
|
||||
if err != nil {
|
||||
gerr = err
|
||||
}
|
||||
}
|
||||
return gerr
|
||||
}
|
||||
|
||||
func (e *ekv) Write(records ...*store.Record) error {
|
||||
var gerr error
|
||||
for _, record := range records {
|
||||
// TODO create lease to expire keys
|
||||
_, err := e.kv.Put(context.Background(), record.Key, string(record.Value))
|
||||
if err != nil {
|
||||
gerr = err
|
||||
}
|
||||
}
|
||||
return gerr
|
||||
}
|
||||
|
||||
func (e *ekv) Sync() ([]*store.Record, error) {
|
||||
keyval, err := e.kv.Get(context.Background(), "/", client.WithPrefix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var vals []*store.Record
|
||||
if keyval == nil || len(keyval.Kvs) == 0 {
|
||||
return vals, nil
|
||||
}
|
||||
for _, keyv := range keyval.Kvs {
|
||||
vals = append(vals, &store.Record{
|
||||
Key: string(keyv.Key),
|
||||
Value: keyv.Value,
|
||||
})
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
func (e *ekv) String() string {
|
||||
return "etcd"
|
||||
}
|
||||
|
||||
func NewStore(opts ...options.Option) store.Store {
|
||||
options := options.NewOptions(opts...)
|
||||
|
||||
var endpoints []string
|
||||
|
||||
if e, ok := options.Values().Get("store.nodes"); ok {
|
||||
endpoints = e.([]string)
|
||||
}
|
||||
|
||||
if len(endpoints) == 0 {
|
||||
endpoints = []string{"http://127.0.0.1:2379"}
|
||||
}
|
||||
|
||||
// TODO: parse addresses
|
||||
c, err := client.New(client.Config{
|
||||
Endpoints: endpoints,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &ekv{
|
||||
Options: options,
|
||||
kv: client.NewKV(c),
|
||||
}
|
||||
}
|
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/config/options"
|
||||
"github.com/micro/go-micro/data/store"
|
||||
"github.com/micro/go-micro/store"
|
||||
)
|
||||
|
||||
type memoryStore struct {
|
||||
@@ -21,7 +21,7 @@ type memoryRecord struct {
|
||||
c time.Time
|
||||
}
|
||||
|
||||
func (m *memoryStore) Dump() ([]*store.Record, error) {
|
||||
func (m *memoryStore) Sync() ([]*store.Record, error) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
@@ -48,51 +48,61 @@ func (m *memoryStore) Dump() ([]*store.Record, error) {
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Read(key string) (*store.Record, error) {
|
||||
func (m *memoryStore) Read(keys ...string) ([]*store.Record, error) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
v, ok := m.values[key]
|
||||
if !ok {
|
||||
return nil, store.ErrNotFound
|
||||
}
|
||||
var records []*store.Record
|
||||
|
||||
// get expiry
|
||||
d := v.r.Expiry
|
||||
t := time.Since(v.c)
|
||||
|
||||
// expired
|
||||
if d > time.Duration(0) {
|
||||
if t > d {
|
||||
for _, key := range keys {
|
||||
v, ok := m.values[key]
|
||||
if !ok {
|
||||
return nil, store.ErrNotFound
|
||||
}
|
||||
// update expiry
|
||||
v.r.Expiry -= t
|
||||
v.c = time.Now()
|
||||
|
||||
// get expiry
|
||||
d := v.r.Expiry
|
||||
t := time.Since(v.c)
|
||||
|
||||
// expired
|
||||
if d > time.Duration(0) {
|
||||
if t > d {
|
||||
return nil, store.ErrNotFound
|
||||
}
|
||||
// update expiry
|
||||
v.r.Expiry -= t
|
||||
v.c = time.Now()
|
||||
}
|
||||
|
||||
records = append(records, v.r)
|
||||
}
|
||||
|
||||
return v.r, nil
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Write(r *store.Record) error {
|
||||
func (m *memoryStore) Write(records ...*store.Record) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// set the record
|
||||
m.values[r.Key] = &memoryRecord{
|
||||
r: r,
|
||||
c: time.Now(),
|
||||
for _, r := range records {
|
||||
// set the record
|
||||
m.values[r.Key] = &memoryRecord{
|
||||
r: r,
|
||||
c: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Delete(key string) error {
|
||||
func (m *memoryStore) Delete(keys ...string) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// delete the value
|
||||
delete(m.values, key)
|
||||
for _, key := range keys {
|
||||
// delete the value
|
||||
delete(m.values, key)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/data/store"
|
||||
"github.com/micro/go-micro/store"
|
||||
)
|
||||
|
||||
func TestReadRecordExpire(t *testing.T) {
|
||||
@@ -25,7 +25,7 @@ func TestReadRecordExpire(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if rrec.Expiry >= expire {
|
||||
if rrec[0].Expiry >= expire {
|
||||
t.Fatal("expiry of read record is not changed")
|
||||
}
|
||||
|
89
store/service/handler/handler.go
Normal file
89
store/service/handler/handler.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/store"
|
||||
pb "github.com/micro/go-micro/store/service/proto"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
Store store.Store
|
||||
}
|
||||
|
||||
func (s *Store) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadResponse) error {
|
||||
vals, err := s.Store.Read(req.Keys...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.store", err.Error())
|
||||
}
|
||||
for _, val := range vals {
|
||||
rsp.Records = append(rsp.Records, &pb.Record{
|
||||
Key: val.Key,
|
||||
Value: val.Value,
|
||||
Expiry: int64(val.Expiry.Seconds()),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) Write(ctx context.Context, req *pb.WriteRequest, rsp *pb.WriteResponse) error {
|
||||
var records []*store.Record
|
||||
|
||||
for _, record := range req.Records {
|
||||
records = append(records, &store.Record{
|
||||
Key: record.Key,
|
||||
Value: record.Value,
|
||||
Expiry: time.Duration(record.Expiry) * time.Second,
|
||||
})
|
||||
}
|
||||
|
||||
err := s.Store.Write(records...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.store", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) Delete(ctx context.Context, req *pb.DeleteRequest, rsp *pb.DeleteResponse) error {
|
||||
err := s.Store.Delete(req.Keys...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.store", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) Sync(ctx context.Context, req *pb.SyncRequest, stream pb.Store_SyncStream) error {
|
||||
var vals []*store.Record
|
||||
var err error
|
||||
|
||||
if len(req.Key) > 0 {
|
||||
vals, err = s.Store.Read(req.Key)
|
||||
} else {
|
||||
vals, err = s.Store.Sync()
|
||||
}
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.store", err.Error())
|
||||
}
|
||||
rsp := new(pb.SyncResponse)
|
||||
|
||||
// TODO: batch sync
|
||||
for _, val := range vals {
|
||||
rsp.Records = append(rsp.Records, &pb.Record{
|
||||
Key: val.Key,
|
||||
Value: val.Value,
|
||||
Expiry: int64(val.Expiry.Seconds()),
|
||||
})
|
||||
}
|
||||
|
||||
err = stream.Send(rsp)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.store", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
207
store/service/proto/store.micro.go
Normal file
207
store/service/proto/store.micro.go
Normal file
@@ -0,0 +1,207 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: micro/go-micro/store/service/proto/store.proto
|
||||
|
||||
package go_micro_store
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
client "github.com/micro/go-micro/client"
|
||||
server "github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Client API for Store service
|
||||
|
||||
type StoreService interface {
|
||||
Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error)
|
||||
Write(ctx context.Context, in *WriteRequest, opts ...client.CallOption) (*WriteResponse, error)
|
||||
Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error)
|
||||
Sync(ctx context.Context, in *SyncRequest, opts ...client.CallOption) (Store_SyncService, error)
|
||||
}
|
||||
|
||||
type storeService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewStoreService(name string, c client.Client) StoreService {
|
||||
if c == nil {
|
||||
c = client.NewClient()
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = "go.micro.store"
|
||||
}
|
||||
return &storeService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *storeService) Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Store.Read", in)
|
||||
out := new(ReadResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storeService) Write(ctx context.Context, in *WriteRequest, opts ...client.CallOption) (*WriteResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Store.Write", in)
|
||||
out := new(WriteResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storeService) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Store.Delete", in)
|
||||
out := new(DeleteResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storeService) Sync(ctx context.Context, in *SyncRequest, opts ...client.CallOption) (Store_SyncService, error) {
|
||||
req := c.c.NewRequest(c.name, "Store.Sync", &SyncRequest{})
|
||||
stream, err := c.c.Stream(ctx, req, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := stream.Send(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &storeServiceSync{stream}, nil
|
||||
}
|
||||
|
||||
type Store_SyncService interface {
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Recv() (*SyncResponse, error)
|
||||
}
|
||||
|
||||
type storeServiceSync struct {
|
||||
stream client.Stream
|
||||
}
|
||||
|
||||
func (x *storeServiceSync) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *storeServiceSync) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *storeServiceSync) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *storeServiceSync) Recv() (*SyncResponse, error) {
|
||||
m := new(SyncResponse)
|
||||
err := x.stream.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Server API for Store service
|
||||
|
||||
type StoreHandler interface {
|
||||
Read(context.Context, *ReadRequest, *ReadResponse) error
|
||||
Write(context.Context, *WriteRequest, *WriteResponse) error
|
||||
Delete(context.Context, *DeleteRequest, *DeleteResponse) error
|
||||
Sync(context.Context, *SyncRequest, Store_SyncStream) error
|
||||
}
|
||||
|
||||
func RegisterStoreHandler(s server.Server, hdlr StoreHandler, opts ...server.HandlerOption) error {
|
||||
type store interface {
|
||||
Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error
|
||||
Write(ctx context.Context, in *WriteRequest, out *WriteResponse) error
|
||||
Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error
|
||||
Sync(ctx context.Context, stream server.Stream) error
|
||||
}
|
||||
type Store struct {
|
||||
store
|
||||
}
|
||||
h := &storeHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Store{h}, opts...))
|
||||
}
|
||||
|
||||
type storeHandler struct {
|
||||
StoreHandler
|
||||
}
|
||||
|
||||
func (h *storeHandler) Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error {
|
||||
return h.StoreHandler.Read(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *storeHandler) Write(ctx context.Context, in *WriteRequest, out *WriteResponse) error {
|
||||
return h.StoreHandler.Write(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *storeHandler) Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error {
|
||||
return h.StoreHandler.Delete(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *storeHandler) Sync(ctx context.Context, stream server.Stream) error {
|
||||
m := new(SyncRequest)
|
||||
if err := stream.Recv(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return h.StoreHandler.Sync(ctx, m, &storeSyncStream{stream})
|
||||
}
|
||||
|
||||
type Store_SyncStream interface {
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*SyncResponse) error
|
||||
}
|
||||
|
||||
type storeSyncStream struct {
|
||||
stream server.Stream
|
||||
}
|
||||
|
||||
func (x *storeSyncStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *storeSyncStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *storeSyncStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *storeSyncStream) Send(m *SyncResponse) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
618
store/service/proto/store.pb.go
Normal file
618
store/service/proto/store.pb.go
Normal file
@@ -0,0 +1,618 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: micro/go-micro/store/service/proto/store.proto
|
||||
|
||||
package go_micro_store
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Record struct {
|
||||
// key of the record
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// value in the record
|
||||
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
// timestamp in unix seconds
|
||||
Expiry int64 `protobuf:"varint,3,opt,name=expiry,proto3" json:"expiry,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Record) Reset() { *m = Record{} }
|
||||
func (m *Record) String() string { return proto.CompactTextString(m) }
|
||||
func (*Record) ProtoMessage() {}
|
||||
func (*Record) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f84ccc98e143ed3e, []int{0}
|
||||
}
|
||||
|
||||
func (m *Record) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Record.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Record) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Record.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Record) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Record.Merge(m, src)
|
||||
}
|
||||
func (m *Record) XXX_Size() int {
|
||||
return xxx_messageInfo_Record.Size(m)
|
||||
}
|
||||
func (m *Record) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Record.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Record proto.InternalMessageInfo
|
||||
|
||||
func (m *Record) GetKey() string {
|
||||
if m != nil {
|
||||
return m.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Record) GetValue() []byte {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Record) GetExpiry() int64 {
|
||||
if m != nil {
|
||||
return m.Expiry
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ReadRequest struct {
|
||||
Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ReadRequest) Reset() { *m = ReadRequest{} }
|
||||
func (m *ReadRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ReadRequest) ProtoMessage() {}
|
||||
func (*ReadRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f84ccc98e143ed3e, []int{1}
|
||||
}
|
||||
|
||||
func (m *ReadRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ReadRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ReadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ReadRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ReadRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ReadRequest.Merge(m, src)
|
||||
}
|
||||
func (m *ReadRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_ReadRequest.Size(m)
|
||||
}
|
||||
func (m *ReadRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ReadRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ReadRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *ReadRequest) GetKeys() []string {
|
||||
if m != nil {
|
||||
return m.Keys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReadResponse struct {
|
||||
Records []*Record `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ReadResponse) Reset() { *m = ReadResponse{} }
|
||||
func (m *ReadResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ReadResponse) ProtoMessage() {}
|
||||
func (*ReadResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f84ccc98e143ed3e, []int{2}
|
||||
}
|
||||
|
||||
func (m *ReadResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ReadResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ReadResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ReadResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ReadResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ReadResponse.Merge(m, src)
|
||||
}
|
||||
func (m *ReadResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_ReadResponse.Size(m)
|
||||
}
|
||||
func (m *ReadResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ReadResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ReadResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *ReadResponse) GetRecords() []*Record {
|
||||
if m != nil {
|
||||
return m.Records
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WriteRequest struct {
|
||||
Records []*Record `protobuf:"bytes,2,rep,name=records,proto3" json:"records,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *WriteRequest) Reset() { *m = WriteRequest{} }
|
||||
func (m *WriteRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*WriteRequest) ProtoMessage() {}
|
||||
func (*WriteRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f84ccc98e143ed3e, []int{3}
|
||||
}
|
||||
|
||||
func (m *WriteRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_WriteRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *WriteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_WriteRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *WriteRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_WriteRequest.Merge(m, src)
|
||||
}
|
||||
func (m *WriteRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_WriteRequest.Size(m)
|
||||
}
|
||||
func (m *WriteRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_WriteRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_WriteRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *WriteRequest) GetRecords() []*Record {
|
||||
if m != nil {
|
||||
return m.Records
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WriteResponse struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *WriteResponse) Reset() { *m = WriteResponse{} }
|
||||
func (m *WriteResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*WriteResponse) ProtoMessage() {}
|
||||
func (*WriteResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f84ccc98e143ed3e, []int{4}
|
||||
}
|
||||
|
||||
func (m *WriteResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_WriteResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *WriteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_WriteResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *WriteResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_WriteResponse.Merge(m, src)
|
||||
}
|
||||
func (m *WriteResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_WriteResponse.Size(m)
|
||||
}
|
||||
func (m *WriteResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_WriteResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_WriteResponse proto.InternalMessageInfo
|
||||
|
||||
type DeleteRequest struct {
|
||||
Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *DeleteRequest) Reset() { *m = DeleteRequest{} }
|
||||
func (m *DeleteRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*DeleteRequest) ProtoMessage() {}
|
||||
func (*DeleteRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f84ccc98e143ed3e, []int{5}
|
||||
}
|
||||
|
||||
func (m *DeleteRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_DeleteRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *DeleteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_DeleteRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *DeleteRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_DeleteRequest.Merge(m, src)
|
||||
}
|
||||
func (m *DeleteRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_DeleteRequest.Size(m)
|
||||
}
|
||||
func (m *DeleteRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_DeleteRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_DeleteRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *DeleteRequest) GetKeys() []string {
|
||||
if m != nil {
|
||||
return m.Keys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeleteResponse struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *DeleteResponse) Reset() { *m = DeleteResponse{} }
|
||||
func (m *DeleteResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*DeleteResponse) ProtoMessage() {}
|
||||
func (*DeleteResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f84ccc98e143ed3e, []int{6}
|
||||
}
|
||||
|
||||
func (m *DeleteResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_DeleteResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *DeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_DeleteResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *DeleteResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_DeleteResponse.Merge(m, src)
|
||||
}
|
||||
func (m *DeleteResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_DeleteResponse.Size(m)
|
||||
}
|
||||
func (m *DeleteResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_DeleteResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo
|
||||
|
||||
type SyncRequest struct {
|
||||
// optional key
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SyncRequest) Reset() { *m = SyncRequest{} }
|
||||
func (m *SyncRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SyncRequest) ProtoMessage() {}
|
||||
func (*SyncRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f84ccc98e143ed3e, []int{7}
|
||||
}
|
||||
|
||||
func (m *SyncRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SyncRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *SyncRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_SyncRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *SyncRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_SyncRequest.Merge(m, src)
|
||||
}
|
||||
func (m *SyncRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_SyncRequest.Size(m)
|
||||
}
|
||||
func (m *SyncRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_SyncRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_SyncRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *SyncRequest) GetKey() string {
|
||||
if m != nil {
|
||||
return m.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SyncResponse struct {
|
||||
Records []*Record `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SyncResponse) Reset() { *m = SyncResponse{} }
|
||||
func (m *SyncResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*SyncResponse) ProtoMessage() {}
|
||||
func (*SyncResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f84ccc98e143ed3e, []int{8}
|
||||
}
|
||||
|
||||
func (m *SyncResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SyncResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *SyncResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_SyncResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *SyncResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_SyncResponse.Merge(m, src)
|
||||
}
|
||||
func (m *SyncResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_SyncResponse.Size(m)
|
||||
}
|
||||
func (m *SyncResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_SyncResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_SyncResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *SyncResponse) GetRecords() []*Record {
|
||||
if m != nil {
|
||||
return m.Records
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Record)(nil), "go.micro.store.Record")
|
||||
proto.RegisterType((*ReadRequest)(nil), "go.micro.store.ReadRequest")
|
||||
proto.RegisterType((*ReadResponse)(nil), "go.micro.store.ReadResponse")
|
||||
proto.RegisterType((*WriteRequest)(nil), "go.micro.store.WriteRequest")
|
||||
proto.RegisterType((*WriteResponse)(nil), "go.micro.store.WriteResponse")
|
||||
proto.RegisterType((*DeleteRequest)(nil), "go.micro.store.DeleteRequest")
|
||||
proto.RegisterType((*DeleteResponse)(nil), "go.micro.store.DeleteResponse")
|
||||
proto.RegisterType((*SyncRequest)(nil), "go.micro.store.SyncRequest")
|
||||
proto.RegisterType((*SyncResponse)(nil), "go.micro.store.SyncResponse")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("micro/go-micro/store/service/proto/store.proto", fileDescriptor_f84ccc98e143ed3e)
|
||||
}
|
||||
|
||||
var fileDescriptor_f84ccc98e143ed3e = []byte{
|
||||
// 333 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0xcb, 0x6e, 0xf2, 0x30,
|
||||
0x10, 0x85, 0x09, 0x81, 0xfc, 0x62, 0xb8, 0xfc, 0x68, 0x54, 0xa1, 0x88, 0xde, 0xd2, 0x74, 0x93,
|
||||
0x4d, 0x03, 0xa2, 0x2f, 0x50, 0xa9, 0x17, 0xb5, 0x5b, 0xb3, 0xe8, 0x9a, 0x86, 0x11, 0x8a, 0xa0,
|
||||
0x98, 0x3a, 0x01, 0x35, 0x2f, 0xd4, 0xe7, 0xac, 0x6c, 0x27, 0x69, 0x90, 0x41, 0xaa, 0xba, 0x1b,
|
||||
0x7b, 0xce, 0x1c, 0x9f, 0xf9, 0x64, 0x08, 0xdf, 0xe3, 0x48, 0xf0, 0xd1, 0x82, 0xdf, 0xe8, 0x22,
|
||||
0x49, 0xb9, 0xa0, 0x51, 0x42, 0x62, 0x17, 0x47, 0x34, 0xda, 0x08, 0x9e, 0xe6, 0x77, 0xa1, 0xaa,
|
||||
0xb1, 0xb7, 0xe0, 0x7a, 0x24, 0x54, 0xb7, 0xfe, 0x33, 0x38, 0x8c, 0x22, 0x2e, 0xe6, 0xd8, 0x07,
|
||||
0x7b, 0x49, 0x99, 0x6b, 0x79, 0x56, 0xd0, 0x62, 0xb2, 0xc4, 0x13, 0x68, 0xee, 0x66, 0xab, 0x2d,
|
||||
0xb9, 0x75, 0xcf, 0x0a, 0x3a, 0x4c, 0x1f, 0x70, 0x00, 0x0e, 0x7d, 0x6e, 0x62, 0x91, 0xb9, 0xb6,
|
||||
0x67, 0x05, 0x36, 0xcb, 0x4f, 0xfe, 0x15, 0xb4, 0x19, 0xcd, 0xe6, 0x8c, 0x3e, 0xb6, 0x94, 0xa4,
|
||||
0x88, 0xd0, 0x58, 0x52, 0x96, 0xb8, 0x96, 0x67, 0x07, 0x2d, 0xa6, 0x6a, 0xff, 0x0e, 0x3a, 0x5a,
|
||||
0x92, 0x6c, 0xf8, 0x3a, 0x21, 0x1c, 0xc3, 0x3f, 0xa1, 0x1e, 0xd7, 0xb2, 0xf6, 0x64, 0x10, 0xee,
|
||||
0xc7, 0x0b, 0x75, 0x36, 0x56, 0xc8, 0xa4, 0xc3, 0xab, 0x88, 0x53, 0x2a, 0x5e, 0xa9, 0x38, 0xd4,
|
||||
0x7f, 0xe7, 0xf0, 0x1f, 0xba, 0xb9, 0x83, 0x0e, 0xe1, 0x5f, 0x43, 0xf7, 0x81, 0x56, 0xf4, 0xe3,
|
||||
0x79, 0x28, 0x79, 0x1f, 0x7a, 0x85, 0x28, 0x1f, 0xbb, 0x84, 0xf6, 0x34, 0x5b, 0x47, 0xc5, 0x90,
|
||||
0x41, 0x4f, 0x46, 0xd5, 0x82, 0xbf, 0x2e, 0x3b, 0xf9, 0xaa, 0x43, 0x73, 0x2a, 0x3b, 0x78, 0x0f,
|
||||
0x0d, 0x09, 0x0e, 0x4f, 0xcd, 0x91, 0x92, 0xf8, 0xf0, 0xec, 0x70, 0x33, 0xcf, 0x5b, 0xc3, 0x27,
|
||||
0x68, 0xaa, 0xcd, 0xd1, 0x10, 0x56, 0x91, 0x0e, 0xcf, 0x8f, 0x74, 0x4b, 0x9f, 0x17, 0x70, 0x34,
|
||||
0x0b, 0x34, 0xa4, 0x7b, 0x20, 0x87, 0x17, 0xc7, 0xda, 0xa5, 0xd5, 0x23, 0x34, 0x24, 0x23, 0x73,
|
||||
0xaf, 0x0a, 0x5a, 0x73, 0xaf, 0x2a, 0x56, 0xbf, 0x36, 0xb6, 0xde, 0x1c, 0xf5, 0xb7, 0x6f, 0xbf,
|
||||
0x03, 0x00, 0x00, 0xff, 0xff, 0x30, 0xc8, 0x99, 0x52, 0x0d, 0x03, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// StoreClient is the client API for Store service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type StoreClient interface {
|
||||
Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error)
|
||||
Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error)
|
||||
Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error)
|
||||
Sync(ctx context.Context, in *SyncRequest, opts ...grpc.CallOption) (Store_SyncClient, error)
|
||||
}
|
||||
|
||||
type storeClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewStoreClient(cc *grpc.ClientConn) StoreClient {
|
||||
return &storeClient{cc}
|
||||
}
|
||||
|
||||
func (c *storeClient) Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) {
|
||||
out := new(ReadResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.store.Store/Read", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storeClient) Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) {
|
||||
out := new(WriteResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.store.Store/Write", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storeClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error) {
|
||||
out := new(DeleteResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.store.Store/Delete", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storeClient) Sync(ctx context.Context, in *SyncRequest, opts ...grpc.CallOption) (Store_SyncClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_Store_serviceDesc.Streams[0], "/go.micro.store.Store/Sync", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &storeSyncClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Store_SyncClient interface {
|
||||
Recv() (*SyncResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type storeSyncClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *storeSyncClient) Recv() (*SyncResponse, error) {
|
||||
m := new(SyncResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// StoreServer is the server API for Store service.
|
||||
type StoreServer interface {
|
||||
Read(context.Context, *ReadRequest) (*ReadResponse, error)
|
||||
Write(context.Context, *WriteRequest) (*WriteResponse, error)
|
||||
Delete(context.Context, *DeleteRequest) (*DeleteResponse, error)
|
||||
Sync(*SyncRequest, Store_SyncServer) error
|
||||
}
|
||||
|
||||
func RegisterStoreServer(s *grpc.Server, srv StoreServer) {
|
||||
s.RegisterService(&_Store_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Store_Read_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ReadRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StoreServer).Read(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.store.Store/Read",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StoreServer).Read(ctx, req.(*ReadRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Store_Write_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(WriteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StoreServer).Write(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.store.Store/Write",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StoreServer).Write(ctx, req.(*WriteRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Store_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StoreServer).Delete(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.store.Store/Delete",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StoreServer).Delete(ctx, req.(*DeleteRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Store_Sync_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SyncRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(StoreServer).Sync(m, &storeSyncServer{stream})
|
||||
}
|
||||
|
||||
type Store_SyncServer interface {
|
||||
Send(*SyncResponse) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type storeSyncServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *storeSyncServer) Send(m *SyncResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
var _Store_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.store.Store",
|
||||
HandlerType: (*StoreServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Read",
|
||||
Handler: _Store_Read_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Write",
|
||||
Handler: _Store_Write_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Delete",
|
||||
Handler: _Store_Delete_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Sync",
|
||||
Handler: _Store_Sync_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "micro/go-micro/store/service/proto/store.proto",
|
||||
}
|
48
store/service/proto/store.proto
Normal file
48
store/service/proto/store.proto
Normal file
@@ -0,0 +1,48 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package go.micro.store;
|
||||
|
||||
service Store {
|
||||
rpc Read(ReadRequest) returns (ReadResponse) {};
|
||||
rpc Write(WriteRequest) returns (WriteResponse) {};
|
||||
rpc Delete(DeleteRequest) returns (DeleteResponse) {};
|
||||
rpc Sync(SyncRequest) returns (stream SyncResponse) {};
|
||||
}
|
||||
|
||||
message Record {
|
||||
// key of the record
|
||||
string key = 1;
|
||||
// value in the record
|
||||
bytes value = 2;
|
||||
// timestamp in unix seconds
|
||||
int64 expiry = 3;
|
||||
}
|
||||
|
||||
message ReadRequest {
|
||||
repeated string keys = 1;
|
||||
}
|
||||
|
||||
message ReadResponse {
|
||||
repeated Record records = 1;
|
||||
}
|
||||
|
||||
message WriteRequest {
|
||||
repeated Record records = 2;
|
||||
}
|
||||
|
||||
message WriteResponse {}
|
||||
|
||||
message DeleteRequest {
|
||||
repeated string keys = 1;
|
||||
}
|
||||
|
||||
message DeleteResponse {}
|
||||
|
||||
message SyncRequest {
|
||||
// optional key
|
||||
string key = 1;
|
||||
}
|
||||
|
||||
message SyncResponse {
|
||||
repeated Record records = 1;
|
||||
}
|
119
store/service/service.go
Normal file
119
store/service/service.go
Normal file
@@ -0,0 +1,119 @@
|
||||
// Package service implements the store service interface
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/config/options"
|
||||
"github.com/micro/go-micro/store"
|
||||
pb "github.com/micro/go-micro/store/service/proto"
|
||||
)
|
||||
|
||||
type serviceStore struct {
|
||||
options.Options
|
||||
|
||||
// Addresses of the nodes
|
||||
Nodes []string
|
||||
|
||||
// store service client
|
||||
Client pb.StoreService
|
||||
}
|
||||
|
||||
// Sync all the known records
|
||||
func (s *serviceStore) Sync() ([]*store.Record, error) {
|
||||
stream, err := s.Client.Sync(context.Background(), &pb.SyncRequest{}, client.WithAddress(s.Nodes...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stream.Close()
|
||||
|
||||
var records []*store.Record
|
||||
|
||||
for {
|
||||
rsp, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return records, err
|
||||
}
|
||||
for _, record := range rsp.Records {
|
||||
records = append(records, &store.Record{
|
||||
Key: record.Key,
|
||||
Value: record.Value,
|
||||
Expiry: time.Duration(record.Expiry) * time.Second,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// Read a record with key
|
||||
func (s *serviceStore) Read(keys ...string) ([]*store.Record, error) {
|
||||
rsp, err := s.Client.Read(context.Background(), &pb.ReadRequest{
|
||||
Keys: keys,
|
||||
}, client.WithAddress(s.Nodes...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var records []*store.Record
|
||||
for _, val := range rsp.Records {
|
||||
records = append(records, &store.Record{
|
||||
Key: val.Key,
|
||||
Value: val.Value,
|
||||
Expiry: time.Duration(val.Expiry) * time.Second,
|
||||
})
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// Write a record
|
||||
func (s *serviceStore) Write(recs ...*store.Record) error {
|
||||
var records []*pb.Record
|
||||
|
||||
for _, record := range recs {
|
||||
records = append(records, &pb.Record{
|
||||
Key: record.Key,
|
||||
Value: record.Value,
|
||||
Expiry: int64(record.Expiry.Seconds()),
|
||||
})
|
||||
}
|
||||
|
||||
_, err := s.Client.Write(context.Background(), &pb.WriteRequest{
|
||||
Records: records,
|
||||
}, client.WithAddress(s.Nodes...))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete a record with key
|
||||
func (s *serviceStore) Delete(keys ...string) error {
|
||||
_, err := s.Client.Delete(context.Background(), &pb.DeleteRequest{
|
||||
Keys: keys,
|
||||
}, client.WithAddress(s.Nodes...))
|
||||
return err
|
||||
}
|
||||
|
||||
// NewStore returns a new store service implementation
|
||||
func NewStore(opts ...options.Option) store.Store {
|
||||
options := options.NewOptions(opts...)
|
||||
|
||||
var nodes []string
|
||||
|
||||
n, ok := options.Values().Get("store.nodes")
|
||||
if ok {
|
||||
nodes = n.([]string)
|
||||
}
|
||||
|
||||
service := &serviceStore{
|
||||
Options: options,
|
||||
Nodes: nodes,
|
||||
Client: pb.NewStoreService("go.micro.store", client.DefaultClient),
|
||||
}
|
||||
|
||||
return service
|
||||
}
|
@@ -16,14 +16,14 @@ var (
|
||||
type Store interface {
|
||||
// embed options
|
||||
options.Options
|
||||
// Dump the known records
|
||||
Dump() ([]*Record, error)
|
||||
// Sync all the known records
|
||||
Sync() ([]*Record, error)
|
||||
// Read a record with key
|
||||
Read(key string) (*Record, error)
|
||||
Read(keys ...string) ([]*Record, error)
|
||||
// Write a record
|
||||
Write(r *Record) error
|
||||
Write(recs ...*Record) error
|
||||
// Delete a record with key
|
||||
Delete(key string) error
|
||||
Delete(keys ...string) error
|
||||
}
|
||||
|
||||
// Record represents a data record
|
@@ -5,7 +5,7 @@ import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/sync/leader/consul"
|
||||
"github.com/micro/go-micro/sync/leader/etcd"
|
||||
"github.com/micro/go-micro/sync/task"
|
||||
"github.com/micro/go-micro/sync/task/local"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
@@ -80,7 +80,7 @@ func NewCron(opts ...Option) Cron {
|
||||
}
|
||||
|
||||
if options.Leader == nil {
|
||||
options.Leader = consul.NewLeader()
|
||||
options.Leader = etcd.NewLeader()
|
||||
}
|
||||
|
||||
if options.Task == nil {
|
||||
|
@@ -1,158 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/api/watch"
|
||||
"github.com/micro/go-micro/sync/leader"
|
||||
)
|
||||
|
||||
type consulLeader struct {
|
||||
opts leader.Options
|
||||
c *api.Client
|
||||
}
|
||||
|
||||
type consulElected struct {
|
||||
c *api.Client
|
||||
l *api.Lock
|
||||
id string
|
||||
key string
|
||||
opts leader.ElectOptions
|
||||
|
||||
mtx sync.RWMutex
|
||||
rv <-chan struct{}
|
||||
}
|
||||
|
||||
func (c *consulLeader) Elect(id string, opts ...leader.ElectOption) (leader.Elected, error) {
|
||||
var options leader.ElectOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
key := path.Join("micro/leader", c.opts.Group)
|
||||
|
||||
lc, err := c.c.LockOpts(&api.LockOptions{
|
||||
Key: key,
|
||||
Value: []byte(id),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rv, err := lc.Lock(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &consulElected{
|
||||
c: c.c,
|
||||
key: key,
|
||||
rv: rv,
|
||||
id: id,
|
||||
l: lc,
|
||||
opts: options,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *consulLeader) Follow() chan string {
|
||||
ch := make(chan string, 1)
|
||||
|
||||
key := path.Join("/micro/leader", c.opts.Group)
|
||||
|
||||
p, err := watch.Parse(map[string]interface{}{
|
||||
"type": "key",
|
||||
"key": key,
|
||||
})
|
||||
if err != nil {
|
||||
return ch
|
||||
}
|
||||
p.Handler = func(idx uint64, raw interface{}) {
|
||||
if raw == nil {
|
||||
return // ignore
|
||||
}
|
||||
v, ok := raw.(*api.KVPair)
|
||||
if !ok || v == nil {
|
||||
return // ignore
|
||||
}
|
||||
ch <- string(v.Value)
|
||||
}
|
||||
|
||||
go p.RunWithClientAndLogger(c.c, log.New(os.Stdout, "consul: ", log.Lshortfile))
|
||||
return ch
|
||||
}
|
||||
|
||||
func (c *consulLeader) String() string {
|
||||
return "consul"
|
||||
}
|
||||
|
||||
func (c *consulElected) Id() string {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func (c *consulElected) Reelect() error {
|
||||
rv, err := c.l.Lock(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.mtx.Lock()
|
||||
c.rv = rv
|
||||
c.mtx.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *consulElected) Revoked() chan bool {
|
||||
ch := make(chan bool, 1)
|
||||
c.mtx.RLock()
|
||||
rv := c.rv
|
||||
c.mtx.RUnlock()
|
||||
|
||||
go func() {
|
||||
<-rv
|
||||
ch <- true
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (c *consulElected) Resign() error {
|
||||
return c.l.Unlock()
|
||||
}
|
||||
|
||||
func NewLeader(opts ...leader.Option) leader.Leader {
|
||||
options := leader.Options{
|
||||
Group: "default",
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
config := api.DefaultConfig()
|
||||
|
||||
// set host
|
||||
// config.Host something
|
||||
// check if there are any addrs
|
||||
if len(options.Nodes) > 0 {
|
||||
addr, port, err := net.SplitHostPort(options.Nodes[0])
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "8500"
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
} else if err == nil {
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
}
|
||||
}
|
||||
|
||||
client, _ := api.NewClient(config)
|
||||
|
||||
return &consulLeader{
|
||||
opts: options,
|
||||
c: client,
|
||||
}
|
||||
}
|
145
sync/leader/etcd/etcd.go
Normal file
145
sync/leader/etcd/etcd.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
client "github.com/coreos/etcd/clientv3"
|
||||
cc "github.com/coreos/etcd/clientv3/concurrency"
|
||||
"github.com/micro/go-micro/sync/leader"
|
||||
)
|
||||
|
||||
type etcdLeader struct {
|
||||
opts leader.Options
|
||||
path string
|
||||
client *client.Client
|
||||
}
|
||||
|
||||
type etcdElected struct {
|
||||
s *cc.Session
|
||||
e *cc.Election
|
||||
id string
|
||||
}
|
||||
|
||||
func (e *etcdLeader) Elect(id string, opts ...leader.ElectOption) (leader.Elected, error) {
|
||||
var options leader.ElectOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
// make path
|
||||
path := path.Join(e.path, strings.Replace(id, "/", "-", -1))
|
||||
|
||||
s, err := cc.NewSession(e.client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l := cc.NewElection(s, path)
|
||||
|
||||
ctx, _ := context.WithCancel(context.Background())
|
||||
|
||||
if err := l.Campaign(ctx, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &etcdElected{
|
||||
e: l,
|
||||
id: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *etcdLeader) Follow() chan string {
|
||||
ch := make(chan string)
|
||||
|
||||
s, err := cc.NewSession(e.client)
|
||||
if err != nil {
|
||||
return ch
|
||||
}
|
||||
|
||||
l := cc.NewElection(s, e.path)
|
||||
ech := l.Observe(context.Background())
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case r, ok := <-ech:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ch <- string(r.Kvs[0].Value)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (e *etcdLeader) String() string {
|
||||
return "etcd"
|
||||
}
|
||||
|
||||
func (e *etcdElected) Reelect() error {
|
||||
ctx, _ := context.WithCancel(context.Background())
|
||||
return e.e.Campaign(ctx, e.id)
|
||||
}
|
||||
|
||||
func (e *etcdElected) Revoked() chan bool {
|
||||
ch := make(chan bool, 1)
|
||||
ech := e.e.Observe(context.Background())
|
||||
|
||||
go func() {
|
||||
for r := range ech {
|
||||
if string(r.Kvs[0].Value) != e.id {
|
||||
ch <- true
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (e *etcdElected) Resign() error {
|
||||
return e.e.Resign(context.Background())
|
||||
}
|
||||
|
||||
func (e *etcdElected) Id() string {
|
||||
return e.id
|
||||
}
|
||||
|
||||
func NewLeader(opts ...leader.Option) leader.Leader {
|
||||
var options leader.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
var endpoints []string
|
||||
|
||||
for _, addr := range options.Nodes {
|
||||
if len(addr) > 0 {
|
||||
endpoints = append(endpoints, addr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(endpoints) == 0 {
|
||||
endpoints = []string{"http://127.0.0.1:2379"}
|
||||
}
|
||||
|
||||
// TODO: parse addresses
|
||||
c, err := client.New(client.Config{
|
||||
Endpoints: endpoints,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &etcdLeader{
|
||||
path: "/micro/leader",
|
||||
client: c,
|
||||
opts: options,
|
||||
}
|
||||
}
|
@@ -1,104 +0,0 @@
|
||||
// Package consul is a consul implemenation of lock
|
||||
package consul
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
lock "github.com/micro/go-micro/sync/lock"
|
||||
)
|
||||
|
||||
type consulLock struct {
|
||||
sync.Mutex
|
||||
|
||||
locks map[string]*api.Lock
|
||||
opts lock.Options
|
||||
c *api.Client
|
||||
}
|
||||
|
||||
func (c *consulLock) Acquire(id string, opts ...lock.AcquireOption) error {
|
||||
var options lock.AcquireOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
if options.Wait <= time.Duration(0) {
|
||||
options.Wait = api.DefaultLockWaitTime
|
||||
}
|
||||
|
||||
ttl := fmt.Sprintf("%v", options.TTL)
|
||||
if options.TTL <= time.Duration(0) {
|
||||
ttl = api.DefaultLockSessionTTL
|
||||
}
|
||||
|
||||
l, err := c.c.LockOpts(&api.LockOptions{
|
||||
Key: c.opts.Prefix + id,
|
||||
LockWaitTime: options.Wait,
|
||||
SessionTTL: ttl,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = l.Lock(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
c.locks[id] = l
|
||||
c.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *consulLock) Release(id string) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
l, ok := c.locks[id]
|
||||
if !ok {
|
||||
return errors.New("lock not found")
|
||||
}
|
||||
err := l.Unlock()
|
||||
delete(c.locks, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *consulLock) String() string {
|
||||
return "consul"
|
||||
}
|
||||
|
||||
func NewLock(opts ...lock.Option) lock.Lock {
|
||||
var options lock.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
config := api.DefaultConfig()
|
||||
|
||||
// set host
|
||||
// config.Host something
|
||||
// check if there are any addrs
|
||||
if len(options.Nodes) > 0 {
|
||||
addr, port, err := net.SplitHostPort(options.Nodes[0])
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "8500"
|
||||
config.Address = fmt.Sprintf("%s:%s", options.Nodes[0], port)
|
||||
} else if err == nil {
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
}
|
||||
}
|
||||
|
||||
client, _ := api.NewClient(config)
|
||||
|
||||
return &consulLock{
|
||||
locks: make(map[string]*api.Lock),
|
||||
opts: options,
|
||||
c: client,
|
||||
}
|
||||
}
|
115
sync/lock/etcd/etcd.go
Normal file
115
sync/lock/etcd/etcd.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// Package etcd is an etcd implementation of lock
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
client "github.com/coreos/etcd/clientv3"
|
||||
cc "github.com/coreos/etcd/clientv3/concurrency"
|
||||
"github.com/micro/go-micro/sync/lock"
|
||||
)
|
||||
|
||||
type etcdLock struct {
|
||||
opts lock.Options
|
||||
path string
|
||||
client *client.Client
|
||||
|
||||
sync.Mutex
|
||||
locks map[string]*elock
|
||||
}
|
||||
|
||||
type elock struct {
|
||||
s *cc.Session
|
||||
m *cc.Mutex
|
||||
}
|
||||
|
||||
func (e *etcdLock) Acquire(id string, opts ...lock.AcquireOption) error {
|
||||
var options lock.AcquireOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
// make path
|
||||
path := path.Join(e.path, strings.Replace(e.opts.Prefix+id, "/", "-", -1))
|
||||
|
||||
var sopts []cc.SessionOption
|
||||
if options.TTL > 0 {
|
||||
sopts = append(sopts, cc.WithTTL(int(options.TTL.Seconds())))
|
||||
}
|
||||
|
||||
s, err := cc.NewSession(e.client, sopts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := cc.NewMutex(s, path)
|
||||
|
||||
ctx, _ := context.WithCancel(context.Background())
|
||||
|
||||
if err := m.Lock(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Lock()
|
||||
e.locks[id] = &elock{
|
||||
s: s,
|
||||
m: m,
|
||||
}
|
||||
e.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *etcdLock) Release(id string) error {
|
||||
e.Lock()
|
||||
defer e.Unlock()
|
||||
v, ok := e.locks[id]
|
||||
if !ok {
|
||||
return errors.New("lock not found")
|
||||
}
|
||||
err := v.m.Unlock(context.Background())
|
||||
delete(e.locks, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *etcdLock) String() string {
|
||||
return "etcd"
|
||||
}
|
||||
|
||||
func NewLock(opts ...lock.Option) lock.Lock {
|
||||
var options lock.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
var endpoints []string
|
||||
|
||||
for _, addr := range options.Nodes {
|
||||
if len(addr) > 0 {
|
||||
endpoints = append(endpoints, addr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(endpoints) == 0 {
|
||||
endpoints = []string{"http://127.0.0.1:2379"}
|
||||
}
|
||||
|
||||
// TODO: parse addresses
|
||||
c, err := client.New(client.Config{
|
||||
Endpoints: endpoints,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &etcdLock{
|
||||
path: "/micro/lock",
|
||||
client: c,
|
||||
opts: options,
|
||||
locks: make(map[string]*elock),
|
||||
}
|
||||
}
|
135
sync/lock/http/http.go
Normal file
135
sync/lock/http/http.go
Normal file
@@ -0,0 +1,135 @@
|
||||
// Package http adds a http lock implementation
|
||||
package http
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/micro/go-micro/sync/lock"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultPath = "/sync/lock"
|
||||
DefaultAddress = "localhost:8080"
|
||||
)
|
||||
|
||||
type httpLock struct {
|
||||
opts lock.Options
|
||||
}
|
||||
|
||||
func (h *httpLock) url(do, id string) (string, error) {
|
||||
sum := crc32.ChecksumIEEE([]byte(id))
|
||||
node := h.opts.Nodes[sum%uint32(len(h.opts.Nodes))]
|
||||
|
||||
// parse the host:port or whatever
|
||||
uri, err := url.Parse(node)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(uri.Scheme) == 0 {
|
||||
uri.Scheme = "http"
|
||||
}
|
||||
|
||||
// set path
|
||||
// build path
|
||||
path := filepath.Join(DefaultPath, do, h.opts.Prefix, id)
|
||||
uri.Path = path
|
||||
|
||||
// return url
|
||||
return uri.String(), nil
|
||||
}
|
||||
|
||||
func (h *httpLock) Acquire(id string, opts ...lock.AcquireOption) error {
|
||||
var options lock.AcquireOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
uri, err := h.url("acquire", id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ttl := fmt.Sprintf("%d", int64(options.TTL.Seconds()))
|
||||
wait := fmt.Sprintf("%d", int64(options.Wait.Seconds()))
|
||||
|
||||
rsp, err := http.PostForm(uri, url.Values{
|
||||
"id": {id},
|
||||
"ttl": {ttl},
|
||||
"wait": {wait},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rsp.Body.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(rsp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// success
|
||||
if rsp.StatusCode == 200 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// return error
|
||||
return errors.New(string(b))
|
||||
}
|
||||
|
||||
func (h *httpLock) Release(id string) error {
|
||||
uri, err := h.url("release", id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vals := url.Values{
|
||||
"id": {id},
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("DELETE", uri, strings.NewReader(vals.Encode()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rsp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rsp.Body.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(rsp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// success
|
||||
if rsp.StatusCode == 200 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// return error
|
||||
return errors.New(string(b))
|
||||
}
|
||||
|
||||
func NewLock(opts ...lock.Option) lock.Lock {
|
||||
var options lock.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
if len(options.Nodes) == 0 {
|
||||
options.Nodes = []string{DefaultAddress}
|
||||
}
|
||||
|
||||
return &httpLock{
|
||||
opts: options,
|
||||
}
|
||||
}
|
45
sync/lock/http/server/server.go
Normal file
45
sync/lock/http/server/server.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Package server implements the sync http server
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/micro/go-micro/sync/lock"
|
||||
lkhttp "github.com/micro/go-micro/sync/lock/http"
|
||||
)
|
||||
|
||||
func Handler(lk lock.Lock) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc(lkhttp.DefaultPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
id := r.Form.Get("id")
|
||||
if len(id) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case "POST":
|
||||
err := lk.Acquire(id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
case "DELETE":
|
||||
err := lk.Release(id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return mux
|
||||
}
|
||||
|
||||
func Server(lk lock.Lock) *http.Server {
|
||||
server := &http.Server{
|
||||
Addr: lkhttp.DefaultAddress,
|
||||
Handler: Handler(lk),
|
||||
}
|
||||
return server
|
||||
}
|
@@ -2,9 +2,14 @@
|
||||
package lock
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLockTimeout = errors.New("lock timeout")
|
||||
)
|
||||
|
||||
// Lock is a distributed locking interface
|
||||
type Lock interface {
|
||||
// Acquire a lock with given id
|
||||
|
142
sync/lock/memory/memory.go
Normal file
142
sync/lock/memory/memory.go
Normal file
@@ -0,0 +1,142 @@
|
||||
// Package memoy provides a sync.Mutex implementation of the lock for local use
|
||||
package memory
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
lock "github.com/micro/go-micro/sync/lock"
|
||||
)
|
||||
|
||||
type memoryLock struct {
|
||||
sync.RWMutex
|
||||
locks map[string]*mlock
|
||||
}
|
||||
|
||||
type mlock struct {
|
||||
id string
|
||||
time time.Time
|
||||
ttl time.Duration
|
||||
release chan bool
|
||||
}
|
||||
|
||||
func (m *memoryLock) Acquire(id string, opts ...lock.AcquireOption) error {
|
||||
// lock our access
|
||||
m.Lock()
|
||||
|
||||
var options lock.AcquireOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
lk, ok := m.locks[id]
|
||||
if !ok {
|
||||
m.locks[id] = &mlock{
|
||||
id: id,
|
||||
time: time.Now(),
|
||||
ttl: options.TTL,
|
||||
release: make(chan bool),
|
||||
}
|
||||
// unlock
|
||||
m.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
m.Unlock()
|
||||
|
||||
// set wait time
|
||||
var wait <-chan time.Time
|
||||
var ttl <-chan time.Time
|
||||
|
||||
// decide if we should wait
|
||||
if options.Wait > time.Duration(0) {
|
||||
wait = time.After(options.Wait)
|
||||
}
|
||||
|
||||
// check the ttl of the lock
|
||||
if lk.ttl > time.Duration(0) {
|
||||
// time lived for the lock
|
||||
live := time.Since(lk.time)
|
||||
|
||||
// set a timer for the leftover ttl
|
||||
if live > lk.ttl {
|
||||
// release the lock if it expired
|
||||
m.Release(id)
|
||||
} else {
|
||||
ttl = time.After(live)
|
||||
}
|
||||
}
|
||||
|
||||
lockLoop:
|
||||
for {
|
||||
// wait for the lock to be released
|
||||
select {
|
||||
case <-lk.release:
|
||||
m.Lock()
|
||||
|
||||
// someone locked before us
|
||||
lk, ok = m.locks[id]
|
||||
if ok {
|
||||
m.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
// got chance to lock
|
||||
m.locks[id] = &mlock{
|
||||
id: id,
|
||||
time: time.Now(),
|
||||
ttl: options.TTL,
|
||||
release: make(chan bool),
|
||||
}
|
||||
|
||||
m.Unlock()
|
||||
|
||||
break lockLoop
|
||||
case <-ttl:
|
||||
// ttl exceeded
|
||||
m.Release(id)
|
||||
// TODO: check the ttl again above
|
||||
ttl = nil
|
||||
// try acquire
|
||||
continue
|
||||
case <-wait:
|
||||
return lock.ErrLockTimeout
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryLock) Release(id string) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
lk, ok := m.locks[id]
|
||||
// no lock exists
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// delete the lock
|
||||
delete(m.locks, id)
|
||||
|
||||
select {
|
||||
case <-lk.release:
|
||||
return nil
|
||||
default:
|
||||
close(lk.release)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewLock(opts ...lock.Option) lock.Lock {
|
||||
var options lock.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
return &memoryLock{
|
||||
locks: make(map[string]*mlock),
|
||||
}
|
||||
}
|
14
sync/map.go
14
sync/map.go
@@ -6,9 +6,9 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/micro/go-micro/data/store"
|
||||
ckv "github.com/micro/go-micro/data/store/consul"
|
||||
lock "github.com/micro/go-micro/sync/lock/consul"
|
||||
"github.com/micro/go-micro/store"
|
||||
ckv "github.com/micro/go-micro/store/etcd"
|
||||
lock "github.com/micro/go-micro/sync/lock/etcd"
|
||||
)
|
||||
|
||||
type syncMap struct {
|
||||
@@ -39,8 +39,12 @@ func (m *syncMap) Read(key, val interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(kval) == 0 {
|
||||
return store.ErrNotFound
|
||||
}
|
||||
|
||||
// decode value
|
||||
return json.Unmarshal(kval.Value, val)
|
||||
return json.Unmarshal(kval[0].Value, val)
|
||||
}
|
||||
|
||||
func (m *syncMap) Write(key, val interface{}) error {
|
||||
@@ -85,7 +89,7 @@ func (m *syncMap) Delete(key interface{}) error {
|
||||
}
|
||||
|
||||
func (m *syncMap) Iterate(fn func(key, val interface{}) error) error {
|
||||
keyvals, err := m.opts.Store.Dump()
|
||||
keyvals, err := m.opts.Store.Sync()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user