Merge pull request #1 from micro/master

Update forks to latest
This commit is contained in:
potato 2019-08-09 18:46:28 +08:00 committed by GitHub
commit b13604fb4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
193 changed files with 10179 additions and 2163 deletions

View File

@ -1,9 +1,11 @@
language: go language: go
go: go:
- 1.11.x
- 1.12.x - 1.12.x
env: env:
- GO111MODULE=on - GO111MODULE=on
notifications: notifications:
slack: slack:
secure: aEvhLbhujaGaKSrOokiG3//PaVHTIrc3fBpoRbCRqfZpyq6WREoapJJhF+tIpWWOwaC9GmChbD6aHo/jMUgwKXVyPSaNjiEL87YzUUpL8B2zslNp1rgfTg/LrzthOx3Q1TYwpaAl3to0fuHUVFX4yMeC2vuThq7WSXgMMxFCtbc= secure: aEvhLbhujaGaKSrOokiG3//PaVHTIrc3fBpoRbCRqfZpyq6WREoapJJhF+tIpWWOwaC9GmChbD6aHo/jMUgwKXVyPSaNjiEL87YzUUpL8B2zslNp1rgfTg/LrzthOx3Q1TYwpaAl3to0fuHUVFX4yMeC2vuThq7WSXgMMxFCtbc=
cache:
directories:
- $GOPATH/pkg/mod

View File

@ -1,197 +0,0 @@
# Agent
Agent is a library used to create commands, inputs and robot services
## Getting Started
- [Commands](#commands) - Commands are functions executed by the bot based on text based pattern matching.
- [Inputs](#inputs) - Inputs are plugins for communication e.g Slack, Telegram, IRC, etc.
- [Services](#services) - Write bots as micro services
## Commands
Commands are functions executed by the bot based on text based pattern matching.
### Write a Command
```go
import "github.com/micro/go-micro/agent/command"
func Ping() command.Command {
usage := "ping"
description := "Returns pong"
return command.NewCommand("ping", usage, desc, func(args ...string) ([]byte, error) {
return []byte("pong"), nil
})
}
```
### Register the command
Add the command to the Commands map with a pattern key that can be matched by golang/regexp.Match
```go
import "github.com/micro/go-micro/agent/command"
func init() {
command.Commands["^ping$"] = Ping()
}
```
### Rebuild Micro
Build binary
```shell
cd github.com/micro/micro
// For local use
go build -i -o micro ./main.go
// For docker image
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -i -o micro ./main.go
```
## Inputs
Inputs are plugins for communication e.g Slack, HipChat, XMPP, IRC, SMTP, etc, etc.
New inputs can be added in the following way.
### Write an Input
Write an input that satisfies the Input interface.
```go
type Input interface {
// Provide cli flags
Flags() []cli.Flag
// Initialise input using cli context
Init(*cli.Context) error
// Stream events from the input
Stream() (Conn, error)
// Start the input
Start() error
// Stop the input
Stop() error
// name of the input
String() string
}
```
### Register the input
Add the input to the Inputs map.
```go
import "github.com/micro/micro/bot/input"
func init() {
input.Inputs["name"] = MyInput
}
```
### Rebuild Micro
Build binary
```shell
cd github.com/micro/micro
// For local use
go build -i -o micro ./main.go
// For docker image
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -i -o micro ./main.go
```
## Services
The micro bot supports the ability to create commands as micro services.
### How does it work?
The bot watches the service registry for services with it's namespace. The default namespace is `go.micro.bot`.
Any service within this namespace will automatically be added to the list of available commands. When a command
is executed, the bot will call the service with method `Command.Exec`. It also expects the method `Command.Help`
to exist for usage info.
The service interface is as follows and can be found at [go-micro/agent/proto](https://github.com/micro/go-micro/agent/blob/master/proto/bot.proto)
```
syntax = "proto3";
package go.micro.bot;
service Command {
rpc Help(HelpRequest) returns (HelpResponse) {};
rpc Exec(ExecRequest) returns (ExecResponse) {};
}
message HelpRequest {
}
message HelpResponse {
string usage = 1;
string description = 2;
}
message ExecRequest {
repeated string args = 1;
}
message ExecResponse {
bytes result = 1;
string error = 2;
}
```
### Example
Here's an example echo command as a microservice
```go
package main
import (
"fmt"
"strings"
"github.com/micro/go-micro"
"golang.org/x/net/context"
proto "github.com/micro/go-micro/agent/proto"
)
type Command struct{}
// Help returns the command usage
func (c *Command) Help(ctx context.Context, req *proto.HelpRequest, rsp *proto.HelpResponse) error {
// Usage should include the name of the command
rsp.Usage = "echo"
rsp.Description = "This is an example bot command as a micro service which echos the message"
return nil
}
// Exec executes the command
func (c *Command) Exec(ctx context.Context, req *proto.ExecRequest, rsp *proto.ExecResponse) error {
rsp.Result = []byte(strings.Join(req.Args, " "))
// rsp.Error could be set to return an error instead
// the function error would only be used for service level issues
return nil
}
func main() {
service := micro.NewService(
micro.Name("go.micro.bot.echo"),
)
service.Init()
proto.RegisterCommandHandler(service.Server(), new(Command))
if err := service.Run(); err != nil {
fmt.Println(err)
}
}
```

View File

@ -1,18 +0,0 @@
# Go API [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![GoDoc](https://godoc.org/github.com/micro/go-micro/api?status.svg)](https://godoc.org/github.com/micro/go-micro/api) [![Travis CI](https://api.travis-ci.org/micro/go-micro/api.svg?branch=master)](https://travis-ci.org/micro/go-micro/api) [![Go Report Card](https://goreportcard.com/badge/micro/go-micro/api)](https://goreportcard.com/report/github.com/micro/go-micro/api)
Go API is a pluggable API framework driven by service discovery to help build powerful public API gateways.
## Overview
The Go API library provides api gateway routing capabilities. A microservice architecture decouples application logic into
separate service. An api gateway provides a single entry point to consolidate these services into a unified api. The
Go API uses routes defined in service discovery metadata to generate routing rules and serve http requests.
<img src="https://micro.mu/docs/images/go-api.png?v=1" alt="Go API" />
Go API is the basis for the [micro api](https://micro.mu/docs/api.html).
## Getting Started
See the [docs](https://micro.mu/docs/go-api.html) to learn more

View File

@ -8,8 +8,8 @@ import (
"github.com/micro/go-micro/api/handler" "github.com/micro/go-micro/api/handler"
api "github.com/micro/go-micro/api/proto" api "github.com/micro/go-micro/api/proto"
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/errors" "github.com/micro/go-micro/errors"
"github.com/micro/go-micro/selector"
"github.com/micro/go-micro/util/ctx" "github.com/micro/go-micro/util/ctx"
) )

View File

@ -9,8 +9,8 @@ import (
"strings" "strings"
api "github.com/micro/go-micro/api/proto" api "github.com/micro/go-micro/api/proto"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
) )
func requestToProto(r *http.Request) (*api.Request, error) { func requestToProto(r *http.Request) (*api.Request, error) {

View File

@ -120,7 +120,7 @@ func (c *conn) writeLoop() {
opts = append(opts, broker.Queue(c.queue)) opts = append(opts, broker.Queue(c.queue))
} }
subscriber, err := c.b.Subscribe(c.topic, func(p broker.Publication) error { subscriber, err := c.b.Subscribe(c.topic, func(p broker.Event) error {
b, err := json.Marshal(p.Message()) b, err := json.Marshal(p.Message())
if err != nil { if err != nil {
return nil return nil

View File

@ -10,7 +10,7 @@ import (
"github.com/micro/go-micro/api" "github.com/micro/go-micro/api"
"github.com/micro/go-micro/api/handler" "github.com/micro/go-micro/api/handler"
"github.com/micro/go-micro/selector" "github.com/micro/go-micro/client/selector"
) )
const ( const (
@ -73,7 +73,7 @@ func (h *httpHandler) getService(r *http.Request) (string, error) {
return "", nil return "", nil
} }
return fmt.Sprintf("http://%s:%d", s.Address, s.Port), nil return fmt.Sprintf("http://%s", s.Address), nil
} }
func (h *httpHandler) String() string { func (h *httpHandler) String() string {

View File

@ -4,14 +4,12 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strconv"
"strings"
"testing" "testing"
"github.com/micro/go-micro/api/handler" "github.com/micro/go-micro/api/handler"
"github.com/micro/go-micro/api/router" "github.com/micro/go-micro/api/router"
regRouter "github.com/micro/go-micro/api/router/registry" regRouter "github.com/micro/go-micro/api/router/registry"
"github.com/micro/go-micro/cmd" "github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/registry/memory" "github.com/micro/go-micro/registry/memory"
) )
@ -26,21 +24,12 @@ func testHttp(t *testing.T, path, service, ns string) {
} }
defer l.Close() defer l.Close()
parts := strings.Split(l.Addr().String(), ":")
var host string
var port int
host = parts[0]
port, _ = strconv.Atoi(parts[1])
s := &registry.Service{ s := &registry.Service{
Name: service, Name: service,
Nodes: []*registry.Node{ Nodes: []*registry.Node{
&registry.Node{ &registry.Node{
Id: service + "-1", Id: service + "-1",
Address: host, Address: l.Addr().String(),
Port: port,
}, },
}, },
} }

View File

@ -14,12 +14,12 @@ import (
"github.com/micro/go-micro/api/handler" "github.com/micro/go-micro/api/handler"
proto "github.com/micro/go-micro/api/internal/proto" proto "github.com/micro/go-micro/api/internal/proto"
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/codec" "github.com/micro/go-micro/codec"
"github.com/micro/go-micro/codec/jsonrpc" "github.com/micro/go-micro/codec/jsonrpc"
"github.com/micro/go-micro/codec/protorpc" "github.com/micro/go-micro/codec/protorpc"
"github.com/micro/go-micro/errors" "github.com/micro/go-micro/errors"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
"github.com/micro/go-micro/util/ctx" "github.com/micro/go-micro/util/ctx"
) )
@ -120,32 +120,6 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var rsp []byte var rsp []byte
switch { switch {
// json codecs
case hasCodec(ct, jsonCodecs):
var request json.RawMessage
// if the extracted payload isn't empty lets use it
if len(br) > 0 {
request = json.RawMessage(br)
}
// create request/response
var response json.RawMessage
req := c.NewRequest(
service.Name,
service.Endpoint.Name,
&request,
client.WithContentType(ct),
)
// make the call
if err := c.Call(cx, req, &response, client.WithSelectOption(so)); err != nil {
writeError(w, r, err)
return
}
// marshall response
rsp, _ = response.MarshalJSON()
// proto codecs // proto codecs
case hasCodec(ct, protoCodecs): case hasCodec(ct, protoCodecs):
request := &proto.Message{} request := &proto.Message{}
@ -173,10 +147,38 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// marshall response // marshall response
rsp, _ = response.Marshal() rsp, _ = response.Marshal()
default: default:
http.Error(w, "Unsupported Content-Type", 400) // if json codec is not present set to json
if !hasCodec(ct, jsonCodecs) {
ct = "application/json"
}
// default to trying json
var request json.RawMessage
// if the extracted payload isn't empty lets use it
if len(br) > 0 {
request = json.RawMessage(br)
}
// create request/response
var response json.RawMessage
req := c.NewRequest(
service.Name,
service.Endpoint.Name,
&request,
client.WithContentType(ct),
)
// make the call
if err := c.Call(cx, req, &response, client.WithSelectOption(so)); err != nil {
writeError(w, r, err)
return return
} }
// marshall response
rsp, _ = response.MarshalJSON()
}
// write the response // write the response
writeResponse(w, r, rsp) writeResponse(w, r, rsp)
} }

View File

@ -13,7 +13,7 @@ import (
"github.com/micro/go-micro/api" "github.com/micro/go-micro/api"
"github.com/micro/go-micro/api/handler" "github.com/micro/go-micro/api/handler"
"github.com/micro/go-micro/selector" "github.com/micro/go-micro/client/selector"
) )
const ( const (
@ -79,7 +79,7 @@ func (wh *webHandler) getService(r *http.Request) (string, error) {
return "", nil return "", nil
} }
return fmt.Sprintf("http://%s:%d", s.Address, s.Port), nil return fmt.Sprintf("http://%s", s.Address), nil
} }
// serveWebSocket used to serve a web socket proxied connection // serveWebSocket used to serve a web socket proxied connection

View File

@ -3,7 +3,7 @@ package router
import ( import (
"github.com/micro/go-micro/api/resolver" "github.com/micro/go-micro/api/resolver"
"github.com/micro/go-micro/api/resolver/micro" "github.com/micro/go-micro/api/resolver/micro"
"github.com/micro/go-micro/cmd" "github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
) )

View File

@ -3,28 +3,28 @@ package broker
// Broker is an interface used for asynchronous messaging. // Broker is an interface used for asynchronous messaging.
type Broker interface { type Broker interface {
Init(...Option) error
Options() Options Options() Options
Address() string Address() string
Connect() error Connect() error
Disconnect() error Disconnect() error
Init(...Option) error Publish(topic string, m *Message, opts ...PublishOption) error
Publish(string, *Message, ...PublishOption) error Subscribe(topic string, h Handler, opts ...SubscribeOption) (Subscriber, error)
Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
String() string String() string
} }
// Handler is used to process messages via a subscription of a topic. // Handler is used to process messages via a subscription of a topic.
// The handler is passed a publication interface which contains the // The handler is passed a publication interface which contains the
// message and optional Ack method to acknowledge receipt of the message. // message and optional Ack method to acknowledge receipt of the message.
type Handler func(Publication) error type Handler func(Event) error
type Message struct { type Message struct {
Header map[string]string Header map[string]string
Body []byte Body []byte
} }
// Publication is given to a subscription handler for processing // Event is given to a subscription handler for processing
type Publication interface { type Event interface {
Topic() string Topic() string
Message() *Message Message() *Message
Ack() error Ack() error

View File

@ -14,13 +14,11 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.0-123", Id: "foo-1.0.0-123",
Address: "localhost", Address: "localhost:9999",
Port: 9999,
}, },
{ {
Id: "foo-1.0.0-321", Id: "foo-1.0.0-321",
Address: "localhost", Address: "localhost:9999",
Port: 9999,
}, },
}, },
}, },
@ -30,8 +28,7 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.1-321", Id: "foo-1.0.1-321",
Address: "localhost", Address: "localhost:6666",
Port: 6666,
}, },
}, },
}, },
@ -41,8 +38,7 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.3-345", Id: "foo-1.0.3-345",
Address: "localhost", Address: "localhost:8888",
Port: 8888,
}, },
}, },
}, },

View File

@ -13,8 +13,6 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"runtime" "runtime"
"strconv"
"strings"
"sync" "sync"
"time" "time"
@ -59,7 +57,7 @@ type httpSubscriber struct {
hb *httpBroker hb *httpBroker
} }
type httpPublication struct { type httpEvent struct {
m *Message m *Message
t string t string
} }
@ -155,15 +153,15 @@ func newHttpBroker(opts ...Option) Broker {
return h return h
} }
func (h *httpPublication) Ack() error { func (h *httpEvent) Ack() error {
return nil return nil
} }
func (h *httpPublication) Message() *Message { func (h *httpEvent) Message() *Message {
return h.m return h.m
} }
func (h *httpPublication) Topic() string { func (h *httpEvent) Topic() string {
return h.t return h.t
} }
@ -323,7 +321,7 @@ func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
p := &httpPublication{m: m, t: topic} p := &httpEvent{m: m, t: topic}
id := req.Form.Get("id") id := req.Form.Get("id")
h.RLock() h.RLock()
@ -403,6 +401,7 @@ func (h *httpBroker) Connect() error {
go func() { go func() {
h.run(l) h.run(l)
h.Lock() h.Lock()
h.opts.Addrs = []string{addr}
h.address = addr h.address = addr
h.Unlock() h.Unlock()
}() }()
@ -542,7 +541,7 @@ func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption)
vals := url.Values{} vals := url.Values{}
vals.Add("id", node.Id) vals.Add("id", node.Id)
uri := fmt.Sprintf("%s://%s:%d%s?%s", scheme, node.Address, node.Port, DefaultSubPath, vals.Encode()) uri := fmt.Sprintf("%s://%s%s?%s", scheme, node.Address, DefaultSubPath, vals.Encode())
r, err := h.c.Post(uri, "application/json", bytes.NewReader(b)) r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
if err != nil { if err != nil {
return err return err
@ -613,12 +612,15 @@ func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption)
} }
func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) { func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
var err error
var host, port string
options := NewSubscribeOptions(opts...) options := NewSubscribeOptions(opts...)
// parse address for host, port // parse address for host, port
parts := strings.Split(h.Address(), ":") host, port, err = net.SplitHostPort(h.Address())
host := strings.Join(parts[:len(parts)-1], ":") if err != nil {
port, _ := strconv.Atoi(parts[len(parts)-1]) return nil, err
}
addr, err := maddr.Extract(host) addr, err := maddr.Extract(host)
if err != nil { if err != nil {
@ -637,8 +639,7 @@ func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeO
// register service // register service
node := &registry.Node{ node := &registry.Node{
Id: id, Id: id,
Address: addr, Address: mnet.HostPort(addr, port),
Port: port,
Metadata: map[string]string{ Metadata: map[string]string{
"secure": fmt.Sprintf("%t", secure), "secure": fmt.Sprintf("%t", secure),
}, },

View File

@ -47,7 +47,7 @@ func sub(be *testing.B, c int) {
done := make(chan bool, c) done := make(chan bool, c)
for i := 0; i < c; i++ { for i := 0; i < c; i++ {
sub, err := b.Subscribe(topic, func(p Publication) error { sub, err := b.Subscribe(topic, func(p Event) error {
done <- true done <- true
m := p.Message() m := p.Message()
@ -107,7 +107,7 @@ func pub(be *testing.B, c int) {
done := make(chan bool, c*4) done := make(chan bool, c*4)
sub, err := b.Subscribe(topic, func(p Publication) error { sub, err := b.Subscribe(topic, func(p Event) error {
done <- true done <- true
m := p.Message() m := p.Message()
if string(m.Body) != string(msg.Body) { if string(m.Body) != string(msg.Body) {
@ -175,7 +175,7 @@ func TestBroker(t *testing.T) {
done := make(chan bool) done := make(chan bool)
sub, err := b.Subscribe("test", func(p Publication) error { sub, err := b.Subscribe("test", func(p Event) error {
m := p.Message() m := p.Message()
if string(m.Body) != string(msg.Body) { if string(m.Body) != string(msg.Body) {
@ -224,7 +224,7 @@ func TestConcurrentSubBroker(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
sub, err := b.Subscribe("test", func(p Publication) error { sub, err := b.Subscribe("test", func(p Event) error {
defer wg.Done() defer wg.Done()
m := p.Message() m := p.Message()
@ -279,7 +279,7 @@ func TestConcurrentPubBroker(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
sub, err := b.Subscribe("test", func(p Publication) error { sub, err := b.Subscribe("test", func(p Event) error {
defer wg.Done() defer wg.Done()
m := p.Message() m := p.Message()

View File

@ -3,21 +3,26 @@ package memory
import ( import (
"errors" "errors"
"math/rand"
"sync" "sync"
"time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/micro/go-micro/broker" "github.com/micro/go-micro/broker"
maddr "github.com/micro/go-micro/util/addr"
mnet "github.com/micro/go-micro/util/net"
) )
type memoryBroker struct { type memoryBroker struct {
opts broker.Options opts broker.Options
addr string
sync.RWMutex sync.RWMutex
connected bool connected bool
Subscribers map[string][]*memorySubscriber Subscribers map[string][]*memorySubscriber
} }
type memoryPublication struct { type memoryEvent struct {
topic string topic string
message *broker.Message message *broker.Message
} }
@ -35,7 +40,7 @@ func (m *memoryBroker) Options() broker.Options {
} }
func (m *memoryBroker) Address() string { func (m *memoryBroker) Address() string {
return "" return m.addr
} }
func (m *memoryBroker) Connect() error { func (m *memoryBroker) Connect() error {
@ -46,6 +51,15 @@ func (m *memoryBroker) Connect() error {
return nil return nil
} }
addr, err := maddr.Extract("::")
if err != nil {
return err
}
i := rand.Intn(20000)
// set addr with port
addr = mnet.HostPort(addr, 10000+i)
m.addr = addr
m.connected = true m.connected = true
return nil return nil
@ -72,19 +86,19 @@ func (m *memoryBroker) Init(opts ...broker.Option) error {
} }
func (m *memoryBroker) Publish(topic string, message *broker.Message, opts ...broker.PublishOption) error { func (m *memoryBroker) Publish(topic string, message *broker.Message, opts ...broker.PublishOption) error {
m.Lock() m.RLock()
defer m.Unlock()
if !m.connected { if !m.connected {
m.RUnlock()
return errors.New("not connected") return errors.New("not connected")
} }
subs, ok := m.Subscribers[topic] subs, ok := m.Subscribers[topic]
m.RUnlock()
if !ok { if !ok {
return nil return nil
} }
p := &memoryPublication{ p := &memoryEvent{
topic: topic, topic: topic,
message: message, message: message,
} }
@ -99,12 +113,12 @@ func (m *memoryBroker) Publish(topic string, message *broker.Message, opts ...br
} }
func (m *memoryBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { func (m *memoryBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
m.Lock() m.RLock()
defer m.Unlock()
if !m.connected { if !m.connected {
m.RUnlock()
return nil, errors.New("not connected") return nil, errors.New("not connected")
} }
m.RUnlock()
var options broker.SubscribeOptions var options broker.SubscribeOptions
for _, o := range opts { for _, o := range opts {
@ -119,7 +133,9 @@ func (m *memoryBroker) Subscribe(topic string, handler broker.Handler, opts ...b
opts: options, opts: options,
} }
m.Lock()
m.Subscribers[topic] = append(m.Subscribers[topic], sub) m.Subscribers[topic] = append(m.Subscribers[topic], sub)
m.Unlock()
go func() { go func() {
<-sub.exit <-sub.exit
@ -142,15 +158,15 @@ func (m *memoryBroker) String() string {
return "memory" return "memory"
} }
func (m *memoryPublication) Topic() string { func (m *memoryEvent) Topic() string {
return m.topic return m.topic
} }
func (m *memoryPublication) Message() *broker.Message { func (m *memoryEvent) Message() *broker.Message {
return m.message return m.message
} }
func (m *memoryPublication) Ack() error { func (m *memoryEvent) Ack() error {
return nil return nil
} }
@ -169,6 +185,7 @@ func (m *memorySubscriber) Unsubscribe() error {
func NewBroker(opts ...broker.Option) broker.Broker { func NewBroker(opts ...broker.Option) broker.Broker {
var options broker.Options var options broker.Options
rand.Seed(time.Now().UnixNano())
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }

View File

@ -17,7 +17,7 @@ func TestMemoryBroker(t *testing.T) {
topic := "test" topic := "test"
count := 10 count := 10
fn := func(p broker.Publication) error { fn := func(p broker.Event) error {
return nil return nil
} }

View File

@ -13,18 +13,19 @@ import (
) )
type natsBroker struct { type natsBroker struct {
sync.Once
sync.RWMutex sync.RWMutex
addrs []string addrs []string
conn *nats.Conn conn *nats.Conn
opts broker.Options opts broker.Options
nopts nats.Options nopts nats.Options
drain bool drain bool
closeCh chan (error)
} }
type subscriber struct { type subscriber struct {
s *nats.Subscription s *nats.Subscription
opts broker.SubscribeOptions opts broker.SubscribeOptions
drain bool
} }
type publication struct { type publication struct {
@ -54,9 +55,6 @@ func (s *subscriber) Topic() string {
} }
func (s *subscriber) Unsubscribe() error { func (s *subscriber) Unsubscribe() error {
if s.drain {
return s.s.Drain()
}
return s.s.Unsubscribe() return s.s.Unsubscribe()
} }
@ -122,20 +120,17 @@ func (n *natsBroker) Connect() error {
func (n *natsBroker) Disconnect() error { func (n *natsBroker) Disconnect() error {
n.RLock() n.RLock()
defer n.RUnlock()
if n.drain { if n.drain {
n.conn.Drain() n.conn.Drain()
} else { return <-n.closeCh
n.conn.Close()
} }
n.RUnlock() n.conn.Close()
return nil return nil
} }
func (n *natsBroker) Init(opts ...broker.Option) error { func (n *natsBroker) Init(opts ...broker.Option) error {
for _, o := range opts { n.setOption(opts...)
o(&n.opts)
}
n.addrs = setAddrs(n.opts.Addrs)
return nil return nil
} }
@ -167,11 +162,6 @@ func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...bro
o(&opt) o(&opt)
} }
var drain bool
if _, ok := opt.Context.Value(drainSubscriptionKey{}).(bool); ok {
drain = true
}
fn := func(msg *nats.Msg) { fn := func(msg *nats.Msg) {
var m broker.Message var m broker.Message
if err := n.opts.Codec.Unmarshal(msg.Data, &m); err != nil { if err := n.opts.Codec.Unmarshal(msg.Data, &m); err != nil {
@ -193,7 +183,7 @@ func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...bro
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &subscriber{s: sub, opts: opt, drain: drain}, nil return &subscriber{s: sub, opts: opt}, nil
} }
func (n *natsBroker) String() string { func (n *natsBroker) String() string {
@ -207,39 +197,59 @@ func NewBroker(opts ...broker.Option) broker.Broker {
Context: context.Background(), Context: context.Background(),
} }
n := &natsBroker{
opts: options,
}
n.setOption(opts...)
return n
}
func (n *natsBroker) setOption(opts ...broker.Option) {
for _, o := range opts { for _, o := range opts {
o(&options) o(&n.opts)
} }
natsOpts := nats.GetDefaultOptions() n.Once.Do(func() {
if n, ok := options.Context.Value(optionsKey{}).(nats.Options); ok { n.nopts = nats.GetDefaultOptions()
natsOpts = n })
}
var drain bool if nopts, ok := n.opts.Context.Value(optionsKey{}).(nats.Options); ok {
if _, ok := options.Context.Value(drainSubscriptionKey{}).(bool); ok { n.nopts = nopts
drain = true
} }
// broker.Options have higher priority than nats.Options // broker.Options have higher priority than nats.Options
// only if Addrs, Secure or TLSConfig were not set through a broker.Option // only if Addrs, Secure or TLSConfig were not set through a broker.Option
// we read them from nats.Option // we read them from nats.Option
if len(options.Addrs) == 0 { if len(n.opts.Addrs) == 0 {
options.Addrs = natsOpts.Servers n.opts.Addrs = n.nopts.Servers
} }
if !options.Secure { if !n.opts.Secure {
options.Secure = natsOpts.Secure n.opts.Secure = n.nopts.Secure
} }
if options.TLSConfig == nil { if n.opts.TLSConfig == nil {
options.TLSConfig = natsOpts.TLSConfig n.opts.TLSConfig = n.nopts.TLSConfig
} }
n.addrs = setAddrs(n.opts.Addrs)
return &natsBroker{ if n.opts.Context.Value(drainConnectionKey{}) != nil {
opts: options, n.drain = true
nopts: natsOpts, n.closeCh = make(chan error)
addrs: setAddrs(options.Addrs), n.nopts.ClosedCB = n.onClose
drain: drain, n.nopts.AsyncErrorCB = n.onAsyncError
}
}
func (n *natsBroker) onClose(conn *nats.Conn) {
n.closeCh <- nil
}
func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err error) {
// There are kinds of different async error nats might callback, but we are interested
// in ErrDrainTimeout only here.
if err == nats.ErrDrainTimeout {
n.closeCh <- err
} }
} }

View File

@ -7,7 +7,6 @@ import (
type optionsKey struct{} type optionsKey struct{}
type drainConnectionKey struct{} type drainConnectionKey struct{}
type drainSubscriptionKey struct{}
// Options accepts nats.Options // Options accepts nats.Options
func Options(opts nats.Options) broker.Option { func Options(opts nats.Options) broker.Option {
@ -16,10 +15,5 @@ func Options(opts nats.Options) broker.Option {
// DrainConnection will drain subscription on close // DrainConnection will drain subscription on close
func DrainConnection() broker.Option { func DrainConnection() broker.Option {
return setBrokerOption(drainConnectionKey{}, true) return setBrokerOption(drainConnectionKey{}, struct{}{})
}
// DrainSubscription will drain pending messages when unsubscribe
func DrainSubscription() broker.SubscribeOption {
return setSubscribeOption(drainSubscriptionKey{}, true)
} }

View File

@ -1,14 +0,0 @@
package client
import (
"bytes"
)
type buffer struct {
*bytes.Buffer
}
func (b *buffer) Close() error {
b.Buffer.Reset()
return nil
}

View File

@ -10,7 +10,7 @@ import (
// Client is the interface used to make requests to services. // Client is the interface used to make requests to services.
// It supports Request/Response via Transport and Publishing via the Broker. // It supports Request/Response via Transport and Publishing via the Broker.
// It also supports bidiectional streaming of requests. // It also supports bidirectional streaming of requests.
type Client interface { type Client interface {
Init(...Option) error Init(...Option) error
Options() Options Options() Options

View File

@ -14,13 +14,11 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.0-123", Id: "foo-1.0.0-123",
Address: "localhost", Address: "localhost:9999",
Port: 9999,
}, },
{ {
Id: "foo-1.0.0-321", Id: "foo-1.0.0-321",
Address: "localhost", Address: "localhost:9999",
Port: 9999,
}, },
}, },
}, },
@ -30,8 +28,7 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.1-321", Id: "foo-1.0.1-321",
Address: "localhost", Address: "localhost:6666",
Port: 6666,
}, },
}, },
}, },
@ -41,8 +38,7 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.3-345", Id: "foo-1.0.3-345",
Address: "localhost", Address: "localhost:8888",
Port: 8888,
}, },
}, },
}, },

View File

@ -1,14 +0,0 @@
package grpc
import (
"bytes"
)
type buffer struct {
*bytes.Buffer
}
func (b *buffer) Close() error {
b.Buffer.Reset()
return nil
}

View File

@ -2,12 +2,18 @@ package grpc
import ( import (
"fmt" "fmt"
"strings"
b "bytes"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
"github.com/micro/go-micro/codec" "github.com/micro/go-micro/codec"
"github.com/micro/go-micro/codec/bytes"
"github.com/micro/go-micro/codec/jsonrpc" "github.com/micro/go-micro/codec/jsonrpc"
"github.com/micro/go-micro/codec/protorpc" "github.com/micro/go-micro/codec/protorpc"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding"
) )
@ -16,6 +22,8 @@ type protoCodec struct{}
type bytesCodec struct{} type bytesCodec struct{}
type wrapCodec struct{ encoding.Codec } type wrapCodec struct{ encoding.Codec }
var jsonpbMarshaler = &jsonpb.Marshaler{}
var ( var (
defaultGRPCCodecs = map[string]encoding.Codec{ defaultGRPCCodecs = map[string]encoding.Codec{
"application/json": jsonCodec{}, "application/json": jsonCodec{},
@ -52,7 +60,28 @@ func (w wrapCodec) String() string {
return w.Codec.Name() return w.Codec.Name()
} }
func (w wrapCodec) Marshal(v interface{}) ([]byte, error) {
b, ok := v.(*bytes.Frame)
if ok {
return b.Data, nil
}
return w.Codec.Marshal(v)
}
func (w wrapCodec) Unmarshal(data []byte, v interface{}) error {
b, ok := v.(*bytes.Frame)
if ok {
b.Data = data
return nil
}
return w.Codec.Unmarshal(data, v)
}
func (protoCodec) Marshal(v interface{}) ([]byte, error) { func (protoCodec) Marshal(v interface{}) ([]byte, error) {
b, ok := v.(*bytes.Frame)
if ok {
return b.Data, nil
}
return proto.Marshal(v.(proto.Message)) return proto.Marshal(v.(proto.Message))
} }
@ -86,13 +115,79 @@ func (bytesCodec) Name() string {
} }
func (jsonCodec) Marshal(v interface{}) ([]byte, error) { func (jsonCodec) Marshal(v interface{}) ([]byte, error) {
if pb, ok := v.(proto.Message); ok {
s, err := jsonpbMarshaler.MarshalToString(pb)
return []byte(s), err
}
return json.Marshal(v) return json.Marshal(v)
} }
func (jsonCodec) Unmarshal(data []byte, v interface{}) error { func (jsonCodec) Unmarshal(data []byte, v interface{}) error {
if pb, ok := v.(proto.Message); ok {
return jsonpb.Unmarshal(b.NewReader(data), pb)
}
return json.Unmarshal(data, v) return json.Unmarshal(data, v)
} }
func (jsonCodec) Name() string { func (jsonCodec) Name() string {
return "json" return "json"
} }
type grpcCodec struct {
// headers
id string
target string
method string
endpoint string
s grpc.ClientStream
c encoding.Codec
}
func (g *grpcCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error {
md, err := g.s.Header()
if err != nil {
return err
}
if m == nil {
m = new(codec.Message)
}
if m.Header == nil {
m.Header = make(map[string]string)
}
for k, v := range md {
m.Header[k] = strings.Join(v, ",")
}
m.Id = g.id
m.Target = g.target
m.Method = g.method
m.Endpoint = g.endpoint
return nil
}
func (g *grpcCodec) ReadBody(v interface{}) error {
if f, ok := v.(*bytes.Frame); ok {
return g.s.RecvMsg(f)
}
return g.s.RecvMsg(v)
}
func (g *grpcCodec) Write(m *codec.Message, v interface{}) error {
// if we don't have a body
if v != nil {
return g.s.SendMsg(v)
}
// write the body using the framing codec
return g.s.SendMsg(&bytes.Frame{m.Body})
}
func (g *grpcCodec) Close() error {
return g.s.CloseSend()
}
func (g *grpcCodec) String() string {
return g.c.Name()
}

View File

@ -2,7 +2,6 @@
package grpc package grpc
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
@ -12,13 +11,14 @@ import (
"github.com/micro/go-micro/broker" "github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/codec" "github.com/micro/go-micro/codec"
"github.com/micro/go-micro/errors" "github.com/micro/go-micro/errors"
"github.com/micro/go-micro/metadata" "github.com/micro/go-micro/metadata"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/buf"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding"
@ -32,8 +32,9 @@ type grpcClient struct {
} }
func init() { func init() {
encoding.RegisterCodec(jsonCodec{}) encoding.RegisterCodec(wrapCodec{jsonCodec{}})
encoding.RegisterCodec(bytesCodec{}) encoding.RegisterCodec(wrapCodec{protoCodec{}})
encoding.RegisterCodec(wrapCodec{bytesCodec{}})
} }
// secure returns the dial option for whether its a secure or insecure connection // secure returns the dial option for whether its a secure or insecure connection
@ -58,14 +59,14 @@ func (g *grpcClient) next(request client.Request, opts client.CallOptions) (sele
// get proxy address // get proxy address
if prx := os.Getenv("MICRO_PROXY_ADDRESS"); len(prx) > 0 { if prx := os.Getenv("MICRO_PROXY_ADDRESS"); len(prx) > 0 {
opts.Address = prx opts.Address = []string{prx}
} }
// return remote address // return remote address
if len(opts.Address) > 0 { if len(opts.Address) > 0 {
return func() (*registry.Node, error) { return func() (*registry.Node, error) {
return &registry.Node{ return &registry.Node{
Address: opts.Address, Address: opts.Address[0],
}, nil }, nil
}, nil }, nil
} }
@ -83,9 +84,6 @@ func (g *grpcClient) next(request client.Request, opts client.CallOptions) (sele
func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error { func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
address := node.Address address := node.Address
if node.Port > 0 {
address = fmt.Sprintf("%s:%d", address, node.Port)
}
header := make(map[string]string) header := make(map[string]string)
if md, ok := metadata.FromContext(ctx); ok { if md, ok := metadata.FromContext(ctx); ok {
@ -129,7 +127,7 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
ch := make(chan error, 1) ch := make(chan error, 1)
go func() { go func() {
err := cc.Invoke(ctx, methodToGRPC(req.Endpoint(), req.Body()), req.Body(), rsp, grpc.ForceCodec(cf)) err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpc.CallContentSubtype(cf.Name()))
ch <- microError(err) ch <- microError(err)
}() }()
@ -145,9 +143,6 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client.Request, opts client.CallOptions) (client.Stream, error) { func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client.Request, opts client.CallOptions) (client.Stream, error) {
address := node.Address address := node.Address
if node.Port > 0 {
address = fmt.Sprintf("%s:%d", address, node.Port)
}
header := make(map[string]string) header := make(map[string]string)
if md, ok := metadata.FromContext(ctx); ok { if md, ok := metadata.FromContext(ctx); ok {
@ -177,7 +172,10 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
dialCtx, cancel = context.WithCancel(ctx) dialCtx, cancel = context.WithCancel(ctx)
} }
defer cancel() defer cancel()
cc, err := grpc.DialContext(dialCtx, address, grpc.WithDefaultCallOptions(grpc.ForceCodec(cf)), g.secure())
wc := wrapCodec{cf}
cc, err := grpc.DialContext(dialCtx, address, grpc.WithDefaultCallOptions(grpc.ForceCodec(wc)), g.secure())
if err != nil { if err != nil {
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
} }
@ -188,15 +186,26 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
ServerStreams: true, ServerStreams: true,
} }
st, err := cc.NewStream(ctx, desc, methodToGRPC(req.Endpoint(), req.Body())) st, err := cc.NewStream(ctx, desc, methodToGRPC(req.Service(), req.Endpoint()))
if err != nil { if err != nil {
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err)) return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err))
} }
codec := &grpcCodec{
s: st,
c: wc,
}
// set request codec
if r, ok := req.(*grpcRequest); ok {
r.codec = codec
}
rsp := &response{ rsp := &response{
conn: cc, conn: cc,
stream: st, stream: st,
codec: cf, codec: cf,
gcodec: codec,
} }
return &grpcStream{ return &grpcStream{
@ -280,7 +289,7 @@ func (g *grpcClient) Options() client.Options {
} }
func (g *grpcClient) NewMessage(topic string, msg interface{}, opts ...client.MessageOption) client.Message { func (g *grpcClient) NewMessage(topic string, msg interface{}, opts ...client.MessageOption) client.Message {
return newGRPCPublication(topic, msg, "application/octet-stream") return newGRPCEvent(topic, msg, g.opts.ContentType, opts...)
} }
func (g *grpcClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request { func (g *grpcClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
@ -357,9 +366,9 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
var gerr error var gerr error
for i := 0; i <= callOpts.Retries; i++ { for i := 0; i <= callOpts.Retries; i++ {
go func() { go func(i int) {
ch <- call(i) ch <- call(i)
}() }(i)
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -440,10 +449,10 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
var grr error var grr error
for i := 0; i <= callOpts.Retries; i++ { for i := 0; i <= callOpts.Retries; i++ {
go func() { go func(i int) {
s, err := call(i) s, err := call(i)
ch <- response{s, err} ch <- response{s, err}
}() }(i)
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -482,8 +491,9 @@ func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...clie
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", err.Error())
} }
b := &buffer{bytes.NewBuffer(nil)} b := buf.New(nil)
if err := cf(b).Write(&codec.Message{Type: codec.Publication}, p.Payload()); err != nil {
if err := cf(b).Write(&codec.Message{Type: codec.Event}, p.Payload()); err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", err.Error())
} }

View File

@ -3,14 +3,12 @@ package grpc
import ( import (
"context" "context"
"net" "net"
"strconv"
"strings"
"testing" "testing"
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/registry/memory" "github.com/micro/go-micro/registry/memory"
"github.com/micro/go-micro/selector"
pgrpc "google.golang.org/grpc" pgrpc "google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld" pb "google.golang.org/grpc/examples/helloworld/helloworld"
) )
@ -36,22 +34,17 @@ func TestGRPCClient(t *testing.T) {
go s.Serve(l) go s.Serve(l)
defer s.Stop() defer s.Stop()
parts := strings.Split(l.Addr().String(), ":")
port, _ := strconv.Atoi(parts[len(parts)-1])
addr := strings.Join(parts[:len(parts)-1], ":")
// create mock registry // create mock registry
r := memory.NewRegistry() r := memory.NewRegistry()
// register service // register service
r.Register(&registry.Service{ r.Register(&registry.Service{
Name: "test", Name: "helloworld",
Version: "test", Version: "test",
Nodes: []*registry.Node{ Nodes: []*registry.Node{
&registry.Node{ &registry.Node{
Id: "test-1", Id: "test-1",
Address: addr, Address: l.Addr().String(),
Port: port,
}, },
}, },
}) })
@ -73,7 +66,7 @@ func TestGRPCClient(t *testing.T) {
} }
for _, method := range testMethods { for _, method := range testMethods {
req := c.NewRequest("test", method, &pb.HelloRequest{ req := c.NewRequest("helloworld", method, &pb.HelloRequest{
Name: "John", Name: "John",
}) })

View File

@ -4,13 +4,13 @@ import (
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
) )
type grpcPublication struct { type grpcEvent struct {
topic string topic string
contentType string contentType string
payload interface{} payload interface{}
} }
func newGRPCPublication(topic string, payload interface{}, contentType string, opts ...client.MessageOption) client.Message { func newGRPCEvent(topic string, payload interface{}, contentType string, opts ...client.MessageOption) client.Message {
var options client.MessageOptions var options client.MessageOptions
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
@ -20,21 +20,21 @@ func newGRPCPublication(topic string, payload interface{}, contentType string, o
contentType = options.ContentType contentType = options.ContentType
} }
return &grpcPublication{ return &grpcEvent{
payload: payload, payload: payload,
topic: topic, topic: topic,
contentType: contentType, contentType: contentType,
} }
} }
func (g *grpcPublication) ContentType() string { func (g *grpcEvent) ContentType() string {
return g.contentType return g.contentType
} }
func (g *grpcPublication) Topic() string { func (g *grpcEvent) Topic() string {
return g.topic return g.topic
} }
func (g *grpcPublication) Payload() interface{} { func (g *grpcEvent) Payload() interface{} {
return g.payload return g.payload
} }

View File

@ -2,7 +2,6 @@ package grpc
import ( import (
"fmt" "fmt"
"reflect"
"strings" "strings"
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
@ -18,30 +17,25 @@ type grpcRequest struct {
codec codec.Codec codec codec.Codec
} }
func methodToGRPC(method string, request interface{}) string { // service Struct.Method /service.Struct/Method
func methodToGRPC(service, method string) string {
// no method or already grpc method // no method or already grpc method
if len(method) == 0 || method[0] == '/' { if len(method) == 0 || method[0] == '/' {
return method return method
} }
// can't operate on nil request
t := reflect.TypeOf(request)
if t == nil {
return method
}
// dereference
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// get package name
pParts := strings.Split(t.PkgPath(), "/")
pkg := pParts[len(pParts)-1]
// assume method is Foo.Bar // assume method is Foo.Bar
mParts := strings.Split(method, ".") mParts := strings.Split(method, ".")
if len(mParts) != 2 { if len(mParts) != 2 {
return method return method
} }
if len(service) == 0 {
return fmt.Sprintf("/%s/%s", mParts[0], mParts[1])
}
// return /pkg.Foo/Bar // return /pkg.Foo/Bar
return fmt.Sprintf("/%s.%s/%s", pkg, mParts[0], mParts[1]) return fmt.Sprintf("/%s.%s/%s", service, mParts[0], mParts[1])
} }
func newGRPCRequest(service, method string, request interface{}, contentType string, reqOpts ...client.RequestOption) client.Request { func newGRPCRequest(service, method string, request interface{}, contentType string, reqOpts ...client.RequestOption) client.Request {

View File

@ -2,45 +2,38 @@ package grpc
import ( import (
"testing" "testing"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
) )
func TestMethodToGRPC(t *testing.T) { func TestMethodToGRPC(t *testing.T) {
testData := []struct { testData := []struct {
service string
method string method string
expect string expect string
request interface{}
}{ }{
{ {
"helloworld",
"Greeter.SayHello", "Greeter.SayHello",
"/helloworld.Greeter/SayHello", "/helloworld.Greeter/SayHello",
new(pb.HelloRequest),
}, },
{ {
"helloworld",
"/helloworld.Greeter/SayHello", "/helloworld.Greeter/SayHello",
"/helloworld.Greeter/SayHello", "/helloworld.Greeter/SayHello",
new(pb.HelloRequest),
}, },
{ {
"",
"/helloworld.Greeter/SayHello",
"/helloworld.Greeter/SayHello",
},
{
"",
"Greeter.SayHello", "Greeter.SayHello",
"/helloworld.Greeter/SayHello", "/Greeter/SayHello",
pb.HelloRequest{},
},
{
"/helloworld.Greeter/SayHello",
"/helloworld.Greeter/SayHello",
pb.HelloRequest{},
},
{
"Greeter.SayHello",
"Greeter.SayHello",
nil,
}, },
} }
for _, d := range testData { for _, d := range testData {
method := methodToGRPC(d.method, d.request) method := methodToGRPC(d.service, d.method)
if method != d.expect { if method != d.expect {
t.Fatalf("expected %s got %s", d.expect, method) t.Fatalf("expected %s got %s", d.expect, method)
} }

View File

@ -4,6 +4,7 @@ import (
"strings" "strings"
"github.com/micro/go-micro/codec" "github.com/micro/go-micro/codec"
"github.com/micro/go-micro/codec/bytes"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding"
) )
@ -12,11 +13,12 @@ type response struct {
conn *grpc.ClientConn conn *grpc.ClientConn
stream grpc.ClientStream stream grpc.ClientStream
codec encoding.Codec codec encoding.Codec
gcodec codec.Codec
} }
// Read the response // Read the response
func (r *response) Codec() codec.Reader { func (r *response) Codec() codec.Reader {
return nil return r.gcodec
} }
// read the header // read the header
@ -34,5 +36,9 @@ func (r *response) Header() map[string]string {
// Read the undecoded response // Read the undecoded response
func (r *response) Read() ([]byte, error) { func (r *response) Read() ([]byte, error) {
return nil, nil f := &bytes.Frame{}
if err := r.gcodec.ReadBody(f); err != nil {
return nil, err
}
return f.Data, nil
} }

View File

@ -5,9 +5,9 @@ import (
"time" "time"
"github.com/micro/go-micro/broker" "github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/codec" "github.com/micro/go-micro/codec"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
) )
@ -43,8 +43,8 @@ type Options struct {
type CallOptions struct { type CallOptions struct {
SelectOptions []selector.SelectOption SelectOptions []selector.SelectOption
// Address of remote host // Address of remote hosts
Address string Address []string
// Backoff func // Backoff func
Backoff BackoffFunc Backoff BackoffFunc
// Check if retriable func // Check if retriable func
@ -245,8 +245,8 @@ func WithExchange(e string) PublishOption {
} }
} }
// WithAddress sets the remote address to use rather than using service discovery // WithAddress sets the remote addresses to use rather than using service discovery
func WithAddress(a string) CallOption { func WithAddress(a ...string) CallOption {
return func(o *CallOptions) { return func(o *CallOptions) {
o.Address = a o.Address = a
} }

114
client/pool/default.go Normal file
View File

@ -0,0 +1,114 @@
package pool
import (
"sync"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/transport"
)
type pool struct {
size int
ttl time.Duration
tr transport.Transport
sync.Mutex
conns map[string][]*poolConn
}
type poolConn struct {
transport.Client
id string
created time.Time
}
func newPool(options Options) *pool {
return &pool{
size: options.Size,
tr: options.Transport,
ttl: options.TTL,
conns: make(map[string][]*poolConn),
}
}
func (p *pool) Close() error {
p.Lock()
for k, c := range p.conns {
for _, conn := range c {
conn.Client.Close()
}
delete(p.conns, k)
}
p.Unlock()
return nil
}
// NoOp the Close since we manage it
func (p *poolConn) Close() error {
return nil
}
func (p *poolConn) Id() string {
return p.id
}
func (p *poolConn) Created() time.Time {
return p.created
}
func (p *pool) Get(addr string, opts ...transport.DialOption) (Conn, error) {
p.Lock()
conns := p.conns[addr]
// while we have conns check age and then return one
// otherwise we'll create a new conn
for len(conns) > 0 {
conn := conns[len(conns)-1]
conns = conns[:len(conns)-1]
p.conns[addr] = conns
// if conn is old kill it and move on
if d := time.Since(conn.Created()); d > p.ttl {
conn.Client.Close()
continue
}
// we got a good conn, lets unlock and return it
p.Unlock()
return conn, nil
}
p.Unlock()
// create new conn
c, err := p.tr.Dial(addr, opts...)
if err != nil {
return nil, err
}
return &poolConn{
Client: c,
id: uuid.New().String(),
created: time.Now(),
}, nil
}
func (p *pool) Release(conn Conn, err error) error {
// don't store the conn if it has errored
if err != nil {
return conn.(*poolConn).Client.Close()
}
// otherwise put it back for reuse
p.Lock()
conns := p.conns[conn.Remote()]
if len(conns) >= p.size {
p.Unlock()
return conn.(*poolConn).Client.Close()
}
p.conns[conn.Remote()] = append(conns, conn.(*poolConn))
p.Unlock()
return nil
}

View File

@ -1,4 +1,4 @@
package client package pool
import ( import (
"testing" "testing"
@ -9,12 +9,17 @@ import (
) )
func testPool(t *testing.T, size int, ttl time.Duration) { func testPool(t *testing.T, size int, ttl time.Duration) {
// zero pool
p := newPool(size, ttl)
// mock transport // mock transport
tr := memory.NewTransport() tr := memory.NewTransport()
options := Options{
TTL: ttl,
Size: size,
Transport: tr,
}
// zero pool
p := newPool(options)
// listen // listen
l, err := tr.Listen(":0") l, err := tr.Listen(":0")
if err != nil { if err != nil {
@ -43,7 +48,7 @@ func testPool(t *testing.T, size int, ttl time.Duration) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
// get a conn // get a conn
c, err := p.getConn(l.Addr(), tr) c, err := p.Get(l.Addr())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -67,7 +72,7 @@ func testPool(t *testing.T, size int, ttl time.Duration) {
} }
// release the conn // release the conn
p.release(l.Addr(), c, nil) p.Release(c, nil)
p.Lock() p.Lock()
if i := len(p.conns[l.Addr()]); i > size { if i := len(p.conns[l.Addr()]); i > size {
@ -78,7 +83,7 @@ func testPool(t *testing.T, size int, ttl time.Duration) {
} }
} }
func TestRPCPool(t *testing.T) { func TestClientPool(t *testing.T) {
testPool(t, 0, time.Minute) testPool(t, 0, time.Minute)
testPool(t, 2, time.Minute) testPool(t, 2, time.Minute)
} }

33
client/pool/options.go Normal file
View File

@ -0,0 +1,33 @@
package pool
import (
"time"
"github.com/micro/go-micro/transport"
)
type Options struct {
Transport transport.Transport
TTL time.Duration
Size int
}
type Option func(*Options)
func Size(i int) Option {
return func(o *Options) {
o.Size = i
}
}
func Transport(t transport.Transport) Option {
return func(o *Options) {
o.Transport = t
}
}
func TTL(t time.Duration) Option {
return func(o *Options) {
o.TTL = t
}
}

35
client/pool/pool.go Normal file
View File

@ -0,0 +1,35 @@
// Package pool is a connection pool
package pool
import (
"time"
"github.com/micro/go-micro/transport"
)
// Pool is an interface for connection pooling
type Pool interface {
// Close the pool
Close() error
// Get a connection
Get(addr string, opts ...transport.DialOption) (Conn, error)
// Releaes the connection
Release(c Conn, status error) error
}
type Conn interface {
// unique id of connection
Id() string
// time it was created
Created() time.Time
// embedded connection
transport.Client
}
func NewPool(opts ...Option) Pool {
var options Options
for _, o := range opts {
o(&options)
}
return newPool(options)
}

View File

@ -0,0 +1,203 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/client/proto/client.proto
package go_micro_client
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 Micro service
type MicroService interface {
// Call allows a single request to be made
Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
// Stream is a bidirectional stream
Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error)
// Publish publishes a message and returns an empty Message
Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error)
}
type microService struct {
c client.Client
name string
}
func NewMicroService(name string, c client.Client) MicroService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.client"
}
return &microService{
c: c,
name: name,
}
}
func (c *microService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.name, "Micro.Call", in)
out := new(Response)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *microService) Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error) {
req := c.c.NewRequest(c.name, "Micro.Stream", &Request{})
stream, err := c.c.Stream(ctx, req, opts...)
if err != nil {
return nil, err
}
return &microServiceStream{stream}, nil
}
type Micro_StreamService interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*Request) error
Recv() (*Response, error)
}
type microServiceStream struct {
stream client.Stream
}
func (x *microServiceStream) Close() error {
return x.stream.Close()
}
func (x *microServiceStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *microServiceStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *microServiceStream) Send(m *Request) error {
return x.stream.Send(m)
}
func (x *microServiceStream) Recv() (*Response, error) {
m := new(Response)
err := x.stream.Recv(m)
if err != nil {
return nil, err
}
return m, nil
}
func (c *microService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) {
req := c.c.NewRequest(c.name, "Micro.Publish", in)
out := new(Message)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Micro service
type MicroHandler interface {
// Call allows a single request to be made
Call(context.Context, *Request, *Response) error
// Stream is a bidirectional stream
Stream(context.Context, Micro_StreamStream) error
// Publish publishes a message and returns an empty Message
Publish(context.Context, *Message, *Message) error
}
func RegisterMicroHandler(s server.Server, hdlr MicroHandler, opts ...server.HandlerOption) error {
type micro interface {
Call(ctx context.Context, in *Request, out *Response) error
Stream(ctx context.Context, stream server.Stream) error
Publish(ctx context.Context, in *Message, out *Message) error
}
type Micro struct {
micro
}
h := &microHandler{hdlr}
return s.Handle(s.NewHandler(&Micro{h}, opts...))
}
type microHandler struct {
MicroHandler
}
func (h *microHandler) Call(ctx context.Context, in *Request, out *Response) error {
return h.MicroHandler.Call(ctx, in, out)
}
func (h *microHandler) Stream(ctx context.Context, stream server.Stream) error {
return h.MicroHandler.Stream(ctx, &microStreamStream{stream})
}
type Micro_StreamStream interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*Response) error
Recv() (*Request, error)
}
type microStreamStream struct {
stream server.Stream
}
func (x *microStreamStream) Close() error {
return x.stream.Close()
}
func (x *microStreamStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *microStreamStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *microStreamStream) Send(m *Response) error {
return x.stream.Send(m)
}
func (x *microStreamStream) Recv() (*Request, error) {
m := new(Request)
if err := x.stream.Recv(m); err != nil {
return nil, err
}
return m, nil
}
func (h *microHandler) Publish(ctx context.Context, in *Message, out *Message) error {
return h.MicroHandler.Publish(ctx, in, out)
}

388
client/proto/client.pb.go Normal file
View File

@ -0,0 +1,388 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/client/proto/client.proto
package go_micro_client
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 Request struct {
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) {
return fileDescriptor_7d733ae29171347b, []int{0}
}
func (m *Request) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Request.Unmarshal(m, b)
}
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
}
func (m *Request) XXX_Merge(src proto.Message) {
xxx_messageInfo_Request.Merge(m, src)
}
func (m *Request) XXX_Size() int {
return xxx_messageInfo_Request.Size(m)
}
func (m *Request) XXX_DiscardUnknown() {
xxx_messageInfo_Request.DiscardUnknown(m)
}
var xxx_messageInfo_Request proto.InternalMessageInfo
func (m *Request) GetService() string {
if m != nil {
return m.Service
}
return ""
}
func (m *Request) GetEndpoint() string {
if m != nil {
return m.Endpoint
}
return ""
}
func (m *Request) GetContentType() string {
if m != nil {
return m.ContentType
}
return ""
}
func (m *Request) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
type Response struct {
Body []byte `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) {
return fileDescriptor_7d733ae29171347b, []int{1}
}
func (m *Response) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Response.Unmarshal(m, b)
}
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
}
func (m *Response) XXX_Merge(src proto.Message) {
xxx_messageInfo_Response.Merge(m, src)
}
func (m *Response) XXX_Size() int {
return xxx_messageInfo_Response.Size(m)
}
func (m *Response) XXX_DiscardUnknown() {
xxx_messageInfo_Response.DiscardUnknown(m)
}
var xxx_messageInfo_Response proto.InternalMessageInfo
func (m *Response) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
type Message struct {
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
Body []byte `protobuf:"bytes,3,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_7d733ae29171347b, []int{2}
}
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) GetTopic() string {
if m != nil {
return m.Topic
}
return ""
}
func (m *Message) GetContentType() string {
if m != nil {
return m.ContentType
}
return ""
}
func (m *Message) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
func init() {
proto.RegisterType((*Request)(nil), "go.micro.client.Request")
proto.RegisterType((*Response)(nil), "go.micro.client.Response")
proto.RegisterType((*Message)(nil), "go.micro.client.Message")
}
func init() {
proto.RegisterFile("micro/go-micro/client/proto/client.proto", fileDescriptor_7d733ae29171347b)
}
var fileDescriptor_7d733ae29171347b = []byte{
// 270 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x3f, 0x4f, 0xc3, 0x30,
0x10, 0xc5, 0xeb, 0xfe, 0x4b, 0x39, 0x2a, 0x21, 0x9d, 0x18, 0x4c, 0x06, 0x54, 0x32, 0x65, 0xc1,
0x45, 0x30, 0x23, 0x86, 0xce, 0x95, 0x50, 0x40, 0xac, 0x28, 0x71, 0x4f, 0xc1, 0x52, 0x6a, 0x9b,
0xd8, 0xad, 0x94, 0xef, 0xc8, 0x87, 0x42, 0x38, 0x29, 0x45, 0xd0, 0x2e, 0x6c, 0xf7, 0xee, 0x67,
0xbd, 0x3b, 0xbf, 0x83, 0x74, 0xad, 0x64, 0x6d, 0xe6, 0xa5, 0xb9, 0x6e, 0x0b, 0x59, 0x29, 0xd2,
0x7e, 0x6e, 0x6b, 0xe3, 0x77, 0x42, 0x04, 0x81, 0x67, 0xa5, 0x11, 0xe1, 0x8d, 0x68, 0xdb, 0xc9,
0x16, 0xa2, 0x8c, 0xde, 0x37, 0xe4, 0x3c, 0x72, 0x88, 0x1c, 0xd5, 0x5b, 0x25, 0x89, 0xb3, 0x19,
0x4b, 0x4f, 0xb2, 0x9d, 0xc4, 0x18, 0x26, 0xa4, 0x57, 0xd6, 0x28, 0xed, 0x79, 0x3f, 0xa0, 0x6f,
0x8d, 0x57, 0x30, 0x95, 0x46, 0x7b, 0xd2, 0xfe, 0xd5, 0x37, 0x96, 0xf8, 0x20, 0xf0, 0xd3, 0xae,
0xf7, 0xdc, 0x58, 0x42, 0x84, 0x61, 0x61, 0x56, 0x0d, 0x1f, 0xce, 0x58, 0x3a, 0xcd, 0x42, 0x9d,
0x5c, 0xc2, 0x24, 0x23, 0x67, 0x8d, 0x76, 0x7b, 0xce, 0x7e, 0xf0, 0x17, 0x88, 0x96, 0xe4, 0x5c,
0x5e, 0x12, 0x9e, 0xc3, 0xc8, 0x1b, 0xab, 0x64, 0xb7, 0x55, 0x2b, 0xfe, 0xcc, 0xed, 0x1f, 0x9f,
0x3b, 0xd8, 0xfb, 0xde, 0x7e, 0x30, 0x18, 0x2d, 0xbf, 0x02, 0xc0, 0x7b, 0x18, 0x2e, 0xf2, 0xaa,
0x42, 0x2e, 0x7e, 0x65, 0x22, 0xba, 0x40, 0xe2, 0x8b, 0x03, 0xa4, 0x5d, 0x39, 0xe9, 0xe1, 0x02,
0xc6, 0x4f, 0xbe, 0xa6, 0x7c, 0xfd, 0x4f, 0x83, 0x94, 0xdd, 0x30, 0x7c, 0x80, 0xe8, 0x71, 0x53,
0x54, 0xca, 0xbd, 0x1d, 0x70, 0xe9, 0xfe, 0x1f, 0x1f, 0x25, 0x49, 0xaf, 0x18, 0x87, 0xb3, 0xde,
0x7d, 0x06, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x63, 0x94, 0x1a, 0x02, 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
// MicroClient is the client API for Micro service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type MicroClient interface {
// Call allows a single request to be made
Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
// Stream is a bidirectional stream
Stream(ctx context.Context, opts ...grpc.CallOption) (Micro_StreamClient, error)
// Publish publishes a message and returns an empty Message
Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error)
}
type microClient struct {
cc *grpc.ClientConn
}
func NewMicroClient(cc *grpc.ClientConn) MicroClient {
return &microClient{cc}
}
func (c *microClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := c.cc.Invoke(ctx, "/go.micro.client.Micro/Call", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *microClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Micro_StreamClient, error) {
stream, err := c.cc.NewStream(ctx, &_Micro_serviceDesc.Streams[0], "/go.micro.client.Micro/Stream", opts...)
if err != nil {
return nil, err
}
x := &microStreamClient{stream}
return x, nil
}
type Micro_StreamClient interface {
Send(*Request) error
Recv() (*Response, error)
grpc.ClientStream
}
type microStreamClient struct {
grpc.ClientStream
}
func (x *microStreamClient) Send(m *Request) error {
return x.ClientStream.SendMsg(m)
}
func (x *microStreamClient) Recv() (*Response, error) {
m := new(Response)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *microClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
out := new(Message)
err := c.cc.Invoke(ctx, "/go.micro.client.Micro/Publish", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MicroServer is the server API for Micro service.
type MicroServer interface {
// Call allows a single request to be made
Call(context.Context, *Request) (*Response, error)
// Stream is a bidirectional stream
Stream(Micro_StreamServer) error
// Publish publishes a message and returns an empty Message
Publish(context.Context, *Message) (*Message, error)
}
func RegisterMicroServer(s *grpc.Server, srv MicroServer) {
s.RegisterService(&_Micro_serviceDesc, srv)
}
func _Micro_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MicroServer).Call(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.client.Micro/Call",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MicroServer).Call(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
func _Micro_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(MicroServer).Stream(&microStreamServer{stream})
}
type Micro_StreamServer interface {
Send(*Response) error
Recv() (*Request, error)
grpc.ServerStream
}
type microStreamServer struct {
grpc.ServerStream
}
func (x *microStreamServer) Send(m *Response) error {
return x.ServerStream.SendMsg(m)
}
func (x *microStreamServer) Recv() (*Request, error) {
m := new(Request)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _Micro_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Message)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MicroServer).Publish(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.client.Micro/Publish",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MicroServer).Publish(ctx, req.(*Message))
}
return interceptor(ctx, in, info, handler)
}
var _Micro_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.client.Micro",
HandlerType: (*MicroServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Call",
Handler: _Micro_Call_Handler,
},
{
MethodName: "Publish",
Handler: _Micro_Publish_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Stream",
Handler: _Micro_Stream_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "micro/go-micro/client/proto/client.proto",
}

30
client/proto/client.proto Normal file
View File

@ -0,0 +1,30 @@
syntax = "proto3";
package go.micro.client;
// Micro is the micro client interface
service Micro {
// Call allows a single request to be made
rpc Call(Request) returns (Response) {};
// Stream is a bidirectional stream
rpc Stream(stream Request) returns (stream Response) {};
// Publish publishes a message and returns an empty Message
rpc Publish(Message) returns (Message) {};
}
message Request {
string service = 1;
string endpoint = 2;
string content_type = 3;
bytes body = 4;
}
message Response {
bytes body = 1;
}
message Message {
string topic = 1;
string content_type = 2;
bytes body = 3;
}

View File

@ -1,40 +1,45 @@
package client package client
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"net"
"os" "os"
"strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/micro/go-micro/broker" "github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client/pool"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/codec" "github.com/micro/go-micro/codec"
"github.com/micro/go-micro/errors" "github.com/micro/go-micro/errors"
"github.com/micro/go-micro/metadata" "github.com/micro/go-micro/metadata"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/buf"
) )
type rpcClient struct { type rpcClient struct {
once sync.Once once sync.Once
opts Options opts Options
pool *pool pool pool.Pool
seq uint64 seq uint64
} }
func newRpcClient(opt ...Option) Client { func newRpcClient(opt ...Option) Client {
opts := newOptions(opt...) opts := newOptions(opt...)
p := pool.NewPool(
pool.Size(opts.PoolSize),
pool.TTL(opts.PoolTTL),
pool.Transport(opts.Transport),
)
rc := &rpcClient{ rc := &rpcClient{
once: sync.Once{}, once: sync.Once{},
opts: opts, opts: opts,
pool: newPool(opts.PoolSize, opts.PoolTTL), pool: p,
seq: 0, seq: 0,
} }
@ -60,9 +65,6 @@ func (r *rpcClient) newCodec(contentType string) (codec.NewCodec, error) {
func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request, resp interface{}, opts CallOptions) error { func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request, resp interface{}, opts CallOptions) error {
address := node.Address address := node.Address
if node.Port > 0 {
address = fmt.Sprintf("%s:%d", address, node.Port)
}
msg := &transport.Message{ msg := &transport.Message{
Header: make(map[string]string), Header: make(map[string]string),
@ -95,13 +97,13 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
} }
var grr error var grr error
c, err := r.pool.getConn(address, r.opts.Transport, transport.WithTimeout(opts.DialTimeout)) c, err := r.pool.Get(address, transport.WithTimeout(opts.DialTimeout))
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", "connection error: %v", err) return errors.InternalServerError("go.micro.client", "connection error: %v", err)
} }
defer func() { defer func() {
// defer execution of release // defer execution of release
r.pool.release(address, c, grr) r.pool.Release(c, grr)
}() }()
seq := atomic.LoadUint64(&r.seq) seq := atomic.LoadUint64(&r.seq)
@ -160,9 +162,6 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request, opts CallOptions) (Stream, error) { func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request, opts CallOptions) (Stream, error) {
address := node.Address address := node.Address
if node.Port > 0 {
address = fmt.Sprintf("%s:%d", address, node.Port)
}
msg := &transport.Message{ msg := &transport.Message{
Header: make(map[string]string), Header: make(map[string]string),
@ -253,17 +252,22 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
func (r *rpcClient) Init(opts ...Option) error { func (r *rpcClient) Init(opts ...Option) error {
size := r.opts.PoolSize size := r.opts.PoolSize
ttl := r.opts.PoolTTL ttl := r.opts.PoolTTL
tr := r.opts.Transport
for _, o := range opts { for _, o := range opts {
o(&r.opts) o(&r.opts)
} }
// update pool configuration if the options changed // update pool configuration if the options changed
if size != r.opts.PoolSize || ttl != r.opts.PoolTTL { if size != r.opts.PoolSize || ttl != r.opts.PoolTTL || tr != r.opts.Transport {
r.pool.Lock() // close existing pool
r.pool.size = r.opts.PoolSize r.pool.Close()
r.pool.ttl = int64(r.opts.PoolTTL.Seconds()) // create new pool
r.pool.Unlock() r.pool = pool.NewPool(
pool.Size(r.opts.PoolSize),
pool.TTL(r.opts.PoolTTL),
pool.Transport(r.opts.Transport),
)
} }
return nil return nil
@ -283,29 +287,26 @@ func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, erro
// get proxy address // get proxy address
if prx := os.Getenv("MICRO_PROXY_ADDRESS"); len(prx) > 0 { if prx := os.Getenv("MICRO_PROXY_ADDRESS"); len(prx) > 0 {
opts.Address = prx opts.Address = []string{prx}
} }
// return remote address // return remote address
if len(opts.Address) > 0 { if len(opts.Address) > 0 {
address := opts.Address nodes := make([]*registry.Node, len(opts.Address))
port := 0
host, sport, err := net.SplitHostPort(opts.Address) for i, address := range opts.Address {
if err == nil { nodes[i] = &registry.Node{
address = host
port, _ = strconv.Atoi(sport)
}
return func() (*registry.Node, error) {
return &registry.Node{
Address: address, Address: address,
Port: port,
// Set the protocol // Set the protocol
Metadata: map[string]string{ Metadata: map[string]string{
"protocol": "mucp", "protocol": "mucp",
}, },
}, nil }
}
// crude return method
return func() (*registry.Node, error) {
return nodes[time.Now().Unix()%int64(len(nodes))], nil
}, nil }, nil
} }
@ -537,10 +538,13 @@ func (r *rpcClient) Publish(ctx context.Context, msg Message, opts ...PublishOpt
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", err.Error())
} }
b := &buffer{bytes.NewBuffer(nil)}
// new buffer
b := buf.New(nil)
if err := cf(b).Write(&codec.Message{ if err := cf(b).Write(&codec.Message{
Target: topic, Target: topic,
Type: codec.Publication, Type: codec.Event,
Header: map[string]string{ Header: map[string]string{
"Micro-Id": id, "Micro-Id": id,
"Micro-Topic": msg.Topic(), "Micro-Topic": msg.Topic(),

View File

@ -5,10 +5,10 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/errors" "github.com/micro/go-micro/errors"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/registry/memory" "github.com/micro/go-micro/registry/memory"
"github.com/micro/go-micro/selector"
) )
func newTestRegistry() registry.Registry { func newTestRegistry() registry.Registry {
@ -22,8 +22,7 @@ func TestCallAddress(t *testing.T) {
var called bool var called bool
service := "test.service" service := "test.service"
endpoint := "Test.Endpoint" endpoint := "Test.Endpoint"
address := "10.1.10.1" address := "10.1.10.1:8080"
port := 8080
wrap := func(cf CallFunc) CallFunc { wrap := func(cf CallFunc) CallFunc {
return func(ctx context.Context, node *registry.Node, req Request, rsp interface{}, opts CallOptions) error { return func(ctx context.Context, node *registry.Node, req Request, rsp interface{}, opts CallOptions) error {
@ -41,10 +40,6 @@ func TestCallAddress(t *testing.T) {
return fmt.Errorf("expected address: %s got %s", address, node.Address) return fmt.Errorf("expected address: %s got %s", address, node.Address)
} }
if node.Port != port {
return fmt.Errorf("expected address: %d got %d", port, node.Port)
}
// don't do the call // don't do the call
return nil return nil
} }
@ -60,7 +55,7 @@ func TestCallAddress(t *testing.T) {
req := c.NewRequest(service, endpoint, nil) req := c.NewRequest(service, endpoint, nil)
// test calling remote address // test calling remote address
if err := c.Call(context.Background(), req, nil, WithAddress(fmt.Sprintf("%s:%d", address, port))); err != nil { if err := c.Call(context.Background(), req, nil, WithAddress(address)); err != nil {
t.Fatal("call with address error", err) t.Fatal("call with address error", err)
} }
@ -114,8 +109,7 @@ func TestCallWrapper(t *testing.T) {
id := "test.1" id := "test.1"
service := "test.service" service := "test.service"
endpoint := "Test.Endpoint" endpoint := "Test.Endpoint"
address := "10.1.10.1" address := "10.1.10.1:8080"
port := 8080
wrap := func(cf CallFunc) CallFunc { wrap := func(cf CallFunc) CallFunc {
return func(ctx context.Context, node *registry.Node, req Request, rsp interface{}, opts CallOptions) error { return func(ctx context.Context, node *registry.Node, req Request, rsp interface{}, opts CallOptions) error {
@ -152,7 +146,6 @@ func TestCallWrapper(t *testing.T) {
&registry.Node{ &registry.Node{
Id: id, Id: id,
Address: address, Address: address,
Port: port,
}, },
}, },
}) })

View File

@ -1,87 +0,0 @@
package client
import (
"sync"
"time"
"github.com/micro/go-micro/transport"
)
type pool struct {
size int
ttl int64
sync.Mutex
conns map[string][]*poolConn
}
type poolConn struct {
transport.Client
created int64
}
func newPool(size int, ttl time.Duration) *pool {
return &pool{
size: size,
ttl: int64(ttl.Seconds()),
conns: make(map[string][]*poolConn),
}
}
// NoOp the Close since we manage it
func (p *poolConn) Close() error {
return nil
}
func (p *pool) getConn(addr string, tr transport.Transport, opts ...transport.DialOption) (*poolConn, error) {
p.Lock()
conns := p.conns[addr]
now := time.Now().Unix()
// while we have conns check age and then return one
// otherwise we'll create a new conn
for len(conns) > 0 {
conn := conns[len(conns)-1]
conns = conns[:len(conns)-1]
p.conns[addr] = conns
// if conn is old kill it and move on
if d := now - conn.created; d > p.ttl {
conn.Client.Close()
continue
}
// we got a good conn, lets unlock and return it
p.Unlock()
return conn, nil
}
p.Unlock()
// create new conn
c, err := tr.Dial(addr, opts...)
if err != nil {
return nil, err
}
return &poolConn{c, time.Now().Unix()}, nil
}
func (p *pool) release(addr string, conn *poolConn, err error) {
// don't store the conn if it has errored
if err != nil {
conn.Client.Close()
return
}
// otherwise put it back for reuse
p.Lock()
conns := p.conns[addr]
if len(conns) >= p.size {
p.Unlock()
conn.Client.Close()
return
}
p.conns[addr] = append(conns, conn)
p.Unlock()
}

View File

@ -14,13 +14,11 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.0-123", Id: "foo-1.0.0-123",
Address: "localhost", Address: "localhost:9999",
Port: 9999,
}, },
{ {
Id: "foo-1.0.0-321", Id: "foo-1.0.0-321",
Address: "localhost", Address: "localhost:9999",
Port: 9999,
}, },
}, },
}, },
@ -30,8 +28,7 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.1-321", Id: "foo-1.0.1-321",
Address: "localhost", Address: "localhost:6666",
Port: 6666,
}, },
}, },
}, },
@ -41,8 +38,7 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.3-345", Id: "foo-1.0.3-345",
Address: "localhost", Address: "localhost:8888",
Port: 8888,
}, },
}, },
}, },

View File

@ -2,11 +2,12 @@
package dns package dns
import ( import (
"fmt"
"net" "net"
"strconv" "strconv"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
) )
type dnsSelector struct { type dnsSelector struct {
@ -66,8 +67,7 @@ func (d *dnsSelector) Select(service string, opts ...selector.SelectOption) (sel
for _, node := range srv { for _, node := range srv {
nodes = append(nodes, &registry.Node{ nodes = append(nodes, &registry.Node{
Id: node.Target, Id: node.Target,
Address: node.Target, Address: fmt.Sprintf("%s:%d", node.Target, node.Port),
Port: int(node.Port),
}) })
} }

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"time" "time"
"github.com/micro/go-micro/selector" "github.com/micro/go-micro/client/selector"
) )
// Set the registry cache ttl // Set the registry cache ttl

View File

@ -2,7 +2,7 @@
package registry package registry
import ( import (
"github.com/micro/go-micro/selector" "github.com/micro/go-micro/client/selector"
) )
// NewSelector returns a new registry selector // NewSelector returns a new registry selector

View File

@ -0,0 +1,272 @@
// Package router is a network/router selector
package router
import (
"context"
"os"
"sort"
"sync"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/router"
pb "github.com/micro/go-micro/router/proto"
)
type routerSelector struct {
opts selector.Options
// the router
r router.Router
// the client we have
c client.Client
// the client for the remote router
rs pb.RouterService
// name of the router
name string
// address of the remote router
addr string
// whether to use the remote router
remote bool
}
type clientKey struct{}
type routerKey struct{}
// getRoutes returns the routes whether they are remote or local
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(
router.QueryService(service),
))
}
// lookup the remote router
var addrs []string
// set the remote address if specified
if len(r.addr) > 0 {
addrs = append(addrs, r.addr)
} else {
// we have a name so we need to check the registry
services, err := r.c.Options().Registry.GetService(r.name)
if err != nil {
return nil, err
}
for _, service := range services {
for _, node := range service.Nodes {
addrs = append(addrs, node.Address)
}
}
}
// no router addresses available
if len(addrs) == 0 {
return nil, selector.ErrNoneAvailable
}
var pbRoutes *pb.LookupResponse
var err error
// TODO: implement backoff and retries
for _, addr := range addrs {
// call the router
pbRoutes, err = r.rs.Lookup(context.Background(), &pb.LookupRequest{
Query: &pb.Query{
Service: service,
},
}, client.WithAddress(addr))
if err != nil {
continue
}
break
}
// errored out
if err != nil {
return nil, err
}
// no routes
if pbRoutes == nil {
return nil, selector.ErrNoneAvailable
}
var routes []router.Route
// convert from pb to []*router.Route
for _, r := range pbRoutes.Routes {
routes = append(routes, router.Route{
Service: r.Service,
Address: r.Address,
Gateway: r.Gateway,
Network: r.Network,
Link: r.Link,
Metric: int(r.Metric),
})
}
return routes, nil
}
func (r *routerSelector) Init(opts ...selector.Option) error {
// no op
return nil
}
func (r *routerSelector) Options() selector.Options {
return r.opts
}
func (r *routerSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) {
// TODO: pull routes asynchronously and cache
routes, err := r.getRoutes(service)
if err != nil {
return nil, err
}
// no routes return not found error
if len(routes) == 0 {
return nil, selector.ErrNotFound
}
// TODO: apply filters by pseudo constructing service
// sort the routes based on metric
sort.Slice(routes, func(i, j int) bool {
return routes[i].Metric < routes[j].Metric
})
// roundrobin assuming routes are in metric preference order
var i int
var mtx sync.Mutex
return func() (*registry.Node, error) {
// get index and increment counter with every call to next
mtx.Lock()
idx := i
i++
mtx.Unlock()
// get route based on idx
route := routes[idx%len(routes)]
// defaults to gateway and no port
address := route.Address
if len(route.Gateway) > 0 {
address = route.Gateway
}
// return as a node
return &registry.Node{
// TODO: add id and metadata if we can
Address: address,
}, nil
}, nil
}
func (r *routerSelector) Mark(service string, node *registry.Node, err error) {
// TODO: pass back metrics or information to the router
return
}
func (r *routerSelector) Reset(service string) {
// TODO: reset the metrics or information at the router
return
}
func (r *routerSelector) Close() error {
// stop the router advertisements
return r.r.Stop()
}
func (r *routerSelector) String() string {
return "router"
}
// NewSelector returns a new router based selector
func NewSelector(opts ...selector.Option) selector.Selector {
options := selector.Options{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
// set default registry if not set
if options.Registry == nil {
options.Registry = registry.DefaultRegistry
}
// try get router from the context
r, ok := options.Context.Value(routerKey{}).(router.Router)
if !ok {
// TODO: Use router.DefaultRouter?
r = router.NewRouter(
router.Registry(options.Registry),
)
}
// try get client from the context
c, ok := options.Context.Value(clientKey{}).(client.Client)
if !ok {
c = client.DefaultClient
}
// get the router from env vars if its a remote service
remote := true
routerName := os.Getenv("MICRO_ROUTER")
routerAddress := os.Getenv("MICRO_ROUTER_ADDRESS")
// start the router advertisements if we're running it locally
if len(routerName) == 0 && len(routerAddress) == 0 {
go r.Advertise()
remote = false
}
return &routerSelector{
opts: options,
// set the internal router
r: r,
// set the client
c: c,
// set the router client
rs: pb.NewRouterService(routerName, c),
// name of the router
name: routerName,
// address of router
addr: routerAddress,
// let ourselves know to use the remote router
remote: remote,
}
}
// WithClient sets the client for the request
func WithClient(c client.Client) selector.Option {
return func(o *selector.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, clientKey{}, c)
}
}
// WithRouter sets the router as an option
func WithRouter(r router.Router) selector.Option {
return func(o *selector.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, routerKey{}, r)
}
}

View File

@ -2,11 +2,8 @@
package static package static
import ( import (
"net" "github.com/micro/go-micro/client/selector"
"strconv"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
) )
// staticSelector is a static selector // staticSelector is a static selector
@ -26,20 +23,10 @@ func (s *staticSelector) Options() selector.Options {
} }
func (s *staticSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) { func (s *staticSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) {
var port int
addr, pt, err := net.SplitHostPort(service)
if err != nil {
addr = service
port = 0
} else {
port, _ = strconv.Atoi(pt)
}
return func() (*registry.Node, error) { return func() (*registry.Node, error) {
return &registry.Node{ return &registry.Node{
Id: service, Id: service,
Address: addr, Address: service,
Port: port,
}, nil }, nil
}, nil }, nil
} }

View File

@ -14,13 +14,11 @@ func TestStrategies(t *testing.T) {
Nodes: []*registry.Node{ Nodes: []*registry.Node{
&registry.Node{ &registry.Node{
Id: "test1-1", Id: "test1-1",
Address: "10.0.0.1", Address: "10.0.0.1:1001",
Port: 1001,
}, },
&registry.Node{ &registry.Node{
Id: "test1-2", Id: "test1-2",
Address: "10.0.0.2", Address: "10.0.0.2:1002",
Port: 1002,
}, },
}, },
}, },
@ -30,13 +28,11 @@ func TestStrategies(t *testing.T) {
Nodes: []*registry.Node{ Nodes: []*registry.Node{
&registry.Node{ &registry.Node{
Id: "test1-3", Id: "test1-3",
Address: "10.0.0.3", Address: "10.0.0.3:1003",
Port: 1003,
}, },
&registry.Node{ &registry.Node{
Id: "test1-4", Id: "test1-4",
Address: "10.0.0.4", Address: "10.0.0.4:1004",
Port: 1004,
}, },
}, },
}, },

View File

@ -9,7 +9,7 @@ const (
Error MessageType = iota Error MessageType = iota
Request Request
Response Response
Publication Event
) )
type MessageType int type MessageType int

View File

@ -33,7 +33,7 @@ func (j *jsonCodec) Write(m *codec.Message, b interface{}) error {
return j.c.Write(m, b) return j.c.Write(m, b)
case codec.Response, codec.Error: case codec.Response, codec.Error:
return j.s.Write(m, b) return j.s.Write(m, b)
case codec.Publication: case codec.Event:
data, err := json.Marshal(b) data, err := json.Marshal(b)
if err != nil { if err != nil {
return err return err
@ -54,7 +54,7 @@ func (j *jsonCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error {
return j.s.ReadHeader(m) return j.s.ReadHeader(m)
case codec.Response: case codec.Response:
return j.c.ReadHeader(m) return j.c.ReadHeader(m)
case codec.Publication: case codec.Event:
_, err := io.Copy(j.buf, j.rwc) _, err := io.Copy(j.buf, j.rwc)
return err return err
default: default:
@ -69,7 +69,7 @@ func (j *jsonCodec) ReadBody(b interface{}) error {
return j.s.ReadBody(b) return j.s.ReadBody(b)
case codec.Response: case codec.Response:
return j.c.ReadBody(b) return j.c.ReadBody(b)
case codec.Publication: case codec.Event:
if b != nil { if b != nil {
return json.Unmarshal(j.buf.Bytes(), b) return json.Unmarshal(j.buf.Bytes(), b)
} }

View File

@ -99,7 +99,7 @@ func (c *protoCodec) Write(m *codec.Message, b interface{}) error {
return err return err
} }
} }
case codec.Publication: case codec.Event:
data, err := proto.Marshal(b.(proto.Message)) data, err := proto.Marshal(b.(proto.Message))
if err != nil { if err != nil {
return err return err
@ -141,7 +141,7 @@ func (c *protoCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error {
m.Method = rtmp.GetServiceMethod() m.Method = rtmp.GetServiceMethod()
m.Id = fmt.Sprintf("%d", rtmp.GetSeq()) m.Id = fmt.Sprintf("%d", rtmp.GetSeq())
m.Error = rtmp.GetError() m.Error = rtmp.GetError()
case codec.Publication: case codec.Event:
_, err := io.Copy(c.buf, c.rwc) _, err := io.Copy(c.buf, c.rwc)
return err return err
default: default:
@ -159,7 +159,7 @@ func (c *protoCodec) ReadBody(b interface{}) error {
if err != nil { if err != nil {
return err return err
} }
case codec.Publication: case codec.Event:
data = c.buf.Bytes() data = c.buf.Bytes()
default: default:
return fmt.Errorf("Unrecognised message type: %v", c.mt) return fmt.Errorf("Unrecognised message type: %v", c.mt)

View File

@ -14,13 +14,11 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.0-123", Id: "foo-1.0.0-123",
Address: "localhost", Address: "localhost:9999",
Port: 9999,
}, },
{ {
Id: "foo-1.0.0-321", Id: "foo-1.0.0-321",
Address: "localhost", Address: "localhost:9999",
Port: 9999,
}, },
}, },
}, },
@ -30,8 +28,7 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.1-321", Id: "foo-1.0.1-321",
Address: "localhost", Address: "localhost:6666",
Port: 6666,
}, },
}, },
}, },
@ -41,8 +38,7 @@ var (
Nodes: []*registry.Node{ Nodes: []*registry.Node{
{ {
Id: "foo-1.0.3-345", Id: "foo-1.0.3-345",
Address: "localhost", Address: "localhost:8888",
Port: 8888,
}, },
}, },
}, },

View File

@ -1,6 +1,6 @@
# Config [![GoDoc](https://godoc.org/github.com/micro/go-micro/config?status.svg)](https://godoc.org/github.com/micro/go-micro/config) # Config [![GoDoc](https://godoc.org/github.com/micro/go-micro/config?status.svg)](https://godoc.org/github.com/micro/go-micro/config)
Go Config is a pluggable dynamic config library. Config is a pluggable dynamic config package
Most config in applications are statically configured or include complex logic to load from multiple sources. Most config in applications are statically configured or include complex logic to load from multiple sources.
Go Config makes this easy, pluggable and mergeable. You'll never have to deal with config in the same way again. Go Config makes this easy, pluggable and mergeable. You'll never have to deal with config in the same way again.

View File

@ -14,8 +14,8 @@ import (
cgrpc "github.com/micro/go-micro/client/grpc" cgrpc "github.com/micro/go-micro/client/grpc"
cmucp "github.com/micro/go-micro/client/mucp" cmucp "github.com/micro/go-micro/client/mucp"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
smucp "github.com/micro/go-micro/server/mucp"
sgrpc "github.com/micro/go-micro/server/grpc" sgrpc "github.com/micro/go-micro/server/grpc"
smucp "github.com/micro/go-micro/server/mucp"
"github.com/micro/go-micro/util/log" "github.com/micro/go-micro/util/log"
// brokers // brokers
@ -32,15 +32,17 @@ import (
rmem "github.com/micro/go-micro/registry/memory" rmem "github.com/micro/go-micro/registry/memory"
// selectors // selectors
"github.com/micro/go-micro/selector" "github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/selector/dns" "github.com/micro/go-micro/client/selector/dns"
"github.com/micro/go-micro/selector/static" "github.com/micro/go-micro/client/selector/router"
"github.com/micro/go-micro/client/selector/static"
// transports // transports
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
tgrpc "github.com/micro/go-micro/transport/grpc" tgrpc "github.com/micro/go-micro/transport/grpc"
thttp "github.com/micro/go-micro/transport/http" thttp "github.com/micro/go-micro/transport/http"
tmem "github.com/micro/go-micro/transport/memory" tmem "github.com/micro/go-micro/transport/memory"
"github.com/micro/go-micro/transport/quic"
) )
type Cmd interface { type Cmd interface {
@ -196,6 +198,7 @@ var (
"default": selector.NewSelector, "default": selector.NewSelector,
"dns": dns.NewSelector, "dns": dns.NewSelector,
"cache": selector.NewSelector, "cache": selector.NewSelector,
"router": router.NewSelector,
"static": static.NewSelector, "static": static.NewSelector,
} }
@ -209,6 +212,7 @@ var (
"memory": tmem.NewTransport, "memory": tmem.NewTransport,
"http": thttp.NewTransport, "http": thttp.NewTransport,
"grpc": tgrpc.NewTransport, "grpc": tgrpc.NewTransport,
"quic": quic.NewTransport,
} }
// used for default selection as the fall back // used for default selection as the fall back

View File

@ -5,8 +5,8 @@ import (
"github.com/micro/go-micro/broker" "github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
) )

View File

@ -8,9 +8,25 @@ import (
"testing" "testing"
"time" "time"
"github.com/micro/go-micro/config/source/env"
"github.com/micro/go-micro/config/source/file" "github.com/micro/go-micro/config/source/file"
) )
func createFileForIssue18(t *testing.T, content string) *os.File {
data := []byte(content)
path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano()))
fh, err := os.Create(path)
if err != nil {
t.Error(err)
}
_, err = fh.Write(data)
if err != nil {
t.Error(err)
}
return fh
}
func createFileForTest(t *testing.T) *os.File { func createFileForTest(t *testing.T) *os.File {
data := []byte(`{"foo": "bar"}`) data := []byte(`{"foo": "bar"}`)
path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano())) path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano()))
@ -26,7 +42,7 @@ func createFileForTest(t *testing.T) *os.File {
return fh return fh
} }
func TestLoadWithGoodFile(t *testing.T) { func TestConfigLoadWithGoodFile(t *testing.T) {
fh := createFileForTest(t) fh := createFileForTest(t)
path := fh.Name() path := fh.Name()
defer func() { defer func() {
@ -44,7 +60,7 @@ func TestLoadWithGoodFile(t *testing.T) {
} }
} }
func TestLoadWithInvalidFile(t *testing.T) { func TestConfigLoadWithInvalidFile(t *testing.T) {
fh := createFileForTest(t) fh := createFileForTest(t)
path := fh.Name() path := fh.Name()
defer func() { defer func() {
@ -68,34 +84,35 @@ func TestLoadWithInvalidFile(t *testing.T) {
} }
} }
func TestConsul(t *testing.T) { func TestConfigMerge(t *testing.T) {
/*consulSource := consul.NewSource( fh := createFileForIssue18(t, `{
// optionally specify consul address; default to localhost:8500 "amqp": {
consul.WithAddress("131.150.38.111:8500"), "host": "rabbit.platform",
// optionally specify prefix; defaults to /micro/config "port": 80
consul.WithPrefix("/project"), },
// optionally strip the provided prefix from the keys, defaults to false "handler": {
consul.StripPrefix(true), "exchange": "springCloudBus"
consul.WithDatacenter("dc1"), }
consul.WithToken("xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"), }`)
path := fh.Name()
defer func() {
fh.Close()
os.Remove(path)
}()
os.Setenv("AMQP_HOST", "rabbit.testing.com")
conf := NewConfig()
conf.Load(
file.NewSource(
file.WithPath(path),
),
env.NewSource(),
) )
// Create new config actualHost := conf.Get("amqp", "host").String("backup")
conf := NewConfig() if actualHost != "rabbit.testing.com" {
t.Fatalf("Expected %v but got %v",
// Load file source "rabbit.testing.com",
err := conf.Load(consulSource) actualHost)
if err != nil {
t.Error(err)
return
} }
m := conf.Map()
t.Log("m: ", m)
v := conf.Get("project", "dc111", "port")
t.Log("v: ", v.Int(13))*/
t.Log("OK")
} }

View File

@ -1,60 +0,0 @@
package config
import (
"fmt"
"os"
"path/filepath"
"testing"
"time"
"github.com/micro/go-micro/config/source/env"
"github.com/micro/go-micro/config/source/file"
)
func createFileForIssue18(t *testing.T, content string) *os.File {
data := []byte(content)
path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano()))
fh, err := os.Create(path)
if err != nil {
t.Error(err)
}
_, err = fh.Write(data)
if err != nil {
t.Error(err)
}
return fh
}
func TestIssue18(t *testing.T) {
fh := createFileForIssue18(t, `{
"amqp": {
"host": "rabbit.platform",
"port": 80
},
"handler": {
"exchange": "springCloudBus"
}
}`)
path := fh.Name()
defer func() {
fh.Close()
os.Remove(path)
}()
os.Setenv("AMQP_HOST", "rabbit.testing.com")
conf := NewConfig()
conf.Load(
file.NewSource(
file.WithPath(path),
),
env.NewSource(),
)
actualHost := conf.Get("amqp", "host").String("backup")
if actualHost != "rabbit.testing.com" {
t.Fatalf("Expected %v but got %v",
"rabbit.testing.com",
actualHost)
}
}

View File

@ -1,39 +1,85 @@
package json package json
import ( import (
"reflect"
"testing" "testing"
"github.com/micro/go-micro/config/source" "github.com/micro/go-micro/config/source"
) )
func TestValues(t *testing.T) { func TestValues(t *testing.T) {
data := []byte(`{"foo": "bar", "baz": {"bar": "cat"}}`) emptyStr := ""
testData := []struct { testData := []struct {
csdata []byte
path []string path []string
value string accepter interface{}
value interface{}
}{ }{
{ {
[]byte(`{"foo": "bar", "baz": {"bar": "cat"}}`),
[]string{"foo"}, []string{"foo"},
emptyStr,
"bar", "bar",
}, },
{ {
[]byte(`{"foo": "bar", "baz": {"bar": "cat"}}`),
[]string{"baz", "bar"}, []string{"baz", "bar"},
emptyStr,
"cat", "cat",
}, },
} }
for idx, test := range testData {
values, err := newValues(&source.ChangeSet{ values, err := newValues(&source.ChangeSet{
Data: data, Data: test.csdata,
}) })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
for _, test := range testData { err = values.Get(test.path...).Scan(&test.accepter)
if v := values.Get(test.path...).String(""); v != test.value { if err != nil {
t.Fatalf("Expected %s got %s for path %v", test.value, v, test.path) t.Fatal(err)
}
if test.accepter != test.value {
t.Fatalf("No.%d Expected %v got %v for path %v", idx, test.value, test.accepter, test.path)
}
}
}
func TestStructArray(t *testing.T) {
type T struct {
Foo string
}
emptyTSlice := []T{}
testData := []struct {
csdata []byte
accepter []T
value []T
}{
{
[]byte(`[{"foo": "bar"}]`),
emptyTSlice,
[]T{T{Foo: "bar"}},
},
}
for idx, test := range testData {
values, err := newValues(&source.ChangeSet{
Data: test.csdata,
})
if err != nil {
t.Fatal(err)
}
err = values.Get().Scan(&test.accepter)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(test.accepter, test.value) {
t.Fatalf("No.%d Expected %v got %v", idx, test.value, test.accepter)
} }
} }
} }

View File

@ -9,7 +9,7 @@ import (
"github.com/imdario/mergo" "github.com/imdario/mergo"
"github.com/micro/cli" "github.com/micro/cli"
"github.com/micro/go-micro/cmd" "github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/config/source" "github.com/micro/go-micro/config/source"
) )

View File

@ -6,7 +6,7 @@ import (
"testing" "testing"
"github.com/micro/cli" "github.com/micro/cli"
"github.com/micro/go-micro/cmd" "github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/config/source" "github.com/micro/go-micro/config/source"
) )

View File

@ -74,6 +74,11 @@ func NewSource(opts ...source.Option) source.Source {
// use default config // use default config
config := api.DefaultConfig() 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 // check if there are any addrs
a, ok := options.Context.Value(addressKey{}).(string) a, ok := options.Context.Value(addressKey{}).(string)
if ok { if ok {

View File

@ -3,6 +3,7 @@ package consul
import ( import (
"context" "context"
"github.com/hashicorp/consul/api"
"github.com/micro/go-micro/config/source" "github.com/micro/go-micro/config/source"
) )
@ -11,6 +12,7 @@ type prefixKey struct{}
type stripPrefixKey struct{} type stripPrefixKey struct{}
type dcKey struct{} type dcKey struct{}
type tokenKey struct{} type tokenKey struct{}
type configKey struct{}
// WithAddress sets the consul address // WithAddress sets the consul address
func WithAddress(a string) source.Option { func WithAddress(a string) source.Option {
@ -61,3 +63,13 @@ func WithToken(p string) source.Option {
o.Context = context.WithValue(o.Context, tokenKey{}, p) 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)
}
}

View File

@ -8,40 +8,80 @@ import (
"github.com/micro/go-micro/config/encoder" "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) { func makeMap(e encoder.Encoder, kv api.KVPairs, stripPrefix string) (map[string]interface{}, error) {
data := make(map[string]interface{}) data := make(map[string]interface{})
// consul guarantees lexicographic order, so no need to sort // consul guarantees lexicographic order, so no need to sort
for _, v := range kv { for _, v := range kv {
pathString := strings.TrimPrefix(strings.TrimPrefix(v.Key, stripPrefix), "/") pathString := strings.TrimPrefix(strings.TrimPrefix(v.Key, strings.TrimPrefix(stripPrefix, "/")), "/")
var val map[string]interface{} if pathString == "" {
continue
}
var val configValue
var err error
// ensure a valid value is stored at this location // ensure a valid value is stored at this location
if len(v.Value) > 0 { if len(v.Value) > 0 {
if err := e.Decode(v.Value, &val); err != nil { // 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) return nil, fmt.Errorf("faild decode value. path: %s, error: %s", pathString, err)
} }
} }
// set target at the root // set target at the root
target := data target := data
// then descend to the target location, creating as we go, if need be
if pathString != "" {
path := strings.Split(pathString, "/") path := strings.Split(pathString, "/")
// find (or create) the location we want to put this value at // find (or create) the leaf node we want to put this value at
for _, dir := range path { for _, dir := range path[:len(path)-1] {
if _, ok := target[dir]; !ok { if _, ok := target[dir]; !ok {
target[dir] = make(map[string]interface{}) target[dir] = make(map[string]interface{})
} }
target = target[dir].(map[string]interface{}) target = target[dir].(map[string]interface{})
} }
} leafDir := path[len(path)-1]
// copy over the keys from the value // copy over the keys from the value
for k := range val { switch val.(type) {
target[k] = val[k] 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]
}
} }
} }

View File

@ -27,7 +27,7 @@ Specify source with data
```go ```go
memorySource := memory.NewSource( memorySource := memory.NewSource(
memory.WithData(data), memory.WithJSON(data),
) )
``` ```

2
data/data.go Normal file
View File

@ -0,0 +1,2 @@
// Package data is an interface for data access
package data

View File

@ -7,7 +7,7 @@ import (
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
"github.com/micro/go-micro/config/options" "github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/store" "github.com/micro/go-micro/data/store"
) )
type ckv struct { type ckv struct {

View File

@ -6,7 +6,7 @@ import (
"time" "time"
"github.com/micro/go-micro/config/options" "github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/store" "github.com/micro/go-micro/data/store"
) )
type memoryStore struct { type memoryStore struct {
@ -32,10 +32,16 @@ func (m *memoryStore) Dump() ([]*store.Record, error) {
d := v.r.Expiry d := v.r.Expiry
t := time.Since(v.c) t := time.Since(v.c)
if d > time.Duration(0) {
// expired // expired
if d > time.Duration(0) && t > d { if t > d {
continue continue
} }
// update expiry
v.r.Expiry -= t
v.c = time.Now()
}
values = append(values, v.r) values = append(values, v.r)
} }
@ -56,9 +62,14 @@ func (m *memoryStore) Read(key string) (*store.Record, error) {
t := time.Since(v.c) t := time.Since(v.c)
// expired // expired
if d > time.Duration(0) && t > d { if d > time.Duration(0) {
if t > d {
return nil, store.ErrNotFound return nil, store.ErrNotFound
} }
// update expiry
v.r.Expiry -= t
v.c = time.Now()
}
return v.r, nil return v.r, nil
} }

View File

@ -0,0 +1,37 @@
package memory
import (
"testing"
"time"
"github.com/micro/go-micro/data/store"
)
func TestReadRecordExpire(t *testing.T) {
s := NewStore()
var (
key = "foo"
expire = 100 * time.Millisecond
)
rec := &store.Record{
Key: key,
Value: nil,
Expiry: expire,
}
s.Write(rec)
rrec, err := s.Read(key)
if err != nil {
t.Fatal(err)
}
if rrec.Expiry >= expire {
t.Fatal("expiry of read record is not changed")
}
time.Sleep(expire)
if _, err := s.Read(key); err != store.ErrNotFound {
t.Fatal("expire elapsed, but key still accessable")
}
}

41
debug/handler/debug.go Normal file
View File

@ -0,0 +1,41 @@
package handler
import (
"context"
"runtime"
"time"
proto "github.com/micro/go-micro/debug/proto"
)
type Debug struct {
proto.DebugHandler
started int64
}
var (
DefaultHandler = newDebug()
)
func newDebug() *Debug {
return &Debug{
started: time.Now().Unix(),
}
}
func (d *Debug) Health(ctx context.Context, req *proto.HealthRequest, rsp *proto.HealthResponse) error {
rsp.Status = "ok"
return nil
}
func (d *Debug) Stats(ctx context.Context, req *proto.StatsRequest, rsp *proto.StatsResponse) error {
var mstat runtime.MemStats
runtime.ReadMemStats(&mstat)
rsp.Started = uint64(d.started)
rsp.Uptime = uint64(time.Now().Unix() - d.started)
rsp.Memory = mstat.Alloc
rsp.Gc = mstat.PauseTotalNs
rsp.Threads = uint64(runtime.NumGoroutine())
return nil
}

108
debug/proto/debug.micro.go Normal file
View File

@ -0,0 +1,108 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/debug/proto/debug.proto
package debug
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 Debug service
type DebugService interface {
Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error)
Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error)
}
type debugService struct {
c client.Client
name string
}
func NewDebugService(name string, c client.Client) DebugService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "debug"
}
return &debugService{
c: c,
name: name,
}
}
func (c *debugService) Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error) {
req := c.c.NewRequest(c.name, "Debug.Health", in)
out := new(HealthResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *debugService) Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error) {
req := c.c.NewRequest(c.name, "Debug.Stats", in)
out := new(StatsResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Debug service
type DebugHandler interface {
Health(context.Context, *HealthRequest, *HealthResponse) error
Stats(context.Context, *StatsRequest, *StatsResponse) error
}
func RegisterDebugHandler(s server.Server, hdlr DebugHandler, opts ...server.HandlerOption) error {
type debug interface {
Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error
Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error
}
type Debug struct {
debug
}
h := &debugHandler{hdlr}
return s.Handle(s.NewHandler(&Debug{h}, opts...))
}
type debugHandler struct {
DebugHandler
}
func (h *debugHandler) Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error {
return h.DebugHandler.Health(ctx, in, out)
}
func (h *debugHandler) Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error {
return h.DebugHandler.Stats(ctx, in, out)
}

336
debug/proto/debug.pb.go Normal file
View File

@ -0,0 +1,336 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/debug/proto/debug.proto
package debug
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 HealthRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HealthRequest) Reset() { *m = HealthRequest{} }
func (m *HealthRequest) String() string { return proto.CompactTextString(m) }
func (*HealthRequest) ProtoMessage() {}
func (*HealthRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f25415e61bccfa1f, []int{0}
}
func (m *HealthRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HealthRequest.Unmarshal(m, b)
}
func (m *HealthRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HealthRequest.Marshal(b, m, deterministic)
}
func (m *HealthRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HealthRequest.Merge(m, src)
}
func (m *HealthRequest) XXX_Size() int {
return xxx_messageInfo_HealthRequest.Size(m)
}
func (m *HealthRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HealthRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HealthRequest proto.InternalMessageInfo
type HealthResponse struct {
// default: ok
Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HealthResponse) Reset() { *m = HealthResponse{} }
func (m *HealthResponse) String() string { return proto.CompactTextString(m) }
func (*HealthResponse) ProtoMessage() {}
func (*HealthResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f25415e61bccfa1f, []int{1}
}
func (m *HealthResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HealthResponse.Unmarshal(m, b)
}
func (m *HealthResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HealthResponse.Marshal(b, m, deterministic)
}
func (m *HealthResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_HealthResponse.Merge(m, src)
}
func (m *HealthResponse) XXX_Size() int {
return xxx_messageInfo_HealthResponse.Size(m)
}
func (m *HealthResponse) XXX_DiscardUnknown() {
xxx_messageInfo_HealthResponse.DiscardUnknown(m)
}
var xxx_messageInfo_HealthResponse proto.InternalMessageInfo
func (m *HealthResponse) GetStatus() string {
if m != nil {
return m.Status
}
return ""
}
type StatsRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *StatsRequest) Reset() { *m = StatsRequest{} }
func (m *StatsRequest) String() string { return proto.CompactTextString(m) }
func (*StatsRequest) ProtoMessage() {}
func (*StatsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f25415e61bccfa1f, []int{2}
}
func (m *StatsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatsRequest.Unmarshal(m, b)
}
func (m *StatsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_StatsRequest.Marshal(b, m, deterministic)
}
func (m *StatsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_StatsRequest.Merge(m, src)
}
func (m *StatsRequest) XXX_Size() int {
return xxx_messageInfo_StatsRequest.Size(m)
}
func (m *StatsRequest) XXX_DiscardUnknown() {
xxx_messageInfo_StatsRequest.DiscardUnknown(m)
}
var xxx_messageInfo_StatsRequest proto.InternalMessageInfo
type StatsResponse struct {
// unix timestamp
Started uint64 `protobuf:"varint,1,opt,name=started,proto3" json:"started,omitempty"`
// in seconds
Uptime uint64 `protobuf:"varint,2,opt,name=uptime,proto3" json:"uptime,omitempty"`
// in bytes
Memory uint64 `protobuf:"varint,3,opt,name=memory,proto3" json:"memory,omitempty"`
// num threads
Threads uint64 `protobuf:"varint,4,opt,name=threads,proto3" json:"threads,omitempty"`
// total gc in nanoseconds
Gc uint64 `protobuf:"varint,5,opt,name=gc,proto3" json:"gc,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *StatsResponse) Reset() { *m = StatsResponse{} }
func (m *StatsResponse) String() string { return proto.CompactTextString(m) }
func (*StatsResponse) ProtoMessage() {}
func (*StatsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f25415e61bccfa1f, []int{3}
}
func (m *StatsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatsResponse.Unmarshal(m, b)
}
func (m *StatsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_StatsResponse.Marshal(b, m, deterministic)
}
func (m *StatsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_StatsResponse.Merge(m, src)
}
func (m *StatsResponse) XXX_Size() int {
return xxx_messageInfo_StatsResponse.Size(m)
}
func (m *StatsResponse) XXX_DiscardUnknown() {
xxx_messageInfo_StatsResponse.DiscardUnknown(m)
}
var xxx_messageInfo_StatsResponse proto.InternalMessageInfo
func (m *StatsResponse) GetStarted() uint64 {
if m != nil {
return m.Started
}
return 0
}
func (m *StatsResponse) GetUptime() uint64 {
if m != nil {
return m.Uptime
}
return 0
}
func (m *StatsResponse) GetMemory() uint64 {
if m != nil {
return m.Memory
}
return 0
}
func (m *StatsResponse) GetThreads() uint64 {
if m != nil {
return m.Threads
}
return 0
}
func (m *StatsResponse) GetGc() uint64 {
if m != nil {
return m.Gc
}
return 0
}
func init() {
proto.RegisterType((*HealthRequest)(nil), "HealthRequest")
proto.RegisterType((*HealthResponse)(nil), "HealthResponse")
proto.RegisterType((*StatsRequest)(nil), "StatsRequest")
proto.RegisterType((*StatsResponse)(nil), "StatsResponse")
}
func init() {
proto.RegisterFile("micro/go-micro/debug/proto/debug.proto", fileDescriptor_f25415e61bccfa1f)
}
var fileDescriptor_f25415e61bccfa1f = []byte{
// 230 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0x41, 0x4b, 0xc4, 0x30,
0x10, 0x85, 0xb7, 0x75, 0x5b, 0x71, 0xb0, 0x59, 0xc8, 0x41, 0xc2, 0x9e, 0x24, 0x07, 0x29, 0x88,
0x59, 0xd0, 0xbf, 0xe0, 0xc1, 0x73, 0xbd, 0x0b, 0xd9, 0x76, 0xe8, 0x16, 0xac, 0xa9, 0x99, 0xe9,
0xc1, 0xb3, 0x7f, 0x5c, 0x9a, 0xa4, 0x60, 0x6f, 0xef, 0xbd, 0xf0, 0x1e, 0xf9, 0x06, 0x1e, 0xc6,
0xa1, 0xf5, 0xee, 0xd4, 0xbb, 0xa7, 0x28, 0x3a, 0x3c, 0xcf, 0xfd, 0x69, 0xf2, 0x8e, 0x93, 0x36,
0x41, 0xeb, 0x03, 0x54, 0x6f, 0x68, 0x3f, 0xf9, 0xd2, 0xe0, 0xf7, 0x8c, 0xc4, 0xba, 0x06, 0xb1,
0x06, 0x34, 0xb9, 0x2f, 0x42, 0x79, 0x07, 0x25, 0xb1, 0xe5, 0x99, 0x54, 0x76, 0x9f, 0xd5, 0x37,
0x4d, 0x72, 0x5a, 0xc0, 0xed, 0x3b, 0x5b, 0xa6, 0xb5, 0xf9, 0x9b, 0x41, 0x95, 0x82, 0xd4, 0x54,
0x70, 0x4d, 0x6c, 0x3d, 0x63, 0x17, 0xaa, 0xfb, 0x66, 0xb5, 0xcb, 0xe6, 0x3c, 0xf1, 0x30, 0xa2,
0xca, 0xc3, 0x43, 0x72, 0x4b, 0x3e, 0xe2, 0xe8, 0xfc, 0x8f, 0xba, 0x8a, 0x79, 0x74, 0xcb, 0x12,
0x5f, 0x3c, 0xda, 0x8e, 0xd4, 0x3e, 0x2e, 0x25, 0x2b, 0x05, 0xe4, 0x7d, 0xab, 0x8a, 0x10, 0xe6,
0x7d, 0xfb, 0xfc, 0x01, 0xc5, 0xeb, 0xc2, 0x27, 0x1f, 0xa1, 0x8c, 0x20, 0x52, 0x98, 0x0d, 0xe2,
0xf1, 0x60, 0xb6, 0x84, 0x7a, 0x27, 0x6b, 0x28, 0xc2, 0xd7, 0x65, 0x65, 0xfe, 0x33, 0x1d, 0x85,
0xd9, 0x10, 0xe9, 0xdd, 0xb9, 0x0c, 0x77, 0x7b, 0xf9, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xb9,
0x5f, 0xf7, 0x61, 0x01, 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
// DebugClient is the client API for Debug service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type DebugClient interface {
Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error)
Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error)
}
type debugClient struct {
cc *grpc.ClientConn
}
func NewDebugClient(cc *grpc.ClientConn) DebugClient {
return &debugClient{cc}
}
func (c *debugClient) Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error) {
out := new(HealthResponse)
err := c.cc.Invoke(ctx, "/Debug/Health", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *debugClient) Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error) {
out := new(StatsResponse)
err := c.cc.Invoke(ctx, "/Debug/Stats", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// DebugServer is the server API for Debug service.
type DebugServer interface {
Health(context.Context, *HealthRequest) (*HealthResponse, error)
Stats(context.Context, *StatsRequest) (*StatsResponse, error)
}
func RegisterDebugServer(s *grpc.Server, srv DebugServer) {
s.RegisterService(&_Debug_serviceDesc, srv)
}
func _Debug_Health_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HealthRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DebugServer).Health(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Debug/Health",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DebugServer).Health(ctx, req.(*HealthRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Debug_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(StatsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DebugServer).Stats(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Debug/Stats",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DebugServer).Stats(ctx, req.(*StatsRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Debug_serviceDesc = grpc.ServiceDesc{
ServiceName: "Debug",
HandlerType: (*DebugServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Health",
Handler: _Debug_Health_Handler,
},
{
MethodName: "Stats",
Handler: _Debug_Stats_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "micro/go-micro/debug/proto/debug.proto",
}

View File

@ -1,13 +1,9 @@
syntax = "proto3"; syntax = "proto3";
// This is commented out due to import cycles. service Debug {
// But its what we expect the RPC service to rpc Health(HealthRequest) returns (HealthResponse) {}
// return. rpc Stats(StatsRequest) returns (StatsResponse) {}
// }
// service Debug {
// rpc Health(HealthRequest) returns (HealthResponse) {}
// rpc Stats(StatsRequest) returns (StatsResponse) {}
// }
message HealthRequest { message HealthRequest {
} }

View File

@ -5,8 +5,8 @@ import (
"sync" "sync"
"testing" "testing"
proto "github.com/micro/go-micro/debug/proto"
"github.com/micro/go-micro/registry/memory" "github.com/micro/go-micro/registry/memory"
proto "github.com/micro/go-micro/server/debug/proto"
) )
func TestFunction(t *testing.T) { func TestFunction(t *testing.T) {

58
go.mod
View File

@ -3,42 +3,78 @@ module github.com/micro/go-micro
go 1.12 go 1.12
require ( require (
cloud.google.com/go v0.43.0 // indirect
github.com/BurntSushi/toml v0.3.1 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/beevik/ntp v0.2.0
github.com/bitly/go-simplejson v0.5.0 github.com/bitly/go-simplejson v0.5.0
github.com/bwmarrin/discordgo v0.19.0 github.com/bwmarrin/discordgo v0.19.0
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc // indirect
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c
github.com/fsnotify/fsnotify v1.4.7 github.com/fsnotify/fsnotify v1.4.7
github.com/fsouza/go-dockerclient v1.4.1 github.com/fsouza/go-dockerclient v1.4.2
github.com/ghodss/yaml v1.0.0 github.com/ghodss/yaml v1.0.0
github.com/go-kit/kit v0.9.0 // indirect
github.com/go-log/log v0.1.0 github.com/go-log/log v0.1.0
github.com/go-playground/locales v0.12.1 // indirect github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/golang/protobuf v1.3.1 github.com/golang/protobuf v1.3.2
github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70 // indirect
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/gorilla/handlers v1.4.0 github.com/gorilla/handlers v1.4.2
github.com/gorilla/websocket v1.4.0 github.com/gorilla/websocket v1.4.0
github.com/hashicorp/consul/api v1.1.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/hashicorp/hcl v1.0.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/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.7
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1 github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1
github.com/json-iterator/go v1.1.6 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/leodido/go-urn v1.1.0 // indirect
github.com/lucas-clemente/quic-go v0.11.2 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/micro/cli v0.2.0 github.com/micro/cli v0.2.0
github.com/micro/mdns v0.1.0 github.com/micro/mdns v0.2.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/mitchellh/hashstructure v1.0.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/nats-io/nats.go v1.8.1 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/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/pkg/errors v0.8.1 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/technoweenie/multipartstreamer v1.0.1 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/net v0.0.0-20190607181551-461777fb6f67 golang.org/x/mobile v0.0.0-20190806162312-597adff16ade // indirect
google.golang.org/grpc v1.21.1 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
gopkg.in/go-playground/validator.v9 v9.29.0 golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa // indirect
gopkg.in/src-d/go-git.v4 v4.11.0 golang.org/x/tools v0.0.0-20190807201305-8be58fba6352 // indirect
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 // indirect
google.golang.org/grpc v1.22.1
gopkg.in/go-playground/validator.v9 v9.29.1
gopkg.in/src-d/go-git.v4 v4.13.1
gopkg.in/telegram-bot-api.v4 v4.6.4 gopkg.in/telegram-bot-api.v4 v4.6.4
honnef.co/go/tools v0.0.1-2019.2.2 // indirect
) )

296
go.sum
View File

@ -1,16 +1,38 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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.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/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= 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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 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 h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 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 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/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw= 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/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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 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 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
@ -18,19 +40,30 @@ github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5a
github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= 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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M= 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-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/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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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 h1:dmUn0SuGx7unKFwxyeQ/oLUHhEfZosEDrpmYM+6MTuc=
github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 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 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 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 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 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 h1:pBgVXWDXju1m8W4lnEeIqTHPOzhTUO81a7yknM/xQR4=
@ -39,33 +72,61 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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 h1:W7wuJ3IB48WYZv/UBk9dCTIb9oX805+L9KIm65HcUYs=
github.com/fsouza/go-dockerclient v1.4.1/go.mod h1:PUNHxbowDqRXfRgZqMz1OeGtbWC6VKyZvJ99hDjB0qs= 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/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
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 h1:wudGTNsiGzrD5ZjgIkVZ517ugi2XRe9Q/xRCzwEO4/U=
github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M= 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=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= 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-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/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= 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/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 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.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.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 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 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/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= 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 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.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.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
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 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA=
github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= 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/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.7.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.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.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 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
@ -73,33 +134,53 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 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/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 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 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 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.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 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.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 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 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 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 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 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 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-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.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 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/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 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.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=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 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/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/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.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/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 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 h1:gkyML/r71w3FL8gUi74Vk76avkj/9lYAY9lvg0OcoGs=
github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 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 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 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/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:anPrsicrIi2ColgWTVPk+TrN42hJIWlfPHSBP9S0ZkM= 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/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84=
@ -112,55 +193,106 @@ github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1 h1:lnrOS18wZBYrzdD
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1/go.mod h1:DFXrEwSRX0p/aSvxE21319menCBFeQO0jXpRj7LEZUA= 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.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/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/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 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-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.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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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.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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= 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 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.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/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 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= 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.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.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.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/micro/cli v0.2.0 h1:ut3rV5JWqZjsXIa2MvGF+qMUP8DAUTvHX9Br5gO4afA= github.com/micro/cli v0.2.0 h1:ut3rV5JWqZjsXIa2MvGF+qMUP8DAUTvHX9Br5gO4afA=
github.com/micro/cli v0.2.0/go.mod h1:jRT9gmfVKWSS6pkKcXQ8YhUyj6bzwxK8Fp5b0Y7qNnk= 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 h1:fuLybUsfynbigJmCot/54i+gwe0hpc/vtCMvWt2WfDI=
github.com/micro/mdns v0.1.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc= 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM=
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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/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 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 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 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 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/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/nats-io/nats.go v1.8.1 h1:6lF/f1/NN6kzUDBz6pyvQDEXO39jqXcWRLu/tKjtOUQ= 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/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
github.com/nats-io/nkeys v0.0.2 h1:+qM7QpgXnvDDixitZtQUBDY9w/s9mu1ghS+JIbsrx6M= github.com/nats-io/nkeys v0.0.2 h1:+qM7QpgXnvDDixitZtQUBDY9w/s9mu1ghS+JIbsrx6M=
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
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 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 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 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0=
github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
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.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 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/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 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 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 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 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
@ -168,6 +300,7 @@ github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 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 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -175,75 +308,232 @@ 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/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.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 v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
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-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
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.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 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/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= 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/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 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
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 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 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/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= 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/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= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= 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 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8= github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
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=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-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-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-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-20190130090550-b01c7a725664/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-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-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 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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-20190701094942-4def268fd1a4/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-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-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-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-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-20181201002055-351d144fa1fc/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-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-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-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 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-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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/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-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-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-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-20181026203630-95b1ffbd15a5/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-20190129075346-302c3dd5f1cc/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-20190215142949-d0b11bdaac8a/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-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-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-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/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/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/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 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.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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-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-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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 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-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 h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 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/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/grpc v1.19.0/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 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 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=
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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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 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.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 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.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/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.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
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 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.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 h1:hpHWhzn4jTCsAJZZ2loNKfy2QWyPDRJVl3aTFXeMW8g=
gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM= gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@ -254,3 +544,9 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/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=

311
monitor/default.go Normal file
View File

@ -0,0 +1,311 @@
package monitor
import (
"context"
"errors"
"sync"
"time"
"github.com/micro/go-micro/client"
pb "github.com/micro/go-micro/debug/proto"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/registry/cache"
)
type monitor struct {
options Options
exit chan bool
registry cache.Cache
client client.Client
sync.RWMutex
running bool
services map[string]*Status
}
// check provides binary running/failed status.
// In the event Debug.Health cannot be called on a service we reap the node.
func (m *monitor) check(service string) (*Status, error) {
services, err := m.registry.GetService(service)
if err != nil {
return nil, err
}
// create debug client
debug := pb.NewDebugService(service, m.client)
var status *Status
var gerr error
// iterate through multiple versions of a service
for _, service := range services {
for _, node := range service.Nodes {
rsp, err := debug.Health(
context.Background(),
// empty health request
&pb.HealthRequest{},
// call this specific node
client.WithAddress(node.Address),
// retry in the event of failure
client.WithRetries(3),
)
if err != nil {
// reap the dead node
m.registry.Deregister(&registry.Service{
Name: service.Name,
Version: service.Version,
Nodes: []*registry.Node{node},
})
// save the error
gerr = err
continue
}
// expecting ok response status
if rsp.Status != "ok" {
gerr = errors.New(rsp.Status)
continue
}
// no error set status
status = &Status{
Code: StatusRunning,
Info: "running",
}
}
}
// if we got the success case return it
if status != nil {
return status, nil
}
// if gerr is not nil return it
if gerr != nil {
return &Status{
Code: StatusFailed,
Info: "not running",
Error: gerr.Error(),
}, nil
}
// otherwise unknown status
return &Status{
Code: StatusUnknown,
Info: "unknown status",
}, nil
}
func (m *monitor) reap() {
services, err := m.registry.ListServices()
if err != nil {
return
}
serviceMap := make(map[string]bool)
for _, service := range services {
serviceMap[service.Name] = true
}
m.Lock()
defer m.Unlock()
// range over our watched services
for service, _ := range m.services {
// check if the service exists in the registry
if !serviceMap[service] {
// if not, delete it in our status map
delete(m.services, service)
}
}
}
func (m *monitor) run() {
// check the status every tick
t := time.NewTicker(time.Minute)
defer t.Stop()
// reap dead services
t2 := time.NewTicker(time.Hour)
defer t2.Stop()
// list the known services
services, _ := m.registry.ListServices()
// create a check chan of same length
check := make(chan string, len(services))
// front-load the services to watch
for _, service := range services {
check <- service.Name
}
for {
select {
// exit if we're told to
case <-m.exit:
return
// check a service when told to
case service := <-check:
// check the status
status, err := m.check(service)
if err != nil {
status = &Status{
Code: StatusUnknown,
Info: "unknown status",
}
}
// save the status
m.Lock()
m.services[service] = status
m.Unlock()
// on the tick interval get all services and issue a check
case <-t.C:
// create a list of services
serviceMap := make(map[string]bool)
m.RLock()
for service, _ := range m.services {
serviceMap[service] = true
}
m.RUnlock()
go func() {
// check the status of all watched services
for service, _ := range serviceMap {
select {
case <-m.exit:
return
case check <- service:
default:
// barf if we block
}
}
// list services
services, _ := m.registry.ListServices()
for _, service := range services {
// start watching the service
if ok := serviceMap[service.Name]; !ok {
m.Watch(service.Name)
}
}
}()
case <-t2.C:
// reap any dead/non-existent services
m.reap()
}
}
}
func (m *monitor) Reap(service string) error {
services, err := m.registry.GetService(service)
if err != nil {
return nil
}
m.Lock()
defer m.Unlock()
delete(m.services, service)
for _, service := range services {
m.registry.Deregister(service)
}
return nil
}
func (m *monitor) Status(service string) (Status, error) {
m.RLock()
defer m.RUnlock()
if status, ok := m.services[service]; ok {
return *status, nil
}
return Status{}, ErrNotWatching
}
func (m *monitor) Watch(service string) error {
m.Lock()
defer m.Unlock()
// check if we're watching
if _, ok := m.services[service]; ok {
return nil
}
// get the status
status, err := m.check(service)
if err != nil {
return err
}
// set the status
m.services[service] = status
return nil
}
func (m *monitor) Run() error {
m.Lock()
defer m.Unlock()
if m.running {
return nil
}
// reset the exit channel
m.exit = make(chan bool)
// setup a new cache
m.registry = cache.New(m.options.Registry)
// start running
go m.run()
// set to running
m.running = true
return nil
}
func (m *monitor) Stop() error {
m.Lock()
defer m.Unlock()
if !m.running {
return nil
}
select {
case <-m.exit:
return nil
default:
close(m.exit)
for s, _ := range m.services {
delete(m.services, s)
}
m.registry.Stop()
m.running = false
return nil
}
return nil
}
func newMonitor(opts ...Option) Monitor {
options := Options{
Client: client.DefaultClient,
Registry: registry.DefaultRegistry,
}
for _, o := range opts {
o(&options)
}
return &monitor{
options: options,
exit: make(chan bool),
client: options.Client,
registry: cache.New(options.Registry),
services: make(map[string]*Status),
}
}

37
monitor/default_test.go Normal file
View File

@ -0,0 +1,37 @@
package monitor
import (
"testing"
)
func TestMonitor(t *testing.T) {
// create new monitor
m := NewMonitor()
if err := m.Run(); err != nil {
t.Fatalf("failed to stop monitor: %v", err)
}
services := []string{"foo", "bar", "baz"}
for _, service := range services {
_, err := m.Status(service)
if err == nil {
t.Fatal("expected status error for unknown service")
}
if err := m.Watch(service); err == nil {
t.Fatal("expected watch error for unknown service")
}
// TODO:
// 1. start a service
// 2. watch service
// 3. get service status
}
// stop monitor
if err := m.Stop(); err != nil {
t.Fatalf("failed to stop monitor: %v", err)
}
}

43
monitor/monitor.go Normal file
View File

@ -0,0 +1,43 @@
// Package monitor monitors service health
package monitor
import (
"errors"
)
const (
StatusUnknown StatusCode = iota
StatusRunning
StatusFailed
)
type StatusCode int
// Monitor monitors a service and reaps dead instances
type Monitor interface {
// Reap a service and stop monitoring
Reap(service string) error
// Status of the service
Status(service string) (Status, error)
// Watch starts watching the service
Watch(service string) error
// Run the monitor to watch all services
Run() error
// Stop monitoring
Stop() error
}
type Status struct {
Code StatusCode
Info string
Error string
}
var (
ErrNotWatching = errors.New("not watching")
)
// NewMonitor returns a new monitor
func NewMonitor(opts ...Option) Monitor {
return newMonitor(opts...)
}

25
monitor/options.go Normal file
View File

@ -0,0 +1,25 @@
package monitor
import (
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/registry"
)
type Options struct {
Client client.Client
Registry registry.Registry
}
type Option func(*Options)
func Client(c client.Client) Option {
return func(o *Options) {
o.Client = c
}
}
func Registry(r registry.Registry) Option {
return func(o *Options) {
o.Registry = r
}
}

288
network/link/default.go Normal file
View File

@ -0,0 +1,288 @@
// Package link provides a measured transport.Socket link
package link
import (
"io"
"sync"
"time"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/transport"
)
type link struct {
sync.RWMutex
// the link id
id string
// the remote end to dial
addr string
// channel used to close the link
closed chan bool
// if its connected
connected bool
// the transport to use
transport transport.Transport
// the send queue to the socket
sendQueue chan *transport.Message
// the recv queue to the socket
recvQueue chan *transport.Message
// the socket for this link
socket transport.Socket
// determines the cost of the link
// based on queue length and roundtrip
length int
weight int
}
func newLink(options options.Options) *link {
// default values
var sock transport.Socket
var addr string
id := "local"
tr := transport.DefaultTransport
lid, ok := options.Values().Get("link.id")
if ok {
id = lid.(string)
}
laddr, ok := options.Values().Get("link.address")
if ok {
addr = laddr.(string)
}
ltr, ok := options.Values().Get("link.transport")
if ok {
tr = ltr.(transport.Transport)
}
lsock, ok := options.Values().Get("link.socket")
if ok {
sock = lsock.(transport.Socket)
}
l := &link{
// the remote end to dial
addr: addr,
// transport to dial link
transport: tr,
// the socket to use
// this is nil if not specified
socket: sock,
// unique id assigned to the link
id: id,
// the closed channel used to close the conn
closed: make(chan bool),
// then send queue
sendQueue: make(chan *transport.Message, 128),
// the receive queue
recvQueue: make(chan *transport.Message, 128),
}
// return the link
return l
}
// link methods
// process processes messages on the send and receive queues.
func (l *link) process() {
go func() {
for {
m := new(transport.Message)
if err := l.recv(m); err != nil {
return
}
select {
case l.recvQueue <- m:
case <-l.closed:
return
}
}
}()
// messages sent
i := 0
length := 0
for {
select {
case m := <-l.sendQueue:
t := time.Now()
// send the message
if err := l.send(m); err != nil {
return
}
// get header size, body size and time taken
hl := len(m.Header)
bl := len(m.Body)
d := time.Since(t)
// don't calculate on empty messages
if hl == 0 && bl == 0 {
continue
}
// increment sent
i++
// time take to send some bits and bytes
td := float64(hl+bl) / float64(d.Nanoseconds())
// increase the scale
td += 1
// judge the length
length = int(td) / (length + int(td))
// every 10 messages update length
if (i % 10) == 1 {
// cost average the length
// save it
l.Lock()
l.length = length
l.Unlock()
}
case <-l.closed:
return
}
}
}
// send a message over the link
func (l *link) send(m *transport.Message) error {
// TODO: measure time taken and calculate length/rate
// send via the transport socket
return l.socket.Send(m)
}
// recv a message on the link
func (l *link) recv(m *transport.Message) error {
if m.Header == nil {
m.Header = make(map[string]string)
}
// receive the transport message
return l.socket.Recv(m)
}
// Connect attempts to connect to an address and sets the socket
func (l *link) Connect() error {
l.Lock()
if l.connected {
l.Unlock()
return nil
}
defer l.Unlock()
// replace closed
l.closed = make(chan bool)
// assume existing socket
if len(l.addr) == 0 {
go l.process()
return nil
}
// dial the endpoint
c, err := l.transport.Dial(l.addr)
if err != nil {
return err
}
// set the socket
l.socket = c
// kick start the processing
go l.process()
return nil
}
// Close the link
func (l *link) Close() error {
select {
case <-l.closed:
return nil
default:
close(l.closed)
l.Lock()
l.connected = false
l.Unlock()
return l.socket.Close()
}
}
// returns the node id
func (l *link) Id() string {
l.RLock()
defer l.RUnlock()
return l.id
}
// the remote ip of the link
func (l *link) Remote() string {
l.RLock()
defer l.RUnlock()
return l.socket.Remote()
}
// the local ip of the link
func (l *link) Local() string {
l.RLock()
defer l.RUnlock()
return l.socket.Local()
}
// length/rate of the link
func (l *link) Length() int {
l.RLock()
defer l.RUnlock()
return l.length
}
// weight checks the size of the queues
func (l *link) Weight() int {
return len(l.sendQueue) + len(l.recvQueue)
}
// Accept accepts a message on the socket
func (l *link) Recv(m *transport.Message) error {
select {
case <-l.closed:
return io.EOF
case rm := <-l.recvQueue:
*m = *rm
return nil
}
// never reach
return nil
}
// Send sends a message on the socket immediately
func (l *link) Send(m *transport.Message) error {
select {
case <-l.closed:
return io.EOF
case l.sendQueue <- m:
}
return nil
}
func (l *link) Status() string {
select {
case <-l.closed:
return "closed"
default:
return "connected"
}
}

60
network/link/link.go Normal file
View File

@ -0,0 +1,60 @@
// Package link provides a measured link on top of a transport.Socket
package link
import (
"errors"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/transport"
)
// Link is a layer on top of a transport socket with the
// buffering send and recv queue's with the ability to
// measure the actual transport link and reconnect if
// an address is specified.
type Link interface {
// provides the transport.Socket interface
transport.Socket
// Connect connects the link. It must be called first
// if there's an expectation to create a new socket.
Connect() error
// Id of the link is "local" if not specified
Id() string
// Status of the link
Status() string
// Depth of the buffers
Weight() int
// Rate of the link
Length() int
}
var (
ErrLinkClosed = errors.New("link closed")
)
// NewLink creates a new link on top of a socket
func NewLink(opts ...options.Option) Link {
return newLink(options.NewOptions(opts...))
}
// Sets the link id which otherwise defaults to "local"
func Id(id string) options.Option {
return options.WithValue("link.id", id)
}
// The address to use for the link. Connect must be
// called for this to be used, its otherwise unused.
func Address(a string) options.Option {
return options.WithValue("link.address", a)
}
// The transport to use for the link where we
// want to dial the connection first.
func Transport(t transport.Transport) options.Option {
return options.WithValue("link.transport", t)
}
// Socket sets the socket to use instead of dialing.
func Socket(s transport.Socket) options.Option {
return options.WithValue("link.socket", s)
}

View File

@ -1,45 +1,2 @@
// Package network is an interface for defining a network overlay // Package network is for creating internetworks
package network package network
import (
"github.com/micro/go-micro/config/options"
)
type Network interface {
options.Options
// Id of this network
Id() string
// Connect to the network with id
Connect(id string) error
// Close the network connection
Close() error
// Accept messages
Accept() (*Message, error)
// Send a message
Send(*Message) error
// Advertise a service on this network
Advertise(service string) error
// Retrieve list of nodes for a service
Nodes(service string) ([]Node, error)
}
// Node represents a network node
type Node interface {
// Node is a network. Network is a node.
Network
}
// Message is a message sent over the network
type Message struct {
// Headers are the routing headers
// e.g Micro-Service, Micro-Endpoint, Micro-Network
// see https://github.com/micro/development/blob/master/protocol.md
Header map[string]string
// Body is the encaspulated payload
Body []byte
}
var (
// TODO: set default network
DefaultNetwork Network
)

View File

@ -0,0 +1,30 @@
// Package dns resolves names to dns srv records
package dns
import (
"fmt"
"net"
"github.com/micro/go-micro/network/resolver"
)
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
}

View File

@ -0,0 +1,59 @@
// Package http resolves names to network addresses using a http request
package http
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"github.com/micro/go-micro/network/resolver"
)
type Resolver struct {
// If not set, defaults to http
Proto string
// Path sets the path to lookup. Defaults to /network
Path string
}
// Resolve assumes ID is a domain which can be converted to a http://name/network request
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
proto := "http"
path := "/network"
if len(r.Proto) > 0 {
proto = r.Proto
}
if len(r.Path) > 0 {
path = r.Path
}
uri := &url.URL{
Scheme: proto,
Path: path,
Host: name,
}
rsp, err := http.Get(uri.String())
if err != nil {
return nil, err
}
defer rsp.Body.Close()
b, err := ioutil.ReadAll(rsp.Body)
if err != nil {
return nil, err
}
// encoding format is assumed to be json
var records []*resolver.Record
if err := json.Unmarshal(b, &records); err != nil {
return nil, err
}
return records, nil
}

View File

@ -0,0 +1,37 @@
// Package registry resolves names using the go-micro registry
package registry
import (
"github.com/micro/go-micro/network/resolver"
"github.com/micro/go-micro/registry"
)
type Resolver struct {
// Registry is the registry to use otherwise we use the defaul
Registry registry.Registry
}
// Resolve assumes ID is a domain name e.g micro.mu
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
reg := r.Registry
if reg == nil {
reg = registry.DefaultRegistry
}
services, err := reg.GetService(name)
if err != nil {
return nil, err
}
var records []*resolver.Record
for _, service := range services {
for _, node := range service.Nodes {
records = append(records, &resolver.Record{
Address: node.Address,
})
}
}
return records, nil
}

View File

@ -0,0 +1,15 @@
// Package resolver resolves network names to addresses
package resolver
// Resolver is network resolver. It's used to find network nodes
// via the name to connect to. This is done based on Network.Name().
// Before we can be part of any network, we have to connect to it.
type Resolver interface {
// Resolve returns a list of addresses for an name
Resolve(name string) ([]*Record, error)
}
// A resolved record
type Record struct {
Address string `json:"address"`
}

View File

@ -1,203 +0,0 @@
// Package transport implements the network as a transport interface
package transport
import (
"context"
"time"
"github.com/micro/go-micro/network"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/backoff"
)
type networkKey struct{}
// Transport is a network transport
type Transport struct {
Network network.Network
options transport.Options
}
// Socket is a transport socket
type Socket struct {
// The service
Service string
// Send via Network.Send(Message)
Network network.Network
// Remote/Local
remote, local string
// the first message if its a listener
message *network.Message
}
// Listener is a transport listener
type Listener struct {
// The local service
Service string
// The network
Network network.Network
}
func (s *Socket) Local() string {
return s.local
}
func (s *Socket) Remote() string {
return s.remote
}
func (s *Socket) Close() error {
// TODO: should it close the network?
return s.Network.Close()
}
func (t *Transport) Init(opts ...transport.Option) error {
for _, o := range opts {
o(&t.options)
}
return nil
}
func (t *Transport) Options() transport.Options {
return t.options
}
func (t *Transport) Dial(service string, opts ...transport.DialOption) (transport.Client, error) {
// TODO: establish pseudo socket?
return &Socket{
Service: service,
Network: t.Network,
remote: service,
// TODO: local
local: "local",
}, nil
}
func (t *Transport) Listen(service string, opts ...transport.ListenOption) (transport.Listener, error) {
// TODO specify connect id
if err := t.Network.Connect("micro.mu"); err != nil {
return nil, err
}
// advertise the service
if err := t.Network.Advertise(service); err != nil {
return nil, err
}
return &Listener{
Service: service,
Network: t.Network,
}, nil
}
func (t *Transport) String() string {
return "network"
}
func (s *Socket) Send(msg *transport.Message) error {
// TODO: set routing headers?
return s.Network.Send(&network.Message{
Header: msg.Header,
Body: msg.Body,
})
}
func (s *Socket) Recv(msg *transport.Message) error {
if msg == nil {
msg = new(transport.Message)
}
// return first message
if s.message != nil {
msg.Header = s.message.Header
msg.Body = s.message.Body
s.message = nil
return nil
}
m, err := s.Network.Accept()
if err != nil {
return err
}
msg.Header = m.Header
msg.Body = m.Body
return nil
}
func (l *Listener) Addr() string {
return l.Service
}
func (l *Listener) Close() error {
return l.Network.Close()
}
func (l *Listener) Accept(fn func(transport.Socket)) error {
var i int
for {
msg, err := l.Network.Accept()
if err != nil {
// increment error counter
i++
// break if lots of error
if i > 3 {
return err
}
// otherwise continue
time.Sleep(backoff.Do(i))
continue
}
// reset
i = 0
// execute in go routine
go fn(&Socket{
Service: l.Service,
Network: l.Network,
local: l.Service,
// TODO: remote
remote: "remote",
message: msg,
})
}
}
// NewTransport returns a new network transport. It assumes the network is already connected
func NewTransport(opts ...transport.Option) transport.Transport {
options := transport.Options{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
net, ok := options.Context.Value(networkKey{}).(network.Network)
if !ok {
net = network.DefaultNetwork
}
return &Transport{
options: options,
Network: net,
}
}
// WithNetwork passes in the network
func WithNetwork(n network.Network) transport.Option {
return func(o *transport.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, networkKey{}, n)
}
}

View File

@ -7,9 +7,9 @@ import (
"github.com/micro/cli" "github.com/micro/cli"
"github.com/micro/go-micro/broker" "github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
"github.com/micro/go-micro/cmd" "github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
) )

View File

@ -1,25 +0,0 @@
# Go Proxy [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![GoDoc](https://godoc.org/github.com/micro/go-proxy?status.svg)](https://godoc.org/github.com/micro/go-proxy)
Go Proxy is a proxy library for Go Micro.
## Overview
Go Micro is a distributed systems framework for client/server communication. It handles the details
around discovery, fault tolerance, rpc communication, etc. We may want to leverage this in broader ecosystems
which make use of standard http or we may also want to offload a number of requirements to a single proxy.
## Features
- **Transparent Proxy** - Proxy requests to any micro services through a single location. Go Proxy enables
you to write Go Micro proxies which handle and forward requests. This is good for incorporating wrappers.
- **Single Backend Router** - Enable the single backend router to proxy directly to your local app. The proxy
allows you to set a router which serves your backend service whether its http, grpc, etc.
- **Protocol Aware Handler** - Set a request handler which speaks your app protocol to make outbound requests.
Your app may not speak the MUCP protocol so it may be easier to translate internally.
- **Control Planes** - Additionally we support use of control planes to offload many distributed systems concerns.
* [x] [Consul](https://www.consul.io/docs/connect/native.html) - Using Connect-Native to provide secure mTLS.
* [x] [NATS](https://nats.io/) - Fully leveraging NATS as the control plane and data plane.

View File

@ -9,7 +9,6 @@ import (
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/grpc" "github.com/micro/go-micro/client/grpc"
"github.com/micro/go-micro/codec" "github.com/micro/go-micro/codec"
"github.com/micro/go-micro/codec/bytes"
"github.com/micro/go-micro/config/options" "github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/proxy" "github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
@ -86,14 +85,8 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
} }
} }
// read initial request
body, err := req.Read()
if err != nil {
return err
}
// create new request with raw bytes body // create new request with raw bytes body
creq := p.Client.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType())) creq := p.Client.NewRequest(service, endpoint, nil, client.WithContentType(req.ContentType()))
// create new stream // create new stream
stream, err := p.Client.Stream(ctx, creq, opts...) stream, err := p.Client.Stream(ctx, creq, opts...)

View File

@ -10,8 +10,8 @@ import (
"net/url" "net/url"
"path" "path"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/config/options" "github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/proxy" "github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
) )

Some files were not shown because too many files have changed in this diff Show More