Compare commits
189 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
114bc1e18b | ||
|
af94899b54 | ||
|
266b6dbc64 | ||
|
5932dd753c | ||
86a6328254 | |||
|
0d33c029a9 | ||
|
8ecd2381a4 | ||
|
7318807dce | ||
|
811275be26 | ||
|
3f3fd38601 | ||
|
44dd0b1302 | ||
|
6475c1f3ad | ||
|
deabf0b8c9 | ||
|
04ee4b04ad | ||
|
2892686c5f | ||
|
8ee31a63f1 | ||
|
4e363da91f | ||
|
8b63df7a98 | ||
|
b06854b0d5 | ||
|
39bf71376a | ||
|
12d9c5b187 | ||
|
d2eba3f8f9 | ||
|
19f2f8b161 | ||
|
d41185eb84 | ||
|
c420fa2dec | ||
|
db03a564fb | ||
9763820c75 | |||
|
9095b99f6b | ||
|
080363e8c4 | ||
|
252667398e | ||
|
f82c267d81 | ||
|
61fe552ac4 | ||
|
95045be83d | ||
|
52ccd900c7 | ||
|
64a251d69a | ||
|
cae4148594 | ||
|
38e29c5101 | ||
|
8dc3fb964e | ||
|
212144d658 | ||
|
11d81221cc | ||
|
55252cbc32 | ||
|
c87a58db0a | ||
49d73faa5f | |||
da6c1be607 | |||
|
94d409b180 | ||
|
d6e97c5970 | ||
|
64d5a528ca | ||
|
538d3752f9 | ||
|
fb5b358ae2 | ||
|
6a0082741c | ||
|
5744050943 | ||
|
168cc06827 | ||
|
fa01cadc35 | ||
|
342c29de7d | ||
|
eeed493766 | ||
|
90d7a87914 | ||
|
a1c6cdf193 | ||
|
bec13a45cd | ||
|
4107733453 | ||
|
97c1300f53 | ||
|
0af8be35bb | ||
|
a91b3f3e8b | ||
|
383658edf2 | ||
|
16754a7477 | ||
|
58b25d7241 | ||
|
1c7d44282e | ||
|
8e9eef794f | ||
|
920c026f14 | ||
|
946c76cb03 | ||
|
43d11a9b8d | ||
|
9f481542f3 | ||
|
cffa5b6b50 | ||
|
8867539d78 | ||
|
671408b3a5 | ||
|
bdb62e8ed1 | ||
|
72522a869a | ||
|
fd5c29addc | ||
|
65b1283459 | ||
|
5ffe367cae | ||
|
5ae3e179b9 | ||
c696a859be | |||
|
174b01ca29 | ||
|
c433de80cd | ||
|
f2b4c07a00 | ||
|
929ffdcc42 | ||
|
5aa28dfb0d | ||
|
a9e8fc6039 | ||
|
0b1e6d7eaf | ||
|
1ffa289d39 | ||
|
68419cc024 | ||
8227206208 | |||
|
6f28852e1b | ||
|
0e3550229b | ||
|
f9400ba713 | ||
|
ce080d76c6 | ||
|
254045e9f3 | ||
|
b84134581c | ||
|
f67c5e779f | ||
|
4a694c9d02 | ||
|
24b8d2a315 | ||
|
2f3c251b00 | ||
|
c1b0a968ae | ||
|
81e9298be6 | ||
|
45cd14c4b7 | ||
|
8579c8b321 | ||
|
bd37e67839 | ||
|
d3151f1f0f | ||
|
c45ea62ea8 | ||
|
c14bf5dc4e | ||
|
292da40886 | ||
|
6f7702a093 | ||
|
a94a95ab55 | ||
|
e8d2f207d8 | ||
|
bd1918900e | ||
|
cf3af68e31 | ||
|
15e3b9b4c0 | ||
|
107a7ab07f | ||
|
e9dfccc616 | ||
|
f88518d994 | ||
|
ee35fe61af | ||
|
dee63b2b2c | ||
|
0aa01b2ebf | ||
|
f089a89e8a | ||
|
174fbde049 | ||
|
967d7ecda7 | ||
|
fb76755684 | ||
|
cf593e7c50 | ||
|
74286c2939 | ||
|
f9c639af4e | ||
|
dab0f3223f | ||
|
d89256d8d5 | ||
|
99b410c81b | ||
|
92b7d2db3b | ||
|
20c6c36bc4 | ||
|
1f626a55ed | ||
|
b42d242ec1 | ||
|
51922c1763 | ||
|
1c6b85e05d | ||
|
e85863d6cc | ||
|
5d7bf53f78 | ||
|
44c0f1946d | ||
|
1c9ada6413 | ||
|
c170189efb | ||
|
3831199600 | ||
|
1f658cfbff | ||
|
f26d470db1 | ||
|
f5b8a12106 | ||
|
494eb13534 | ||
|
4db1e09798 | ||
|
232c8ac7a1 | ||
|
68d0efbeaa | ||
|
70aaca9876 | ||
|
3ce71e12ff | ||
|
fb3d729681 | ||
|
d65658c890 | ||
|
3fc04f4dff | ||
|
82f94c7861 | ||
|
ecac392dbe | ||
|
4e5a568063 | ||
|
87de2ecaa0 | ||
|
4f1dd3f965 | ||
|
71122836b8 | ||
|
b67be88952 | ||
|
83b232ae26 | ||
|
776284b187 | ||
|
53ee4ee482 | ||
|
35729092e0 | ||
|
4f5db08238 | ||
|
68789af4ea | ||
|
b3d4a7f740 | ||
|
f4f178c130 | ||
|
1ff65e140a | ||
|
326156671d | ||
|
6353b2b894 | ||
|
caca93f65b | ||
|
bf4a73d5c0 | ||
|
fe180148a1 | ||
|
842fc01568 | ||
|
d4832e8f34 | ||
|
5ac5865154 | ||
|
f07a6ac29b | ||
|
d64f8c665e | ||
|
407694232a | ||
|
418b8648bb | ||
|
85e273afa5 | ||
|
ab9fa20a50 | ||
|
4fddd69229 | ||
|
317cf76566 | ||
|
f792fac1cc |
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
issuehunt: micro/development
|
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: For reporting bugs in go-micro
|
||||
title: "[BUG]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
1. What are you trying to do?
|
||||
2. What did you expect to happen?
|
||||
3. What happens instead?
|
||||
|
||||
**How to reproduce the bug:**
|
||||
|
||||
If possible, please include a minimal code snippet here.
|
||||
|
||||
**Environment:**
|
||||
Go Version: please paste `go version` output here
|
||||
```
|
||||
please paste `go env` output here
|
||||
```
|
17
.github/ISSUE_TEMPLATE/feature-request---enhancement.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature-request---enhancement.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request / Enhancement
|
||||
about: If you have a need not served by go-micro
|
||||
title: "[FEATURE]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
14
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question about go-micro
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Before asking, please check if your question has already been answered:
|
||||
|
||||
1. Check the documentation - https://micro.mu/docs/
|
||||
2. Check the examples and plugins - https://github.com/micro/examples & https://github.com/micro/go-plugins
|
||||
3. Search existing issues
|
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM golang:1.13-alpine
|
||||
|
||||
RUN mkdir /user && \
|
||||
echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd && \
|
||||
echo 'nobody:x:65534:' > /user/group
|
||||
|
||||
ENV GO111MODULE=on
|
||||
RUN apk --no-cache add make git gcc libtool musl-dev ca-certificates && \
|
||||
rm -rf /var/cache/apk/* /tmp/*
|
||||
|
||||
WORKDIR /
|
||||
COPY ./go.mod ./go.sum ./
|
||||
RUN go mod download && rm go.mod go.sum
|
@@ -12,7 +12,7 @@ but everything can be easily swapped out.
|
||||
|
||||
Plugins are available at [github.com/micro/go-plugins](https://github.com/micro/go-plugins).
|
||||
|
||||
Follow us on [Twitter](https://twitter.com/microhq) or join the [Slack](http://slack.micro.mu/) community.
|
||||
Follow us on [Twitter](https://twitter.com/microhq) or join the [Slack](https://micro.mu/slack) community.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -20,8 +20,7 @@ Go Micro abstracts away the details of distributed systems. Here are the main fe
|
||||
|
||||
- **Service Discovery** - Automatic service registration and name resolution. Service discovery is at the core of micro service
|
||||
development. When service A needs to speak to service B it needs the location of that service. The default discovery mechanism is
|
||||
multicast DNS (mdns), a zeroconf system. You can optionally set gossip using the SWIM protocol for p2p networks or consul for a
|
||||
resilient cloud-native setup.
|
||||
multicast DNS (mdns), a zeroconf system.
|
||||
|
||||
- **Load Balancing** - Client side load balancing built on service discovery. Once we have the addresses of any number of instances
|
||||
of a service we now need a way to decide which node to route to. We use random hashed load balancing to provide even distribution
|
||||
@@ -45,5 +44,5 @@ are pluggable and allows Go Micro to be runtime agnostic. You can plugin any und
|
||||
|
||||
## Getting Started
|
||||
|
||||
See the [docs](https://micro.mu/docs/go-micro.html) for detailed information on the architecture, installation and use of go-micro.
|
||||
See the [docs](https://micro.mu/docs/framework.html) for detailed information on the architecture, installation and use of go-micro.
|
||||
|
||||
|
1
_config.yml
Normal file
1
_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-architect
|
@@ -1,11 +1,13 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/micro/go-bot/proto/bot.proto
|
||||
// source: github.com/micro/go-micro/agent/proto/bot.proto
|
||||
|
||||
package go_micro_bot
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -16,7 +18,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type HelpRequest struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
@@ -28,16 +30,17 @@ func (m *HelpRequest) Reset() { *m = HelpRequest{} }
|
||||
func (m *HelpRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*HelpRequest) ProtoMessage() {}
|
||||
func (*HelpRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_bot_654832eab83ed4b5, []int{0}
|
||||
return fileDescriptor_018e8d5b14a89d12, []int{0}
|
||||
}
|
||||
|
||||
func (m *HelpRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HelpRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HelpRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HelpRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *HelpRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpRequest.Merge(dst, src)
|
||||
func (m *HelpRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpRequest.Merge(m, src)
|
||||
}
|
||||
func (m *HelpRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_HelpRequest.Size(m)
|
||||
@@ -60,16 +63,17 @@ func (m *HelpResponse) Reset() { *m = HelpResponse{} }
|
||||
func (m *HelpResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*HelpResponse) ProtoMessage() {}
|
||||
func (*HelpResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_bot_654832eab83ed4b5, []int{1}
|
||||
return fileDescriptor_018e8d5b14a89d12, []int{1}
|
||||
}
|
||||
|
||||
func (m *HelpResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HelpResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HelpResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HelpResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *HelpResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpResponse.Merge(dst, src)
|
||||
func (m *HelpResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpResponse.Merge(m, src)
|
||||
}
|
||||
func (m *HelpResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_HelpResponse.Size(m)
|
||||
@@ -105,16 +109,17 @@ func (m *ExecRequest) Reset() { *m = ExecRequest{} }
|
||||
func (m *ExecRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExecRequest) ProtoMessage() {}
|
||||
func (*ExecRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_bot_654832eab83ed4b5, []int{2}
|
||||
return fileDescriptor_018e8d5b14a89d12, []int{2}
|
||||
}
|
||||
|
||||
func (m *ExecRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ExecRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ExecRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ExecRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ExecRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecRequest.Merge(dst, src)
|
||||
func (m *ExecRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecRequest.Merge(m, src)
|
||||
}
|
||||
func (m *ExecRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_ExecRequest.Size(m)
|
||||
@@ -144,16 +149,17 @@ func (m *ExecResponse) Reset() { *m = ExecResponse{} }
|
||||
func (m *ExecResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExecResponse) ProtoMessage() {}
|
||||
func (*ExecResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_bot_654832eab83ed4b5, []int{3}
|
||||
return fileDescriptor_018e8d5b14a89d12, []int{3}
|
||||
}
|
||||
|
||||
func (m *ExecResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ExecResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ExecResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ExecResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ExecResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecResponse.Merge(dst, src)
|
||||
func (m *ExecResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecResponse.Merge(m, src)
|
||||
}
|
||||
func (m *ExecResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_ExecResponse.Size(m)
|
||||
@@ -186,25 +192,25 @@ func init() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/micro/go-bot/proto/bot.proto", fileDescriptor_bot_654832eab83ed4b5)
|
||||
proto.RegisterFile("github.com/micro/go-micro/agent/proto/bot.proto", fileDescriptor_018e8d5b14a89d12)
|
||||
}
|
||||
|
||||
var fileDescriptor_bot_654832eab83ed4b5 = []byte{
|
||||
var fileDescriptor_018e8d5b14a89d12 = []byte{
|
||||
// 246 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x41, 0x4b, 0xc4, 0x30,
|
||||
0x10, 0x85, 0xb7, 0xba, 0xae, 0xec, 0xb4, 0x5e, 0x82, 0x48, 0xdd, 0x53, 0xcd, 0xc5, 0xbd, 0x98,
|
||||
0x82, 0x5e, 0x05, 0x0f, 0xa2, 0x78, 0xee, 0x3f, 0x68, 0xba, 0x43, 0x2c, 0x6c, 0x3b, 0x35, 0x99,
|
||||
0x82, 0xff, 0xc1, 0x3f, 0x2d, 0x4d, 0x72, 0x08, 0xcb, 0xde, 0xe6, 0x65, 0x86, 0xf7, 0xbe, 0x17,
|
||||
0x78, 0x34, 0x3d, 0x7f, 0xcf, 0x5a, 0x75, 0x34, 0xd4, 0x43, 0xdf, 0x59, 0xaa, 0x0d, 0x3d, 0x69,
|
||||
0xe2, 0x7a, 0xb2, 0xc4, 0x54, 0x6b, 0x62, 0xe5, 0x27, 0x51, 0x18, 0x52, 0xfe, 0x40, 0x69, 0x62,
|
||||
0x79, 0x03, 0xf9, 0x17, 0x1e, 0xa7, 0x06, 0x7f, 0x66, 0x74, 0x2c, 0x3f, 0xa1, 0x08, 0xd2, 0x4d,
|
||||
0x34, 0x3a, 0x14, 0xb7, 0x70, 0x35, 0xbb, 0xd6, 0x60, 0x99, 0x55, 0xd9, 0x7e, 0xdb, 0x04, 0x21,
|
||||
0x2a, 0xc8, 0x0f, 0xe8, 0x3a, 0xdb, 0x4f, 0xdc, 0xd3, 0x58, 0x5e, 0xf8, 0x5d, 0xfa, 0x24, 0x1f,
|
||||
0x20, 0xff, 0xf8, 0xc5, 0x2e, 0xda, 0x0a, 0x01, 0xeb, 0xd6, 0x1a, 0x57, 0x66, 0xd5, 0xe5, 0x7e,
|
||||
0xdb, 0xf8, 0x59, 0xbe, 0x42, 0x11, 0x4e, 0x62, 0xd4, 0x1d, 0x6c, 0x2c, 0xba, 0xf9, 0xc8, 0x3e,
|
||||
0xab, 0x68, 0xa2, 0x5a, 0x10, 0xd0, 0x5a, 0xb2, 0x31, 0x26, 0x88, 0xe7, 0xbf, 0x0c, 0xae, 0xdf,
|
||||
0x69, 0x18, 0xda, 0xf1, 0x20, 0xde, 0x60, 0xbd, 0x40, 0x8b, 0x7b, 0x95, 0x56, 0x53, 0x49, 0xaf,
|
||||
0xdd, 0xee, 0xdc, 0x2a, 0x04, 0xcb, 0xd5, 0x62, 0xb0, 0xa0, 0x9c, 0x1a, 0x24, 0x0d, 0x4e, 0x0d,
|
||||
0x52, 0x72, 0xb9, 0xd2, 0x1b, 0xff, 0xb5, 0x2f, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcb, 0x77,
|
||||
0xdf, 0x28, 0x85, 0x01, 0x00, 0x00,
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x50, 0x4d, 0x4b, 0xc4, 0x30,
|
||||
0x10, 0xdd, 0xea, 0xba, 0xb2, 0xd3, 0x7a, 0x09, 0x22, 0x75, 0x4f, 0x35, 0xa7, 0xbd, 0x98, 0x80,
|
||||
0x5e, 0x05, 0x0f, 0xa2, 0x78, 0xee, 0x3f, 0x68, 0xbb, 0x43, 0x2c, 0x6c, 0x3b, 0x35, 0x99, 0x82,
|
||||
0xff, 0xc1, 0x3f, 0x2d, 0x4d, 0x72, 0x08, 0xc5, 0xdb, 0x7b, 0x79, 0xe1, 0x7d, 0x0c, 0x68, 0xd3,
|
||||
0xf3, 0xd7, 0xdc, 0xaa, 0x8e, 0x06, 0x3d, 0xf4, 0x9d, 0x25, 0x6d, 0xe8, 0x31, 0x80, 0xc6, 0xe0,
|
||||
0xc8, 0x7a, 0xb2, 0xc4, 0xa4, 0x5b, 0x62, 0xe5, 0x91, 0x28, 0x0c, 0x29, 0xaf, 0xab, 0x96, 0x58,
|
||||
0xde, 0x40, 0xfe, 0x89, 0xe7, 0xa9, 0xc6, 0xef, 0x19, 0x1d, 0xcb, 0x0f, 0x28, 0x02, 0x75, 0x13,
|
||||
0x8d, 0x0e, 0xc5, 0x2d, 0x5c, 0xcd, 0xae, 0x31, 0x58, 0x66, 0x55, 0x76, 0xdc, 0xd7, 0x81, 0x88,
|
||||
0x0a, 0xf2, 0x13, 0xba, 0xce, 0xf6, 0x13, 0xf7, 0x34, 0x96, 0x17, 0x5e, 0x4b, 0x9f, 0xe4, 0x03,
|
||||
0xe4, 0xef, 0x3f, 0xd8, 0x45, 0x5b, 0x21, 0x60, 0xdb, 0x58, 0xe3, 0xca, 0xac, 0xba, 0x3c, 0xee,
|
||||
0x6b, 0x8f, 0xe5, 0x0b, 0x14, 0xe1, 0x4b, 0x8c, 0xba, 0x83, 0x9d, 0x45, 0x37, 0x9f, 0xd9, 0x67,
|
||||
0x15, 0x75, 0x64, 0x4b, 0x05, 0xb4, 0x96, 0x6c, 0x8c, 0x09, 0xe4, 0xe9, 0x37, 0x83, 0xeb, 0x37,
|
||||
0x1a, 0x86, 0x66, 0x3c, 0x89, 0x57, 0xd8, 0x2e, 0xa5, 0xc5, 0xbd, 0x4a, 0xa7, 0xa9, 0x64, 0xd7,
|
||||
0xe1, 0xf0, 0x9f, 0x14, 0x82, 0xe5, 0x66, 0x31, 0x58, 0xaa, 0xac, 0x0d, 0x92, 0x05, 0x6b, 0x83,
|
||||
0xb4, 0xb9, 0xdc, 0xb4, 0x3b, 0x7f, 0xda, 0xe7, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0xbd,
|
||||
0x39, 0x29, 0x8d, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
@@ -1,23 +1,13 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: github.com/micro/go-bot/proto/bot.proto
|
||||
// source: github.com/micro/go-micro/agent/proto/bot.proto
|
||||
|
||||
/*
|
||||
Package go_micro_bot is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
github.com/micro/go-bot/proto/bot.proto
|
||||
|
||||
It has these top-level messages:
|
||||
HelpRequest
|
||||
HelpResponse
|
||||
ExecRequest
|
||||
ExecResponse
|
||||
*/
|
||||
package go_micro_bot
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
@@ -34,7 +24,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
@@ -94,12 +84,12 @@ type CommandHandler interface {
|
||||
}
|
||||
|
||||
func RegisterCommandHandler(s server.Server, hdlr CommandHandler, opts ...server.HandlerOption) error {
|
||||
type _command interface {
|
||||
type command interface {
|
||||
Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error
|
||||
Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error
|
||||
}
|
||||
type Command struct {
|
||||
_command
|
||||
command
|
||||
}
|
||||
h := &commandHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Command{h}, opts...))
|
@@ -29,16 +29,20 @@ func requestToProto(r *http.Request) (*api.Request, error) {
|
||||
|
||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
ct = "application/x-www-form-urlencoded"
|
||||
ct = "text/plain; charset=UTF-8" //default CT is text/plain
|
||||
r.Header.Set("Content-Type", ct)
|
||||
}
|
||||
|
||||
switch ct {
|
||||
case "application/x-www-form-urlencoded":
|
||||
// expect form vals
|
||||
default:
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
req.Body = string(data)
|
||||
//set the body:
|
||||
if r.Body != nil {
|
||||
switch ct {
|
||||
case "application/x-www-form-urlencoded":
|
||||
// expect form vals in Post data
|
||||
default:
|
||||
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
req.Body = string(data)
|
||||
}
|
||||
}
|
||||
|
||||
// Set X-Forwarded-For if it does not exist
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
func TestRequestToProto(t *testing.T) {
|
||||
testData := []*http.Request{
|
||||
&http.Request{
|
||||
{
|
||||
Method: "GET",
|
||||
Header: http.Header{
|
||||
"Header": []string{"test"},
|
||||
|
@@ -2,6 +2,7 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -91,12 +92,17 @@ func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// set body
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
if r.Method == "GET" {
|
||||
bytes, _ := json.Marshal(r.URL.Query())
|
||||
ev.Data = string(bytes)
|
||||
} else {
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
ev.Data = string(b)
|
||||
}
|
||||
ev.Data = string(b)
|
||||
|
||||
// get client
|
||||
c := e.options.Service.Client()
|
||||
|
@@ -27,7 +27,7 @@ func testHttp(t *testing.T, path, service, ns string) {
|
||||
s := ®istry.Service{
|
||||
Name: service,
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: service + "-1",
|
||||
Address: l.Addr().String(),
|
||||
},
|
||||
|
@@ -3,9 +3,11 @@
|
||||
|
||||
package go_api
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -16,7 +18,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Pair struct {
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
@@ -30,16 +32,17 @@ func (m *Pair) Reset() { *m = Pair{} }
|
||||
func (m *Pair) String() string { return proto.CompactTextString(m) }
|
||||
func (*Pair) ProtoMessage() {}
|
||||
func (*Pair) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_api_17a7876430d97ebd, []int{0}
|
||||
return fileDescriptor_7b6696ef87ec1943, []int{0}
|
||||
}
|
||||
|
||||
func (m *Pair) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Pair.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Pair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Pair.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Pair) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Pair.Merge(dst, src)
|
||||
func (m *Pair) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Pair.Merge(m, src)
|
||||
}
|
||||
func (m *Pair) XXX_Size() int {
|
||||
return xxx_messageInfo_Pair.Size(m)
|
||||
@@ -83,16 +86,17 @@ 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_api_17a7876430d97ebd, []int{1}
|
||||
return fileDescriptor_7b6696ef87ec1943, []int{1}
|
||||
}
|
||||
|
||||
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 (dst *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(dst, src)
|
||||
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)
|
||||
@@ -167,16 +171,17 @@ 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_api_17a7876430d97ebd, []int{2}
|
||||
return fileDescriptor_7b6696ef87ec1943, []int{2}
|
||||
}
|
||||
|
||||
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 (dst *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(dst, src)
|
||||
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)
|
||||
@@ -230,16 +235,17 @@ func (m *Event) Reset() { *m = Event{} }
|
||||
func (m *Event) String() string { return proto.CompactTextString(m) }
|
||||
func (*Event) ProtoMessage() {}
|
||||
func (*Event) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_api_17a7876430d97ebd, []int{3}
|
||||
return fileDescriptor_7b6696ef87ec1943, []int{3}
|
||||
}
|
||||
|
||||
func (m *Event) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Event.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Event.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Event) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Event.Merge(dst, src)
|
||||
func (m *Event) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Event.Merge(m, src)
|
||||
}
|
||||
func (m *Event) XXX_Size() int {
|
||||
return xxx_messageInfo_Event.Size(m)
|
||||
@@ -298,35 +304,35 @@ func init() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/micro/go-micro/api/proto/api.proto", fileDescriptor_api_17a7876430d97ebd)
|
||||
proto.RegisterFile("github.com/micro/go-micro/api/proto/api.proto", fileDescriptor_7b6696ef87ec1943)
|
||||
}
|
||||
|
||||
var fileDescriptor_api_17a7876430d97ebd = []byte{
|
||||
// 410 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0xc1, 0x6e, 0xd4, 0x30,
|
||||
0x10, 0x55, 0xe2, 0x24, 0x6d, 0x66, 0x11, 0x42, 0x3e, 0x20, 0x53, 0x2a, 0xb4, 0xca, 0x85, 0x15,
|
||||
0x52, 0x13, 0x68, 0x39, 0x20, 0xae, 0xb0, 0x2a, 0xc7, 0xca, 0x7f, 0xe0, 0x6d, 0xac, 0xc4, 0x62,
|
||||
0x13, 0x9b, 0xd8, 0xa9, 0xb4, 0x1f, 0xc7, 0x81, 0xcf, 0xe0, 0x6f, 0x90, 0x27, 0xde, 0xdd, 0xb2,
|
||||
0x5a, 0x2e, 0x74, 0x6f, 0x2f, 0xf6, 0x9b, 0x37, 0x6f, 0xde, 0x38, 0xf0, 0xb6, 0x51, 0xae, 0x1d,
|
||||
0x57, 0xe5, 0xbd, 0xee, 0xaa, 0x4e, 0xdd, 0x0f, 0xba, 0x6a, 0xf4, 0x95, 0x30, 0xaa, 0x32, 0x83,
|
||||
0x76, 0xba, 0x12, 0x46, 0x95, 0x88, 0x68, 0xd6, 0xe8, 0x52, 0x18, 0x55, 0xbc, 0x87, 0xe4, 0x4e,
|
||||
0xa8, 0x81, 0xbe, 0x00, 0xf2, 0x5d, 0x6e, 0x58, 0x34, 0x8f, 0x16, 0x39, 0xf7, 0x90, 0xbe, 0x84,
|
||||
0xec, 0x41, 0xac, 0x47, 0x69, 0x59, 0x3c, 0x27, 0x8b, 0x9c, 0x87, 0xaf, 0xe2, 0x17, 0x81, 0x33,
|
||||
0x2e, 0x7f, 0x8c, 0xd2, 0x3a, 0xcf, 0xe9, 0xa4, 0x6b, 0x75, 0x1d, 0x0a, 0xc3, 0x17, 0xa5, 0x90,
|
||||
0x18, 0xe1, 0x5a, 0x16, 0xe3, 0x29, 0x62, 0x7a, 0x03, 0x59, 0x2b, 0x45, 0x2d, 0x07, 0x46, 0xe6,
|
||||
0x64, 0x31, 0xbb, 0x7e, 0x5d, 0x4e, 0x16, 0xca, 0x20, 0x56, 0x7e, 0xc3, 0xdb, 0x65, 0xef, 0x86,
|
||||
0x0d, 0x0f, 0x54, 0xfa, 0x0e, 0x48, 0x23, 0x1d, 0x4b, 0xb0, 0x82, 0x1d, 0x56, 0xdc, 0x4a, 0x37,
|
||||
0xd1, 0x3d, 0x89, 0x5e, 0x41, 0x62, 0xb4, 0x75, 0x2c, 0x45, 0xf2, 0xab, 0x43, 0xf2, 0x9d, 0xb6,
|
||||
0x81, 0x8d, 0x34, 0xef, 0x71, 0xa5, 0xeb, 0x0d, 0xcb, 0x26, 0x8f, 0x1e, 0xfb, 0x14, 0xc6, 0x61,
|
||||
0xcd, 0xce, 0xa6, 0x14, 0xc6, 0x61, 0x7d, 0x71, 0x0b, 0xb3, 0x47, 0xbe, 0x8e, 0xc4, 0x54, 0x40,
|
||||
0x8a, 0xc1, 0xe0, 0xac, 0xb3, 0xeb, 0x67, 0xdb, 0xb6, 0x3e, 0x55, 0x3e, 0x5d, 0x7d, 0x8e, 0x3f,
|
||||
0x45, 0x17, 0x5f, 0xe1, 0x7c, 0x6b, 0xf7, 0x09, 0x2a, 0x4b, 0xc8, 0x77, 0x73, 0xfc, 0xbf, 0x4c,
|
||||
0xf1, 0x33, 0x82, 0x73, 0x2e, 0xad, 0xd1, 0xbd, 0x95, 0xf4, 0x0d, 0x80, 0x75, 0xc2, 0x8d, 0xf6,
|
||||
0x8b, 0xae, 0x25, 0xaa, 0xa5, 0xfc, 0xd1, 0x09, 0xfd, 0xb8, 0x5b, 0x5c, 0x8c, 0xc9, 0x5e, 0xee,
|
||||
0x93, 0x9d, 0x14, 0x8e, 0x6e, 0x6e, 0x1b, 0x2f, 0xd9, 0xc7, 0x7b, 0xb2, 0x30, 0x8b, 0xdf, 0x11,
|
||||
0xa4, 0xcb, 0x07, 0xd9, 0xe3, 0x16, 0x7b, 0xd1, 0xc9, 0x20, 0x82, 0x98, 0x3e, 0x87, 0x58, 0xd5,
|
||||
0xe1, 0xed, 0xc5, 0xaa, 0xa6, 0x97, 0x90, 0x3b, 0xd5, 0x49, 0xeb, 0x44, 0x67, 0xd0, 0x0f, 0xe1,
|
||||
0xfb, 0x03, 0xfa, 0x61, 0x37, 0x5e, 0xf2, 0xf7, 0xc3, 0xc1, 0x06, 0xff, 0x9a, 0xad, 0x16, 0x4e,
|
||||
0xb0, 0x74, 0x6a, 0xea, 0xf1, 0xc9, 0x66, 0x5b, 0x65, 0xf8, 0x83, 0xde, 0xfc, 0x09, 0x00, 0x00,
|
||||
0xff, 0xff, 0x7a, 0xb4, 0xd4, 0x8f, 0xcb, 0x03, 0x00, 0x00,
|
||||
var fileDescriptor_7b6696ef87ec1943 = []byte{
|
||||
// 408 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0x4d, 0x8f, 0xd3, 0x30,
|
||||
0x10, 0x55, 0xe2, 0x24, 0xbb, 0x99, 0x22, 0x84, 0x7c, 0x40, 0x66, 0x59, 0xa1, 0x2a, 0xa7, 0x0a,
|
||||
0xa9, 0x29, 0xec, 0x72, 0x40, 0x5c, 0xa1, 0x5a, 0x8e, 0x2b, 0xff, 0x03, 0x77, 0x63, 0x25, 0x16,
|
||||
0x4d, 0x1c, 0x62, 0xa7, 0x52, 0x7f, 0x1c, 0x07, 0x7e, 0x06, 0xff, 0x06, 0x79, 0xec, 0x7e, 0x50,
|
||||
0x95, 0x0b, 0xf4, 0xf6, 0x62, 0xbf, 0x79, 0xf3, 0xe6, 0x8d, 0x03, 0xf3, 0x5a, 0xd9, 0x66, 0x5c,
|
||||
0x95, 0x4f, 0xba, 0x5d, 0xb4, 0xea, 0x69, 0xd0, 0x8b, 0x5a, 0xcf, 0x3d, 0x10, 0xbd, 0x5a, 0xf4,
|
||||
0x83, 0xb6, 0x88, 0x4a, 0x44, 0x34, 0xab, 0x75, 0x29, 0x7a, 0x55, 0xbc, 0x83, 0xe4, 0x51, 0xa8,
|
||||
0x81, 0xbe, 0x00, 0xf2, 0x4d, 0x6e, 0x59, 0x34, 0x8d, 0x66, 0x39, 0x77, 0x90, 0xbe, 0x84, 0x6c,
|
||||
0x23, 0xd6, 0xa3, 0x34, 0x2c, 0x9e, 0x92, 0x59, 0xce, 0xc3, 0x57, 0xf1, 0x93, 0xc0, 0x15, 0x97,
|
||||
0xdf, 0x47, 0x69, 0xac, 0xe3, 0xb4, 0xd2, 0x36, 0xba, 0x0a, 0x85, 0xe1, 0x8b, 0x52, 0x48, 0x7a,
|
||||
0x61, 0x1b, 0x16, 0xe3, 0x29, 0x62, 0x7a, 0x0f, 0x59, 0x23, 0x45, 0x25, 0x07, 0x46, 0xa6, 0x64,
|
||||
0x36, 0xb9, 0x7b, 0x5d, 0x7a, 0x0b, 0x65, 0x10, 0x2b, 0xbf, 0xe2, 0xed, 0xb2, 0xb3, 0xc3, 0x96,
|
||||
0x07, 0x2a, 0x7d, 0x0b, 0xa4, 0x96, 0x96, 0x25, 0x58, 0xc1, 0x4e, 0x2b, 0x1e, 0xa4, 0xf5, 0x74,
|
||||
0x47, 0xa2, 0x73, 0x48, 0x7a, 0x6d, 0x2c, 0x4b, 0x91, 0xfc, 0xea, 0x94, 0xfc, 0xa8, 0x4d, 0x60,
|
||||
0x23, 0xcd, 0x79, 0x5c, 0xe9, 0x6a, 0xcb, 0x32, 0xef, 0xd1, 0x61, 0x97, 0xc2, 0x38, 0xac, 0xd9,
|
||||
0x95, 0x4f, 0x61, 0x1c, 0xd6, 0x37, 0x0f, 0x30, 0x39, 0xf2, 0x75, 0x26, 0xa6, 0x02, 0x52, 0x0c,
|
||||
0x06, 0x67, 0x9d, 0xdc, 0x3d, 0xdb, 0xb5, 0x75, 0xa9, 0x72, 0x7f, 0xf5, 0x29, 0xfe, 0x18, 0xdd,
|
||||
0x7c, 0x81, 0xeb, 0x9d, 0xdd, 0xff, 0x50, 0x59, 0x42, 0xbe, 0x9f, 0xe3, 0xdf, 0x65, 0x8a, 0x1f,
|
||||
0x11, 0x5c, 0x73, 0x69, 0x7a, 0xdd, 0x19, 0x49, 0xdf, 0x00, 0x18, 0x2b, 0xec, 0x68, 0x3e, 0xeb,
|
||||
0x4a, 0xa2, 0x5a, 0xca, 0x8f, 0x4e, 0xe8, 0x87, 0xfd, 0xe2, 0x62, 0x4c, 0xf6, 0xf6, 0x90, 0xac,
|
||||
0x57, 0x38, 0xbb, 0xb9, 0x5d, 0xbc, 0xe4, 0x10, 0xef, 0xc5, 0xc2, 0x2c, 0x7e, 0x45, 0x90, 0x2e,
|
||||
0x37, 0xb2, 0xc3, 0x2d, 0x76, 0xa2, 0x95, 0x41, 0x04, 0x31, 0x7d, 0x0e, 0xb1, 0xaa, 0xc2, 0xdb,
|
||||
0x8b, 0x55, 0x45, 0x6f, 0x21, 0xb7, 0xaa, 0x95, 0xc6, 0x8a, 0xb6, 0x47, 0x3f, 0x84, 0x1f, 0x0e,
|
||||
0xe8, 0xfb, 0xfd, 0x78, 0xc9, 0x9f, 0x0f, 0x07, 0x1b, 0xfc, 0x6d, 0xb6, 0x4a, 0x58, 0xc1, 0x52,
|
||||
0xdf, 0xd4, 0xe1, 0x8b, 0xcd, 0xb6, 0xca, 0xf0, 0x07, 0xbd, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff,
|
||||
0x97, 0xf3, 0x59, 0x6e, 0xd1, 0x03, 0x00, 0x00,
|
||||
}
|
||||
|
@@ -1,23 +1,13 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: github.com/micro/go-micro/api/proto/api.proto
|
||||
|
||||
/*
|
||||
Package go_api is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
github.com/micro/go-micro/api/proto/api.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Pair
|
||||
Request
|
||||
Response
|
||||
Event
|
||||
*/
|
||||
package go_api
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -28,4 +18,4 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
type certmagicProvider struct {
|
||||
opts *acme.Options
|
||||
opts acme.Options
|
||||
}
|
||||
|
||||
func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
|
||||
@@ -40,23 +40,19 @@ func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, erro
|
||||
|
||||
// New returns a certmagic provider
|
||||
func New(options ...acme.Option) acme.Provider {
|
||||
o := &acme.Options{}
|
||||
if len(options) == 0 {
|
||||
for _, op := range acme.Default() {
|
||||
op(o)
|
||||
}
|
||||
} else {
|
||||
for _, op := range options {
|
||||
op(o)
|
||||
}
|
||||
opts := acme.DefaultOptions()
|
||||
|
||||
for _, o := range options {
|
||||
o(&opts)
|
||||
}
|
||||
if o.Cache != nil {
|
||||
if _, ok := o.Cache.(certmagic.Storage); !ok {
|
||||
|
||||
if opts.Cache != nil {
|
||||
if _, ok := opts.Cache.(certmagic.Storage); !ok {
|
||||
log.Fatal("ACME: cache provided doesn't implement certmagic's Storage interface")
|
||||
}
|
||||
}
|
||||
|
||||
return &certmagicProvider{
|
||||
opts: o,
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
@@ -11,8 +11,7 @@ import (
|
||||
"github.com/go-acme/lego/v3/providers/dns/cloudflare"
|
||||
"github.com/mholt/certmagic"
|
||||
"github.com/micro/go-micro/api/server/acme"
|
||||
"github.com/micro/go-micro/config/options"
|
||||
cloudflarestorage "github.com/micro/go-micro/store/cloudflare"
|
||||
cfstore "github.com/micro/go-micro/store/cloudflare"
|
||||
"github.com/micro/go-micro/sync/lock/memory"
|
||||
)
|
||||
|
||||
@@ -22,7 +21,7 @@ func TestCertMagic(t *testing.T) {
|
||||
}
|
||||
l, err := New().NewListener()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
l.Close()
|
||||
|
||||
@@ -34,7 +33,7 @@ func TestCertMagic(t *testing.T) {
|
||||
|
||||
p, err := cloudflare.NewDNSProviderConfig(c)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
l, err = New(acme.AcceptToS(true),
|
||||
@@ -43,7 +42,7 @@ func TestCertMagic(t *testing.T) {
|
||||
).NewListener()
|
||||
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
l.Close()
|
||||
}
|
||||
@@ -56,14 +55,11 @@ func TestStorageImplementation(t *testing.T) {
|
||||
}
|
||||
|
||||
var s certmagic.Storage
|
||||
st, err := cloudflarestorage.New(
|
||||
options.WithValue("CF_API_TOKEN", apiToken),
|
||||
options.WithValue("CF_ACCOUNT_ID", accountID),
|
||||
options.WithValue("KV_NAMESPACE_ID", kvID),
|
||||
st := cfstore.NewStore(
|
||||
cfstore.Token(apiToken),
|
||||
cfstore.Account(accountID),
|
||||
cfstore.Namespace(kvID),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't initialise cloudflare storage: %s\n", err.Error())
|
||||
}
|
||||
s = &storage{
|
||||
lock: memory.NewLock(),
|
||||
store: st,
|
||||
@@ -71,12 +67,12 @@ func TestStorageImplementation(t *testing.T) {
|
||||
|
||||
// Test Lock
|
||||
if err := s.Lock("test"); err != nil {
|
||||
t.Error(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test Unlock
|
||||
if err := s.Unlock("test"); err != nil {
|
||||
t.Error(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test data
|
||||
@@ -107,17 +103,17 @@ func TestStorageImplementation(t *testing.T) {
|
||||
// Test Store
|
||||
for _, d := range testdata {
|
||||
if err := s.Store(d.key, d.value); err != nil {
|
||||
t.Error(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Test Load
|
||||
for _, d := range testdata {
|
||||
if value, err := s.Load(d.key); err != nil {
|
||||
t.Error(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(value, d.value) {
|
||||
t.Errorf("Load %s: expected %v, got %v", d.key, d.value, value)
|
||||
t.Fatalf("Load %s: expected %v, got %v", d.key, d.value, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,13 +121,13 @@ func TestStorageImplementation(t *testing.T) {
|
||||
// Test Exists
|
||||
for _, d := range testdata {
|
||||
if !s.Exists(d.key) {
|
||||
t.Errorf("%s should exist, but doesn't\n", d.key)
|
||||
t.Fatalf("%s should exist, but doesn't\n", d.key)
|
||||
}
|
||||
}
|
||||
|
||||
// Test List
|
||||
if list, err := s.List("/", true); err != nil {
|
||||
t.Error(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
} else {
|
||||
var expected []string
|
||||
for i, d := range testdata {
|
||||
@@ -143,16 +139,16 @@ func TestStorageImplementation(t *testing.T) {
|
||||
sort.Strings(expected)
|
||||
sort.Strings(list)
|
||||
if !reflect.DeepEqual(expected, list) {
|
||||
t.Errorf("List: Expected %v, got %v\n", expected, list)
|
||||
t.Fatalf("List: Expected %v, got %v\n", expected, list)
|
||||
}
|
||||
}
|
||||
if list, err := s.List("/foo", false); err != nil {
|
||||
t.Error(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
} else {
|
||||
sort.Strings(list)
|
||||
expected := []string{"/foo/a", "/foo/b", "/foo/bar", "/foo/c", "/foo/d"}
|
||||
if !reflect.DeepEqual(expected, list) {
|
||||
t.Errorf("List: expected %s, got %s\n", expected, list)
|
||||
t.Fatalf("List: expected %s, got %s\n", expected, list)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,16 +156,16 @@ func TestStorageImplementation(t *testing.T) {
|
||||
for _, d := range testdata {
|
||||
info, err := s.Stat(d.key)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
} else {
|
||||
if info.Key != d.key {
|
||||
t.Errorf("Stat().Key: expected %s, got %s\n", d.key, info.Key)
|
||||
t.Fatalf("Stat().Key: expected %s, got %s\n", d.key, info.Key)
|
||||
}
|
||||
if info.Size != int64(len(d.value)) {
|
||||
t.Errorf("Stat().Size: expected %d, got %d\n", len(d.value), info.Size)
|
||||
t.Fatalf("Stat().Size: expected %d, got %d\n", len(d.value), info.Size)
|
||||
}
|
||||
if time.Since(info.Modified) > time.Minute {
|
||||
t.Errorf("Stat().Modified: expected time since last modified to be < 1 minute, got %v\n", time.Since(info.Modified))
|
||||
t.Fatalf("Stat().Modified: expected time since last modified to be < 1 minute, got %v\n", time.Since(info.Modified))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +174,7 @@ func TestStorageImplementation(t *testing.T) {
|
||||
// Test Delete
|
||||
for _, d := range testdata {
|
||||
if err := s.Delete(d.key); err != nil {
|
||||
t.Error(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,14 +192,11 @@ func TestE2e(t *testing.T) {
|
||||
}
|
||||
|
||||
testLock := memory.NewLock()
|
||||
testStore, err := cloudflarestorage.New(
|
||||
options.WithValue("CF_API_TOKEN", apiToken),
|
||||
options.WithValue("CF_ACCOUNT_ID", accountID),
|
||||
options.WithValue("KV_NAMESPACE_ID", kvID),
|
||||
testStore := cfstore.NewStore(
|
||||
cfstore.Token(apiToken),
|
||||
cfstore.Account(accountID),
|
||||
cfstore.Namespace(kvID),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
testStorage := NewStorage(testLock, testStore)
|
||||
|
||||
conf := cloudflare.NewDefaultConfig()
|
||||
|
@@ -89,7 +89,7 @@ func (s *storage) Exists(key string) bool {
|
||||
}
|
||||
|
||||
func (s *storage) List(prefix string, recursive bool) ([]string, error) {
|
||||
records, err := s.store.Sync()
|
||||
records, err := s.store.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -63,11 +63,11 @@ func Cache(c interface{}) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Default uses the Let's Encrypt Production CA, with DNS Challenge disabled.
|
||||
func Default() []Option {
|
||||
return []Option{
|
||||
AcceptToS(true),
|
||||
CA(LetsEncryptProductionCA),
|
||||
OnDemand(true),
|
||||
// DefaultOptions uses the Let's Encrypt Production CA, with DNS Challenge disabled.
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
AcceptToS: true,
|
||||
CA: LetsEncryptProductionCA,
|
||||
OnDemand: true,
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
var (
|
||||
// mock data
|
||||
testData = map[string][]*registry.Service{
|
||||
"foo": []*registry.Service{
|
||||
"foo": {
|
||||
{
|
||||
Name: "foo",
|
||||
Version: "1.0.0",
|
||||
|
@@ -126,7 +126,7 @@ func newHttpBroker(opts ...Option) Broker {
|
||||
}
|
||||
|
||||
h := &httpBroker{
|
||||
id: "broker-" + uuid.New().String(),
|
||||
id: "go.micro.http.broker-" + uuid.New().String(),
|
||||
address: addr,
|
||||
opts: options,
|
||||
r: reg,
|
||||
@@ -472,7 +472,7 @@ func (h *httpBroker) Init(opts ...Option) error {
|
||||
}
|
||||
|
||||
if len(h.id) == 0 {
|
||||
h.id = "broker-" + uuid.New().String()
|
||||
h.id = "go.micro.http.broker-" + uuid.New().String()
|
||||
}
|
||||
|
||||
// get registry
|
||||
@@ -648,9 +648,6 @@ func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeO
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create unique id
|
||||
id := h.id + "." + uuid.New().String()
|
||||
|
||||
var secure bool
|
||||
|
||||
if h.opts.Secure || h.opts.TLSConfig != nil {
|
||||
@@ -659,7 +656,7 @@ func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeO
|
||||
|
||||
// register service
|
||||
node := ®istry.Node{
|
||||
Id: id,
|
||||
Id: h.id,
|
||||
Address: mnet.HostPort(addr, port),
|
||||
Metadata: map[string]string{
|
||||
"secure": fmt.Sprintf("%t", secure),
|
||||
@@ -684,7 +681,7 @@ func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeO
|
||||
subscriber := &httpSubscriber{
|
||||
opts: options,
|
||||
hb: h,
|
||||
id: id,
|
||||
id: h.id,
|
||||
topic: topic,
|
||||
fn: handler,
|
||||
svc: service,
|
||||
|
@@ -7,15 +7,13 @@ import (
|
||||
|
||||
glog "github.com/go-log/log"
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/registry/memory"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
func newTestRegistry() *memory.Registry {
|
||||
r := memory.NewRegistry()
|
||||
m := r.(*memory.Registry)
|
||||
m.Services = testData
|
||||
return m
|
||||
func newTestRegistry() registry.Registry {
|
||||
return memory.NewRegistry(memory.Services(testData))
|
||||
}
|
||||
|
||||
func sub(be *testing.B, c int) {
|
||||
@@ -125,7 +123,7 @@ func pub(be *testing.B, c int) {
|
||||
|
||||
for i := 0; i < c; i++ {
|
||||
go func() {
|
||||
for _ = range ch {
|
||||
for range ch {
|
||||
if err := b.Publish(topic, msg); err != nil {
|
||||
be.Fatalf("Unexpected publish error: %v", err)
|
||||
}
|
||||
|
@@ -1,13 +1,11 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/micro/go-micro/broker/proto/broker.proto
|
||||
// source: micro/go-micro/broker/service/proto/broker.proto
|
||||
|
||||
package go_micro_broker
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
@@ -32,7 +30,7 @@ func (m *Empty) Reset() { *m = Empty{} }
|
||||
func (m *Empty) String() string { return proto.CompactTextString(m) }
|
||||
func (*Empty) ProtoMessage() {}
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_5edf81766900dd99, []int{0}
|
||||
return fileDescriptor_178fdc60944ff5e5, []int{0}
|
||||
}
|
||||
|
||||
func (m *Empty) XXX_Unmarshal(b []byte) error {
|
||||
@@ -65,7 +63,7 @@ func (m *PublishRequest) Reset() { *m = PublishRequest{} }
|
||||
func (m *PublishRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*PublishRequest) ProtoMessage() {}
|
||||
func (*PublishRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_5edf81766900dd99, []int{1}
|
||||
return fileDescriptor_178fdc60944ff5e5, []int{1}
|
||||
}
|
||||
|
||||
func (m *PublishRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -112,7 +110,7 @@ func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} }
|
||||
func (m *SubscribeRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SubscribeRequest) ProtoMessage() {}
|
||||
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_5edf81766900dd99, []int{2}
|
||||
return fileDescriptor_178fdc60944ff5e5, []int{2}
|
||||
}
|
||||
|
||||
func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -159,7 +157,7 @@ func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_5edf81766900dd99, []int{3}
|
||||
return fileDescriptor_178fdc60944ff5e5, []int{3}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
@@ -203,162 +201,29 @@ func init() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/micro/go-micro/broker/proto/broker.proto", fileDescriptor_5edf81766900dd99)
|
||||
proto.RegisterFile("micro/go-micro/broker/service/proto/broker.proto", fileDescriptor_178fdc60944ff5e5)
|
||||
}
|
||||
|
||||
var fileDescriptor_5edf81766900dd99 = []byte{
|
||||
// 309 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xcf, 0x4a, 0xf3, 0x40,
|
||||
0x14, 0xc5, 0x3b, 0xed, 0xd7, 0x86, 0xde, 0x7e, 0x68, 0x19, 0x8a, 0x84, 0x6e, 0x8c, 0xc1, 0x45,
|
||||
0x36, 0x4e, 0x24, 0xdd, 0xa8, 0x88, 0x0b, 0xb1, 0xe0, 0x42, 0x41, 0xc6, 0x9d, 0xbb, 0x4c, 0x3a,
|
||||
0x24, 0xa1, 0x8d, 0x93, 0x4e, 0x66, 0x84, 0xbc, 0x88, 0x2b, 0x1f, 0x56, 0x3a, 0x93, 0xfa, 0xa7,
|
||||
0xa1, 0xee, 0xee, 0x49, 0x7e, 0x73, 0xee, 0xe1, 0x5c, 0x98, 0xa5, 0xb9, 0xca, 0x34, 0x23, 0x89,
|
||||
0x28, 0xc2, 0x22, 0x4f, 0xa4, 0x08, 0x53, 0x71, 0x66, 0x07, 0x26, 0xc5, 0x92, 0xcb, 0xb0, 0x94,
|
||||
0x42, 0x6d, 0x05, 0x31, 0x02, 0x1f, 0xa6, 0x82, 0x18, 0x86, 0xd8, 0xcf, 0xbe, 0x03, 0xfd, 0x79,
|
||||
0x51, 0xaa, 0xda, 0x7f, 0x81, 0x83, 0x27, 0xcd, 0x56, 0x79, 0x95, 0x51, 0xbe, 0xd6, 0xbc, 0x52,
|
||||
0x78, 0x02, 0x7d, 0x25, 0xca, 0x3c, 0x71, 0x91, 0x87, 0x82, 0x21, 0xb5, 0x02, 0x47, 0xe0, 0x14,
|
||||
0xbc, 0xaa, 0xe2, 0x94, 0xbb, 0x5d, 0x0f, 0x05, 0xa3, 0xc8, 0x25, 0x3b, 0x9e, 0xe4, 0xd1, 0xfe,
|
||||
0xa7, 0x5b, 0xd0, 0xbf, 0x81, 0xf1, 0xb3, 0x66, 0x55, 0x22, 0x73, 0xc6, 0xff, 0x76, 0x9f, 0x40,
|
||||
0x7f, 0xad, 0xb9, 0xb6, 0xde, 0x43, 0x6a, 0x85, 0xff, 0x8e, 0xc0, 0x69, 0x4c, 0xf1, 0x35, 0x0c,
|
||||
0x32, 0x1e, 0x2f, 0xb8, 0x74, 0x91, 0xd7, 0x0b, 0x46, 0xd1, 0xe9, 0xbe, 0xf5, 0xe4, 0xde, 0x60,
|
||||
0xf3, 0x57, 0x25, 0x6b, 0xda, 0xbc, 0xc1, 0x18, 0xfe, 0x31, 0xb1, 0xa8, 0x8d, 0xfd, 0x7f, 0x6a,
|
||||
0xe6, 0xe9, 0x25, 0x8c, 0x7e, 0xa0, 0x78, 0x0c, 0xbd, 0x25, 0xaf, 0x9b, 0x58, 0x9b, 0x71, 0x13,
|
||||
0xea, 0x2d, 0x5e, 0x7d, 0x87, 0x32, 0xe2, 0xaa, 0x7b, 0x81, 0xa2, 0x0f, 0x04, 0x83, 0x5b, 0xb3,
|
||||
0x15, 0xdf, 0x81, 0xd3, 0xf4, 0x87, 0x8f, 0x5b, 0x91, 0x7e, 0x37, 0x3b, 0x3d, 0x6a, 0x01, 0xf6,
|
||||
0x06, 0x1d, 0xfc, 0x00, 0xc3, 0xaf, 0xa6, 0xf0, 0x49, 0x0b, 0xdb, 0x6d, 0x71, 0xba, 0xb7, 0x7c,
|
||||
0xbf, 0x73, 0x8e, 0xd8, 0xc0, 0x1c, 0x7d, 0xf6, 0x19, 0x00, 0x00, 0xff, 0xff, 0x25, 0x38, 0xfa,
|
||||
0x02, 0x2b, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// BrokerClient is the client API for Broker service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type BrokerClient interface {
|
||||
Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*Empty, error)
|
||||
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Broker_SubscribeClient, error)
|
||||
}
|
||||
|
||||
type brokerClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewBrokerClient(cc *grpc.ClientConn) BrokerClient {
|
||||
return &brokerClient{cc}
|
||||
}
|
||||
|
||||
func (c *brokerClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.broker.Broker/Publish", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *brokerClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Broker_SubscribeClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_Broker_serviceDesc.Streams[0], "/go.micro.broker.Broker/Subscribe", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &brokerSubscribeClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Broker_SubscribeClient interface {
|
||||
Recv() (*Message, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type brokerSubscribeClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeClient) Recv() (*Message, error) {
|
||||
m := new(Message)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// BrokerServer is the server API for Broker service.
|
||||
type BrokerServer interface {
|
||||
Publish(context.Context, *PublishRequest) (*Empty, error)
|
||||
Subscribe(*SubscribeRequest, Broker_SubscribeServer) error
|
||||
}
|
||||
|
||||
func RegisterBrokerServer(s *grpc.Server, srv BrokerServer) {
|
||||
s.RegisterService(&_Broker_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Broker_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PublishRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BrokerServer).Publish(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.broker.Broker/Publish",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BrokerServer).Publish(ctx, req.(*PublishRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Broker_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SubscribeRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(BrokerServer).Subscribe(m, &brokerSubscribeServer{stream})
|
||||
}
|
||||
|
||||
type Broker_SubscribeServer interface {
|
||||
Send(*Message) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type brokerSubscribeServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *brokerSubscribeServer) Send(m *Message) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
var _Broker_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.broker.Broker",
|
||||
HandlerType: (*BrokerServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Publish",
|
||||
Handler: _Broker_Publish_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Subscribe",
|
||||
Handler: _Broker_Subscribe_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "github.com/micro/go-micro/broker/proto/broker.proto",
|
||||
var fileDescriptor_178fdc60944ff5e5 = []byte{
|
||||
// 305 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0x4d, 0x4f, 0xc2, 0x40,
|
||||
0x14, 0x64, 0x41, 0x68, 0x78, 0x18, 0x25, 0x1b, 0x62, 0x1a, 0x2e, 0x62, 0xe3, 0x81, 0x8b, 0x5b,
|
||||
0x52, 0x2f, 0x6a, 0x8c, 0x07, 0x23, 0x89, 0x07, 0x4d, 0xcc, 0x7a, 0xf3, 0xd6, 0x2d, 0x2f, 0xa5,
|
||||
0x81, 0xba, 0x65, 0xb7, 0x25, 0xe9, 0x1f, 0xf1, 0xe4, 0x8f, 0x35, 0xec, 0x16, 0x3f, 0x68, 0xf0,
|
||||
0x36, 0xf3, 0x76, 0x76, 0xde, 0x64, 0x1e, 0x4c, 0xd2, 0x24, 0x52, 0xd2, 0x8f, 0xe5, 0x85, 0x05,
|
||||
0x42, 0xc9, 0x05, 0x2a, 0x5f, 0xa3, 0x5a, 0x27, 0x11, 0xfa, 0x99, 0x92, 0xf9, 0x76, 0xc8, 0x0c,
|
||||
0xa1, 0xc7, 0xb1, 0x64, 0x46, 0xcb, 0xec, 0xd8, 0x73, 0xa0, 0x3d, 0x4d, 0xb3, 0xbc, 0xf4, 0xde,
|
||||
0xe0, 0xe8, 0xa5, 0x10, 0xcb, 0x44, 0xcf, 0x39, 0xae, 0x0a, 0xd4, 0x39, 0x1d, 0x40, 0x3b, 0x97,
|
||||
0x59, 0x12, 0xb9, 0x64, 0x44, 0xc6, 0x5d, 0x6e, 0x09, 0x0d, 0xc0, 0x49, 0x51, 0xeb, 0x30, 0x46,
|
||||
0xb7, 0x39, 0x22, 0xe3, 0x5e, 0xe0, 0xb2, 0x1d, 0x4f, 0xf6, 0x6c, 0xdf, 0xf9, 0x56, 0xe8, 0xdd,
|
||||
0x41, 0xff, 0xb5, 0x10, 0x3a, 0x52, 0x89, 0xc0, 0xff, 0xdd, 0x07, 0xd0, 0x5e, 0x15, 0x58, 0x58,
|
||||
0xef, 0x2e, 0xb7, 0xc4, 0xfb, 0x20, 0xe0, 0x54, 0xa6, 0xf4, 0x16, 0x3a, 0x73, 0x0c, 0x67, 0xa8,
|
||||
0x5c, 0x32, 0x6a, 0x8d, 0x7b, 0xc1, 0xf9, 0xbe, 0xf5, 0xec, 0xd1, 0xc8, 0xa6, 0xef, 0xb9, 0x2a,
|
||||
0x79, 0xf5, 0x87, 0x52, 0x38, 0x10, 0x72, 0x56, 0x1a, 0xfb, 0x43, 0x6e, 0xf0, 0xf0, 0x1a, 0x7a,
|
||||
0xbf, 0xa4, 0xb4, 0x0f, 0xad, 0x05, 0x96, 0x55, 0xac, 0x0d, 0xdc, 0x84, 0x5a, 0x87, 0xcb, 0x9f,
|
||||
0x50, 0x86, 0xdc, 0x34, 0xaf, 0x48, 0xf0, 0x49, 0xa0, 0x73, 0x6f, 0xb6, 0xd2, 0x07, 0x70, 0xaa,
|
||||
0xfe, 0xe8, 0x69, 0x2d, 0xd2, 0xdf, 0x66, 0x87, 0x27, 0x35, 0x81, 0xbd, 0x41, 0x83, 0x3e, 0x41,
|
||||
0xf7, 0xbb, 0x29, 0x7a, 0x56, 0x93, 0xed, 0xb6, 0x38, 0xdc, 0x5b, 0xbe, 0xd7, 0x98, 0x10, 0xd1,
|
||||
0x31, 0x47, 0xbf, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x60, 0x8c, 0x40, 0xd5, 0x28, 0x02, 0x00,
|
||||
0x00,
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: github.com/micro/go-micro/broker/proto/broker.proto
|
||||
// source: micro/go-micro/broker/service/proto/broker.proto
|
||||
|
||||
package go_micro_broker
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
var (
|
||||
// mock data
|
||||
testData = map[string][]*registry.Service{
|
||||
"foo": []*registry.Service{
|
||||
"foo": {
|
||||
{
|
||||
Name: "foo",
|
||||
Version: "1.0.0",
|
||||
@@ -15,10 +15,16 @@ var (
|
||||
{
|
||||
Id: "foo-1.0.0-123",
|
||||
Address: "localhost:9999",
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "foo-1.0.0-321",
|
||||
Address: "localhost:9999",
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -29,6 +35,9 @@ var (
|
||||
{
|
||||
Id: "foo-1.0.1-321",
|
||||
Address: "localhost:6666",
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -39,6 +48,9 @@ var (
|
||||
{
|
||||
Id: "foo-1.0.3-345",
|
||||
Address: "localhost:8888",
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/client/selector"
|
||||
"github.com/micro/go-micro/codec"
|
||||
raw "github.com/micro/go-micro/codec/bytes"
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/metadata"
|
||||
"github.com/micro/go-micro/registry"
|
||||
@@ -70,8 +71,13 @@ func (g *grpcClient) next(request client.Request, opts client.CallOptions) (sele
|
||||
}, nil
|
||||
}
|
||||
|
||||
// only get the things that are of grpc protocol
|
||||
selectOptions := append(opts.SelectOptions, selector.WithFilter(
|
||||
selector.FilterLabel("protocol", "grpc"),
|
||||
))
|
||||
|
||||
// get next nodes from the selector
|
||||
next, err := g.opts.Selector.Select(service, opts.SelectOptions...)
|
||||
next, err := g.opts.Selector.Select(service, selectOptions...)
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
@@ -510,29 +516,56 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
|
||||
}
|
||||
|
||||
func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
||||
var options client.PublishOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
if !ok {
|
||||
md = make(map[string]string)
|
||||
}
|
||||
md["Content-Type"] = p.ContentType()
|
||||
md["Micro-Topic"] = p.Topic()
|
||||
|
||||
cf, err := g.newGRPCCodec(p.ContentType())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
b, err := cf.Marshal(p.Payload())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
var body []byte
|
||||
|
||||
// passed in raw data
|
||||
if d, ok := p.Payload().(*raw.Frame); ok {
|
||||
body = d.Data
|
||||
} else {
|
||||
// set the body
|
||||
b, err := cf.Marshal(p.Payload())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
body = b
|
||||
}
|
||||
|
||||
g.once.Do(func() {
|
||||
g.opts.Broker.Connect()
|
||||
})
|
||||
|
||||
return g.opts.Broker.Publish(p.Topic(), &broker.Message{
|
||||
topic := p.Topic()
|
||||
|
||||
// get proxy topic
|
||||
if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
|
||||
options.Exchange = prx
|
||||
}
|
||||
|
||||
// get the exchange
|
||||
if len(options.Exchange) > 0 {
|
||||
topic = options.Exchange
|
||||
}
|
||||
|
||||
return g.opts.Broker.Publish(topic, &broker.Message{
|
||||
Header: md,
|
||||
Body: b,
|
||||
Body: body,
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -42,9 +42,12 @@ func TestGRPCClient(t *testing.T) {
|
||||
Name: "helloworld",
|
||||
Version: "test",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-1",
|
||||
Address: l.Addr().String(),
|
||||
Metadata: map[string]string{
|
||||
"protocol": "grpc",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@@ -43,14 +43,12 @@ func (g *grpcStream) Send(msg interface{}) error {
|
||||
func (g *grpcStream) Recv(msg interface{}) (err error) {
|
||||
defer g.setError(err)
|
||||
if err = g.stream.RecvMsg(msg); err != nil {
|
||||
if err == io.EOF {
|
||||
// #202 - inconsistent gRPC stream behavior
|
||||
// the only way to tell if the stream is done is when we get a EOF on the Recv
|
||||
// here we should close the underlying gRPC ClientConn
|
||||
closeErr := g.conn.Close()
|
||||
if closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
// #202 - inconsistent gRPC stream behavior
|
||||
// the only way to tell if the stream is done is when we get a EOF on the Recv
|
||||
// here we should close the underlying gRPC ClientConn
|
||||
closeErr := g.conn.Close()
|
||||
if err == io.EOF && closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@@ -1,13 +1,11 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: micro/go-micro/client/proto/client.proto
|
||||
// source: github.com/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"
|
||||
)
|
||||
|
||||
@@ -36,7 +34,7 @@ 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}
|
||||
return fileDescriptor_d418333f021a3308, []int{0}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
@@ -96,7 +94,7 @@ 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}
|
||||
return fileDescriptor_d418333f021a3308, []int{1}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
@@ -137,7 +135,7 @@ 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}
|
||||
return fileDescriptor_d418333f021a3308, []int{2}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
@@ -186,203 +184,27 @@ func init() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("micro/go-micro/client/proto/client.proto", fileDescriptor_7d733ae29171347b)
|
||||
proto.RegisterFile("github.com/micro/go-micro/client/proto/client.proto", fileDescriptor_d418333f021a3308)
|
||||
}
|
||||
|
||||
var fileDescriptor_7d733ae29171347b = []byte{
|
||||
// 270 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x41, 0x4b, 0xc3, 0x40,
|
||||
0x10, 0x85, 0xbb, 0x6d, 0x4c, 0xea, 0x58, 0x10, 0x06, 0x0f, 0x6b, 0x0e, 0x52, 0x73, 0xca, 0xc5,
|
||||
0x54, 0xf4, 0x2c, 0x1e, 0x72, 0x16, 0x24, 0x8a, 0x57, 0x49, 0xb6, 0x43, 0x5c, 0x48, 0x77, 0xd7,
|
||||
0xec, 0xb6, 0x90, 0x1f, 0xe9, 0x7f, 0x12, 0x36, 0xa9, 0x15, 0x6d, 0x2f, 0xbd, 0xcd, 0x9b, 0x6f,
|
||||
0x79, 0x33, 0xfb, 0x06, 0xd2, 0x95, 0x14, 0xad, 0x5e, 0xd4, 0xfa, 0xa6, 0x2f, 0x44, 0x23, 0x49,
|
||||
0xb9, 0x85, 0x69, 0xb5, 0xdb, 0x8a, 0xcc, 0x0b, 0x3c, 0xaf, 0x75, 0xe6, 0xdf, 0x64, 0x7d, 0x3b,
|
||||
0xd9, 0x40, 0x54, 0xd0, 0xe7, 0x9a, 0xac, 0x43, 0x0e, 0x91, 0xa5, 0x76, 0x23, 0x05, 0x71, 0x36,
|
||||
0x67, 0xe9, 0x69, 0xb1, 0x95, 0x18, 0xc3, 0x94, 0xd4, 0xd2, 0x68, 0xa9, 0x1c, 0x1f, 0x7b, 0xf4,
|
||||
0xa3, 0xf1, 0x1a, 0x66, 0x42, 0x2b, 0x47, 0xca, 0xbd, 0xbb, 0xce, 0x10, 0x9f, 0x78, 0x7e, 0x36,
|
||||
0xf4, 0x5e, 0x3b, 0x43, 0x88, 0x10, 0x54, 0x7a, 0xd9, 0xf1, 0x60, 0xce, 0xd2, 0x59, 0xe1, 0xeb,
|
||||
0xe4, 0x0a, 0xa6, 0x05, 0x59, 0xa3, 0x95, 0xdd, 0x71, 0xf6, 0x8b, 0xbf, 0x41, 0xf4, 0x44, 0xd6,
|
||||
0x96, 0x35, 0xe1, 0x05, 0x9c, 0x38, 0x6d, 0xa4, 0x18, 0xb6, 0xea, 0xc5, 0xbf, 0xb9, 0xe3, 0xc3,
|
||||
0x73, 0x27, 0x3b, 0xdf, 0xbb, 0x2f, 0x06, 0x61, 0xee, 0xbf, 0x8e, 0x0f, 0x10, 0xe4, 0x65, 0xd3,
|
||||
0x20, 0xcf, 0xfe, 0x84, 0x92, 0x0d, 0x89, 0xc4, 0x97, 0x7b, 0x48, 0xbf, 0x73, 0x32, 0xc2, 0x1c,
|
||||
0xc2, 0x17, 0xd7, 0x52, 0xb9, 0x3a, 0xd2, 0x20, 0x65, 0xb7, 0x0c, 0x1f, 0x21, 0x7a, 0x5e, 0x57,
|
||||
0x8d, 0xb4, 0x1f, 0x7b, 0x5c, 0x86, 0x00, 0xe2, 0x83, 0x24, 0x19, 0x55, 0xa1, 0xbf, 0xeb, 0xfd,
|
||||
0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x76, 0x1f, 0x51, 0x03, 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
|
||||
|
||||
// ClientClient is the client API for Client service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type ClientClient 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) (Client_StreamClient, error)
|
||||
// Publish publishes a message and returns an empty Message
|
||||
Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error)
|
||||
}
|
||||
|
||||
type clientClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewClientClient(cc *grpc.ClientConn) ClientClient {
|
||||
return &clientClient{cc}
|
||||
}
|
||||
|
||||
func (c *clientClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.client.Client/Call", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clientClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_Client_serviceDesc.Streams[0], "/go.micro.client.Client/Stream", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &clientStreamClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Client_StreamClient interface {
|
||||
Send(*Request) error
|
||||
Recv() (*Response, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type clientStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *clientStreamClient) Send(m *Request) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *clientStreamClient) Recv() (*Response, error) {
|
||||
m := new(Response)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *clientClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
|
||||
out := new(Message)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.client.Client/Publish", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ClientServer is the server API for Client service.
|
||||
type ClientServer interface {
|
||||
// Call allows a single request to be made
|
||||
Call(context.Context, *Request) (*Response, error)
|
||||
// Stream is a bidirectional stream
|
||||
Stream(Client_StreamServer) error
|
||||
// Publish publishes a message and returns an empty Message
|
||||
Publish(context.Context, *Message) (*Message, error)
|
||||
}
|
||||
|
||||
func RegisterClientServer(s *grpc.Server, srv ClientServer) {
|
||||
s.RegisterService(&_Client_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Client_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.(ClientServer).Call(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.client.Client/Call",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ClientServer).Call(ctx, req.(*Request))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Client_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(ClientServer).Stream(&clientStreamServer{stream})
|
||||
}
|
||||
|
||||
type Client_StreamServer interface {
|
||||
Send(*Response) error
|
||||
Recv() (*Request, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type clientStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *clientStreamServer) Send(m *Response) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *clientStreamServer) Recv() (*Request, error) {
|
||||
m := new(Request)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func _Client_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.(ClientServer).Publish(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.client.Client/Publish",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ClientServer).Publish(ctx, req.(*Message))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Client_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.client.Client",
|
||||
HandlerType: (*ClientServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Call",
|
||||
Handler: _Client_Call_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Publish",
|
||||
Handler: _Client_Publish_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Stream",
|
||||
Handler: _Client_Stream_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "micro/go-micro/client/proto/client.proto",
|
||||
var fileDescriptor_d418333f021a3308 = []byte{
|
||||
// 279 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x31, 0x4f, 0xfb, 0x30,
|
||||
0x10, 0xc5, 0xeb, 0xb6, 0xff, 0xa4, 0xff, 0xa3, 0x12, 0x92, 0xc5, 0x60, 0x32, 0xa0, 0x92, 0x29,
|
||||
0x0b, 0x0e, 0xa2, 0x33, 0x62, 0xc8, 0x8c, 0x84, 0x02, 0x62, 0x45, 0x89, 0x7b, 0x4a, 0x2d, 0x25,
|
||||
0xb6, 0x89, 0x9d, 0x4a, 0xf9, 0x90, 0x7c, 0x27, 0x24, 0x27, 0xa5, 0x08, 0xda, 0x85, 0xed, 0xde,
|
||||
0xfd, 0xac, 0x77, 0xe7, 0x77, 0xb0, 0xae, 0xa4, 0xdb, 0x76, 0x25, 0x17, 0xba, 0x49, 0x1b, 0x29,
|
||||
0x5a, 0x9d, 0x56, 0xfa, 0x66, 0x28, 0x44, 0x2d, 0x51, 0xb9, 0xd4, 0xb4, 0xda, 0xed, 0x05, 0xf7,
|
||||
0x82, 0x9e, 0x57, 0x9a, 0xfb, 0x37, 0x7c, 0x68, 0xc7, 0x3b, 0x08, 0x73, 0x7c, 0xef, 0xd0, 0x3a,
|
||||
0xca, 0x20, 0xb4, 0xd8, 0xee, 0xa4, 0x40, 0x46, 0x56, 0x24, 0xf9, 0x9f, 0xef, 0x25, 0x8d, 0x60,
|
||||
0x81, 0x6a, 0x63, 0xb4, 0x54, 0x8e, 0x4d, 0x3d, 0xfa, 0xd2, 0xf4, 0x1a, 0x96, 0x42, 0x2b, 0x87,
|
||||
0xca, 0xbd, 0xb9, 0xde, 0x20, 0x9b, 0x79, 0x7e, 0x36, 0xf6, 0x5e, 0x7a, 0x83, 0x94, 0xc2, 0xbc,
|
||||
0xd4, 0x9b, 0x9e, 0xcd, 0x57, 0x24, 0x59, 0xe6, 0xbe, 0x8e, 0xaf, 0x60, 0x91, 0xa3, 0x35, 0x5a,
|
||||
0xd9, 0x03, 0x27, 0xdf, 0xf8, 0x2b, 0x84, 0x8f, 0x68, 0x6d, 0x51, 0x21, 0xbd, 0x80, 0x7f, 0x4e,
|
||||
0x1b, 0x29, 0xc6, 0xad, 0x06, 0xf1, 0x6b, 0xee, 0xf4, 0xf4, 0xdc, 0xd9, 0xc1, 0xf7, 0xee, 0x83,
|
||||
0x40, 0x90, 0xf9, 0xaf, 0xd3, 0x7b, 0x98, 0x67, 0x45, 0x5d, 0x53, 0xc6, 0x7f, 0x84, 0xc2, 0xc7,
|
||||
0x44, 0xa2, 0xcb, 0x23, 0x64, 0xd8, 0x39, 0x9e, 0xd0, 0x0c, 0x82, 0x67, 0xd7, 0x62, 0xd1, 0xfc,
|
||||
0xd1, 0x20, 0x21, 0xb7, 0x84, 0x3e, 0x40, 0xf8, 0xd4, 0x95, 0xb5, 0xb4, 0xdb, 0x23, 0x2e, 0x63,
|
||||
0x00, 0xd1, 0x49, 0x12, 0x4f, 0xca, 0xc0, 0xdf, 0x75, 0xfd, 0x19, 0x00, 0x00, 0xff, 0xff, 0xb6,
|
||||
0x4d, 0x6e, 0xd5, 0x0e, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: micro/go-micro/client/proto/client.proto
|
||||
// source: github.com/micro/go-micro/client/proto/client.proto
|
||||
|
||||
package go_micro_client
|
||||
|
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/micro/go-micro/client/pool"
|
||||
"github.com/micro/go-micro/client/selector"
|
||||
"github.com/micro/go-micro/codec"
|
||||
raw "github.com/micro/go-micro/codec/bytes"
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/metadata"
|
||||
"github.com/micro/go-micro/registry"
|
||||
@@ -349,8 +350,13 @@ func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, erro
|
||||
}, nil
|
||||
}
|
||||
|
||||
// only get the things that are of mucp protocol
|
||||
selectOptions := append(opts.SelectOptions, selector.WithFilter(
|
||||
selector.FilterLabel("protocol", "mucp"),
|
||||
))
|
||||
|
||||
// get next nodes from the selector
|
||||
next, err := r.opts.Selector.Select(service, opts.SelectOptions...)
|
||||
next, err := r.opts.Selector.Select(service, selectOptions...)
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
@@ -583,26 +589,37 @@ func (r *rpcClient) Publish(ctx context.Context, msg Message, opts ...PublishOpt
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// new buffer
|
||||
b := buf.New(nil)
|
||||
var body []byte
|
||||
|
||||
if err := cf(b).Write(&codec.Message{
|
||||
Target: topic,
|
||||
Type: codec.Event,
|
||||
Header: map[string]string{
|
||||
"Micro-Id": id,
|
||||
"Micro-Topic": msg.Topic(),
|
||||
},
|
||||
}, msg.Payload()); err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
// passed in raw data
|
||||
if d, ok := msg.Payload().(*raw.Frame); ok {
|
||||
body = d.Data
|
||||
} else {
|
||||
// new buffer
|
||||
b := buf.New(nil)
|
||||
|
||||
if err := cf(b).Write(&codec.Message{
|
||||
Target: topic,
|
||||
Type: codec.Event,
|
||||
Header: map[string]string{
|
||||
"Micro-Id": id,
|
||||
"Micro-Topic": msg.Topic(),
|
||||
},
|
||||
}, msg.Payload()); err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// set the body
|
||||
body = b.Bytes()
|
||||
}
|
||||
|
||||
r.once.Do(func() {
|
||||
r.opts.Broker.Connect()
|
||||
})
|
||||
|
||||
return r.opts.Broker.Publish(topic, &broker.Message{
|
||||
Header: md,
|
||||
Body: b.Bytes(),
|
||||
Body: body,
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -12,10 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func newTestRegistry() registry.Registry {
|
||||
r := memory.NewRegistry()
|
||||
reg := r.(*memory.Registry)
|
||||
reg.Services = testData
|
||||
return reg
|
||||
return memory.NewRegistry(memory.Services(testData))
|
||||
}
|
||||
|
||||
func TestCallAddress(t *testing.T) {
|
||||
@@ -143,9 +140,12 @@ func TestCallWrapper(t *testing.T) {
|
||||
Name: service,
|
||||
Version: "latest",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: id,
|
||||
Address: address,
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@@ -145,6 +145,11 @@ func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec {
|
||||
return nil
|
||||
}
|
||||
|
||||
// processing topic publishing
|
||||
if len(msg.Header["Micro-Topic"]) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// no protocol use old codecs
|
||||
switch msg.Header["Content-Type"] {
|
||||
case "application/json":
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
var (
|
||||
// mock data
|
||||
testData = map[string][]*registry.Service{
|
||||
"foo": []*registry.Service{
|
||||
"foo": {
|
||||
{
|
||||
Name: "foo",
|
||||
Version: "1.0.0",
|
||||
|
@@ -9,9 +9,7 @@ import (
|
||||
func TestRegistrySelector(t *testing.T) {
|
||||
counts := map[string]int{}
|
||||
|
||||
r := memory.NewRegistry()
|
||||
rg := r.(*memory.Registry)
|
||||
rg.Services = testData
|
||||
r := memory.NewRegistry(memory.Services(testData))
|
||||
cache := NewSelector(Registry(r))
|
||||
|
||||
next, err := cache.Select("foo")
|
||||
|
@@ -72,7 +72,7 @@ func (d *dnsSelector) Select(service string, opts ...selector.SelectOption) (sel
|
||||
}
|
||||
|
||||
services := []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: service,
|
||||
Nodes: nodes,
|
||||
},
|
||||
|
@@ -14,20 +14,20 @@ func TestFilterEndpoint(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Endpoints: []*registry.Endpoint{
|
||||
®istry.Endpoint{
|
||||
{
|
||||
Name: "Foo.Bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
Endpoints: []*registry.Endpoint{
|
||||
®istry.Endpoint{
|
||||
{
|
||||
Name: "Baz.Bar",
|
||||
},
|
||||
},
|
||||
@@ -38,20 +38,20 @@ func TestFilterEndpoint(t *testing.T) {
|
||||
},
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Endpoints: []*registry.Endpoint{
|
||||
®istry.Endpoint{
|
||||
{
|
||||
Name: "Foo.Bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
Endpoints: []*registry.Endpoint{
|
||||
®istry.Endpoint{
|
||||
{
|
||||
Name: "Foo.Bar",
|
||||
},
|
||||
},
|
||||
@@ -95,11 +95,11 @@ func TestFilterLabel(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-1",
|
||||
Address: "localhost",
|
||||
Metadata: map[string]string{
|
||||
@@ -108,11 +108,11 @@ func TestFilterLabel(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-2",
|
||||
Address: "localhost",
|
||||
Metadata: map[string]string{
|
||||
@@ -127,21 +127,21 @@ func TestFilterLabel(t *testing.T) {
|
||||
},
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-1",
|
||||
Address: "localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test-2",
|
||||
Address: "localhost",
|
||||
},
|
||||
@@ -187,11 +187,11 @@ func TestFilterVersion(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
},
|
||||
@@ -201,11 +201,11 @@ func TestFilterVersion(t *testing.T) {
|
||||
},
|
||||
{
|
||||
services: []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "1.1.0",
|
||||
},
|
||||
|
@@ -101,7 +101,7 @@ func (r *routerSelector) getRoutes(service string) ([]router.Route, error) {
|
||||
return nil, selector.ErrNoneAvailable
|
||||
}
|
||||
|
||||
var routes []router.Route
|
||||
routes := make([]router.Route, 0, len(pbRoutes.Routes))
|
||||
|
||||
// convert from pb to []*router.Route
|
||||
for _, r := range pbRoutes.Routes {
|
||||
@@ -111,7 +111,7 @@ func (r *routerSelector) getRoutes(service string) ([]router.Route, error) {
|
||||
Gateway: r.Gateway,
|
||||
Network: r.Network,
|
||||
Link: r.Link,
|
||||
Metric: int(r.Metric),
|
||||
Metric: r.Metric,
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,7 @@ func init() {
|
||||
|
||||
// Random is a random strategy algorithm for node selection
|
||||
func Random(services []*registry.Service) Next {
|
||||
var nodes []*registry.Node
|
||||
nodes := make([]*registry.Node, 0, len(services))
|
||||
|
||||
for _, service := range services {
|
||||
nodes = append(nodes, service.Nodes...)
|
||||
|
@@ -8,29 +8,29 @@ import (
|
||||
|
||||
func TestStrategies(t *testing.T) {
|
||||
testData := []*registry.Service{
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test1",
|
||||
Version: "latest",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test1-1",
|
||||
Address: "10.0.0.1:1001",
|
||||
},
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test1-2",
|
||||
Address: "10.0.0.2:1002",
|
||||
},
|
||||
},
|
||||
},
|
||||
®istry.Service{
|
||||
{
|
||||
Name: "test1",
|
||||
Version: "default",
|
||||
Nodes: []*registry.Node{
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test1-3",
|
||||
Address: "10.0.0.3:1003",
|
||||
},
|
||||
®istry.Node{
|
||||
{
|
||||
Id: "test1-4",
|
||||
Address: "10.0.0.4:1004",
|
||||
},
|
||||
|
@@ -27,7 +27,6 @@ import (
|
||||
|
||||
// registries
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/registry/consul"
|
||||
"github.com/micro/go-micro/registry/etcd"
|
||||
"github.com/micro/go-micro/registry/mdns"
|
||||
rmem "github.com/micro/go-micro/registry/memory"
|
||||
@@ -45,6 +44,10 @@ import (
|
||||
thttp "github.com/micro/go-micro/transport/http"
|
||||
tmem "github.com/micro/go-micro/transport/memory"
|
||||
"github.com/micro/go-micro/transport/quic"
|
||||
|
||||
// runtimes
|
||||
"github.com/micro/go-micro/runtime"
|
||||
"github.com/micro/go-micro/runtime/kubernetes"
|
||||
)
|
||||
|
||||
type Cmd interface {
|
||||
@@ -152,16 +155,27 @@ var (
|
||||
EnvVar: "MICRO_BROKER_ADDRESS",
|
||||
Usage: "Comma-separated list of broker addresses",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "profile",
|
||||
Usage: "Debug profiler for cpu and memory stats",
|
||||
EnvVar: "MICRO_DEBUG_PROFILE",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "registry",
|
||||
EnvVar: "MICRO_REGISTRY",
|
||||
Usage: "Registry for discovery. consul, etcd, mdns",
|
||||
Usage: "Registry for discovery. etcd, mdns",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "registry_address",
|
||||
EnvVar: "MICRO_REGISTRY_ADDRESS",
|
||||
Usage: "Comma-separated list of registry addresses",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "runtime",
|
||||
Usage: "Runtime for building and running services e.g local, kubernetes",
|
||||
EnvVar: "MICRO_RUNTIME",
|
||||
Value: "local",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "selector",
|
||||
EnvVar: "MICRO_SELECTOR",
|
||||
@@ -196,7 +210,6 @@ var (
|
||||
DefaultRegistries = map[string]func(...registry.Option) registry.Registry{
|
||||
"go.micro.registry": regSrv.NewRegistry,
|
||||
"service": regSrv.NewRegistry,
|
||||
"consul": consul.NewRegistry,
|
||||
"etcd": etcd.NewRegistry,
|
||||
"mdns": mdns.NewRegistry,
|
||||
"memory": rmem.NewRegistry,
|
||||
@@ -223,6 +236,11 @@ var (
|
||||
"quic": quic.NewTransport,
|
||||
}
|
||||
|
||||
DefaultRuntimes = map[string]func(...runtime.Option) runtime.Runtime{
|
||||
"local": runtime.NewRuntime,
|
||||
"kubernetes": kubernetes.NewRuntime,
|
||||
}
|
||||
|
||||
// used for default selection as the fall back
|
||||
defaultClient = "rpc"
|
||||
defaultServer = "rpc"
|
||||
@@ -230,6 +248,7 @@ var (
|
||||
defaultRegistry = "mdns"
|
||||
defaultSelector = "registry"
|
||||
defaultTransport = "http"
|
||||
defaultRuntime = "local"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -249,6 +268,7 @@ func newCmd(opts ...Option) Cmd {
|
||||
Server: &server.DefaultServer,
|
||||
Selector: &selector.DefaultSelector,
|
||||
Transport: &transport.DefaultTransport,
|
||||
Runtime: &runtime.DefaultRuntime,
|
||||
|
||||
Brokers: DefaultBrokers,
|
||||
Clients: DefaultClients,
|
||||
@@ -256,6 +276,7 @@ func newCmd(opts ...Option) Cmd {
|
||||
Selectors: DefaultSelectors,
|
||||
Servers: DefaultServers,
|
||||
Transports: DefaultTransports,
|
||||
Runtimes: DefaultRuntimes,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
@@ -296,6 +317,16 @@ func (c *cmd) Before(ctx *cli.Context) error {
|
||||
var serverOpts []server.Option
|
||||
var clientOpts []client.Option
|
||||
|
||||
// Set the runtime
|
||||
if name := ctx.String("runtime"); len(name) > 0 {
|
||||
r, ok := c.opts.Runtimes[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unsupported runtime: %s", name)
|
||||
}
|
||||
|
||||
*c.opts.Runtime = r()
|
||||
}
|
||||
|
||||
// Set the client
|
||||
if name := ctx.String("client"); len(name) > 0 {
|
||||
// only change if we have the client and type differs
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/client/selector"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/runtime"
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/transport"
|
||||
)
|
||||
@@ -24,6 +25,7 @@ type Options struct {
|
||||
Transport *transport.Transport
|
||||
Client *client.Client
|
||||
Server *server.Server
|
||||
Runtime *runtime.Runtime
|
||||
|
||||
Brokers map[string]func(...broker.Option) broker.Broker
|
||||
Clients map[string]func(...client.Option) client.Client
|
||||
@@ -31,6 +33,7 @@ type Options struct {
|
||||
Selectors map[string]func(...selector.Option) selector.Selector
|
||||
Servers map[string]func(...server.Option) server.Server
|
||||
Transports map[string]func(...transport.Option) transport.Transport
|
||||
Runtimes map[string]func(...runtime.Option) runtime.Runtime
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
@@ -135,3 +138,10 @@ func NewTransport(name string, t func(...transport.Option) transport.Transport)
|
||||
o.Transports[name] = t
|
||||
}
|
||||
}
|
||||
|
||||
// New runtime func
|
||||
func NewRuntime(name string, r func(...runtime.Option) runtime.Runtime) Option {
|
||||
return func(o *Options) {
|
||||
o.Runtimes[name] = r
|
||||
}
|
||||
}
|
||||
|
@@ -153,7 +153,7 @@ func (m *memory) reload() error {
|
||||
}
|
||||
|
||||
func (m *memory) update() {
|
||||
var watchers []*watcher
|
||||
watchers := make([]*watcher, 0, len(m.watchers))
|
||||
|
||||
m.RLock()
|
||||
for _, w := range m.watchers {
|
||||
|
@@ -62,7 +62,7 @@ func TestStructArray(t *testing.T) {
|
||||
{
|
||||
[]byte(`[{"foo": "bar"}]`),
|
||||
emptyTSlice,
|
||||
[]T{T{Foo: "bar"}},
|
||||
[]T{{Foo: "bar"}},
|
||||
},
|
||||
}
|
||||
|
||||
|
@@ -38,7 +38,7 @@ func (c *etcd) Read() (*source.ChangeSet, error) {
|
||||
return nil, fmt.Errorf("source not found: %s", c.prefix)
|
||||
}
|
||||
|
||||
var kvs []*mvccpb.KeyValue
|
||||
kvs := make([]*mvccpb.KeyValue, 0, len(rsp.Kvs))
|
||||
for _, v := range rsp.Kvs {
|
||||
kvs = append(kvs, (*mvccpb.KeyValue)(v))
|
||||
}
|
||||
|
95
debug/buffer/buffer.go
Normal file
95
debug/buffer/buffer.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Package buffer provides a simple ring buffer for storing local data
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Buffer struct {
|
||||
size int
|
||||
sync.RWMutex
|
||||
vals []*Entry
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
Value interface{}
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
func (b *Buffer) Put(v interface{}) {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
// append to values
|
||||
b.vals = append(b.vals, &Entry{
|
||||
Value: v,
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
|
||||
// trim if bigger than size required
|
||||
if len(b.vals) > b.size {
|
||||
b.vals = b.vals[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the last n entries
|
||||
func (b *Buffer) Get(n int) []*Entry {
|
||||
// reset any invalid values
|
||||
if n > b.size || n < 0 {
|
||||
n = b.size
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// create a delta
|
||||
delta := b.size - n
|
||||
|
||||
// if all the values are less than delta
|
||||
if len(b.vals) < delta {
|
||||
return b.vals
|
||||
}
|
||||
|
||||
// return the delta set
|
||||
return b.vals[delta:]
|
||||
}
|
||||
|
||||
// Return the entries since a specific time
|
||||
func (b *Buffer) Since(t time.Time) []*Entry {
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// return all the values
|
||||
if t.IsZero() {
|
||||
return b.vals
|
||||
}
|
||||
|
||||
// if its in the future return nothing
|
||||
if time.Since(t).Seconds() < 0.0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, v := range b.vals {
|
||||
// find the starting point
|
||||
d := v.Timestamp.Sub(t)
|
||||
|
||||
// return the values
|
||||
if d.Seconds() > 0.0 {
|
||||
return b.vals[i:]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Buffer) Size() int {
|
||||
return b.size
|
||||
}
|
||||
|
||||
// New returns a new buffer of the given size
|
||||
func New(i int) *Buffer {
|
||||
return &Buffer{
|
||||
size: i,
|
||||
}
|
||||
}
|
79
debug/buffer/buffer_test.go
Normal file
79
debug/buffer/buffer_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBuffer(t *testing.T) {
|
||||
b := New(10)
|
||||
|
||||
// test one value
|
||||
b.Put("foo")
|
||||
v := b.Get(1)
|
||||
|
||||
if val := v[0].Value.(string); val != "foo" {
|
||||
t.Fatalf("expected foo got %v", val)
|
||||
}
|
||||
|
||||
b = New(10)
|
||||
|
||||
// test 10 values
|
||||
for i := 0; i < 10; i++ {
|
||||
b.Put(i)
|
||||
}
|
||||
|
||||
d := time.Now()
|
||||
v = b.Get(10)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
val := v[i].Value.(int)
|
||||
|
||||
if val != i {
|
||||
t.Fatalf("expected %d got %d", i, val)
|
||||
}
|
||||
}
|
||||
|
||||
// test more values
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
v := i * 2
|
||||
b.Put(v)
|
||||
}
|
||||
|
||||
v = b.Get(10)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
val := v[i].Value.(int)
|
||||
expect := i * 2
|
||||
if val != expect {
|
||||
t.Fatalf("expected %d got %d", expect, val)
|
||||
}
|
||||
}
|
||||
|
||||
// sleep 100 ms
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
// assume we'll get everything
|
||||
v = b.Since(d)
|
||||
|
||||
if len(v) != 10 {
|
||||
t.Fatalf("expected 10 entries but got %d", len(v))
|
||||
}
|
||||
|
||||
// write 1 more entry
|
||||
d = time.Now()
|
||||
b.Put(100)
|
||||
|
||||
// sleep 100 ms
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
v = b.Since(d)
|
||||
if len(v) != 1 {
|
||||
t.Fatalf("expected 1 entries but got %d", len(v))
|
||||
}
|
||||
|
||||
if v[0].Value.(int) != 100 {
|
||||
t.Fatalf("expected value 100 got %v", v[0])
|
||||
}
|
||||
}
|
118
debug/profile/pprof/pprof.go
Normal file
118
debug/profile/pprof/pprof.go
Normal file
@@ -0,0 +1,118 @@
|
||||
// Package pprof provides a pprof profiler
|
||||
package pprof
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/debug/profile"
|
||||
)
|
||||
|
||||
type profiler struct {
|
||||
opts profile.Options
|
||||
|
||||
sync.Mutex
|
||||
running bool
|
||||
exit chan bool
|
||||
|
||||
// where the cpu profile is written
|
||||
cpuFile *os.File
|
||||
// where the mem profile is written
|
||||
memFile *os.File
|
||||
}
|
||||
|
||||
func (p *profiler) writeHeap(f *os.File) {
|
||||
defer f.Close()
|
||||
|
||||
t := time.NewTicker(time.Second * 30)
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
runtime.GC()
|
||||
pprof.WriteHeapProfile(f)
|
||||
case <-p.exit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *profiler) Start() error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if p.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
// create exit channel
|
||||
p.exit = make(chan bool)
|
||||
|
||||
cpuFile := filepath.Join("/tmp", "cpu.pprof")
|
||||
memFile := filepath.Join("/tmp", "mem.pprof")
|
||||
|
||||
if len(p.opts.Name) > 0 {
|
||||
cpuFile = filepath.Join("/tmp", p.opts.Name+".cpu.pprof")
|
||||
memFile = filepath.Join("/tmp", p.opts.Name+".mem.pprof")
|
||||
}
|
||||
|
||||
f1, err := os.Create(cpuFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f2, err := os.Create(memFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// start cpu profiling
|
||||
if err := pprof.StartCPUProfile(f1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write the heap periodically
|
||||
go p.writeHeap(f2)
|
||||
|
||||
// set cpu file
|
||||
p.cpuFile = f1
|
||||
// set mem file
|
||||
p.memFile = f2
|
||||
|
||||
p.running = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *profiler) Stop() error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
select {
|
||||
case <-p.exit:
|
||||
return nil
|
||||
default:
|
||||
close(p.exit)
|
||||
pprof.StopCPUProfile()
|
||||
p.cpuFile.Close()
|
||||
p.running = false
|
||||
p.cpuFile = nil
|
||||
p.memFile = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewProfile(opts ...profile.Option) profile.Profile {
|
||||
var options profile.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
p := new(profiler)
|
||||
p.opts = options
|
||||
return p
|
||||
}
|
23
debug/profile/profile.go
Normal file
23
debug/profile/profile.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Package profile is for profilers
|
||||
package profile
|
||||
|
||||
type Profile interface {
|
||||
// Start the profiler
|
||||
Start() error
|
||||
// Stop the profiler
|
||||
Stop() error
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
// Name to use for the profile
|
||||
Name string
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
// Name of the profile
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
@@ -1,13 +1,11 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: micro/go-micro/debug/proto/debug.proto
|
||||
// source: github.com/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"
|
||||
)
|
||||
|
||||
@@ -32,7 +30,7 @@ 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}
|
||||
return fileDescriptor_7cb19b1a05a6e0a9, []int{0}
|
||||
}
|
||||
|
||||
func (m *HealthRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -65,7 +63,7 @@ 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}
|
||||
return fileDescriptor_7cb19b1a05a6e0a9, []int{1}
|
||||
}
|
||||
|
||||
func (m *HealthResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -103,7 +101,7 @@ 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}
|
||||
return fileDescriptor_7cb19b1a05a6e0a9, []int{2}
|
||||
}
|
||||
|
||||
func (m *StatsRequest) XXX_Unmarshal(b []byte) error {
|
||||
@@ -144,7 +142,7 @@ 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}
|
||||
return fileDescriptor_7cb19b1a05a6e0a9, []int{3}
|
||||
}
|
||||
|
||||
func (m *StatsResponse) XXX_Unmarshal(b []byte) error {
|
||||
@@ -208,129 +206,24 @@ func init() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("micro/go-micro/debug/proto/debug.proto", fileDescriptor_f25415e61bccfa1f)
|
||||
proto.RegisterFile("github.com/micro/go-micro/debug/proto/debug.proto", fileDescriptor_7cb19b1a05a6e0a9)
|
||||
}
|
||||
|
||||
var fileDescriptor_f25415e61bccfa1f = []byte{
|
||||
// 230 bytes of a gzipped FileDescriptorProto
|
||||
var fileDescriptor_7cb19b1a05a6e0a9 = []byte{
|
||||
// 237 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",
|
||||
0x14, 0x84, 0x77, 0xeb, 0xb6, 0xe2, 0xc3, 0x66, 0x21, 0x07, 0x09, 0x7b, 0x92, 0x9c, 0x0a, 0x62,
|
||||
0x8a, 0xfa, 0x17, 0x3c, 0x78, 0xae, 0x77, 0x21, 0x6d, 0x43, 0x5a, 0x30, 0xa6, 0x26, 0x2f, 0x07,
|
||||
0xcf, 0xfe, 0x71, 0x69, 0x92, 0x82, 0xbd, 0xcd, 0x4c, 0x98, 0x21, 0xdf, 0x83, 0x27, 0x3d, 0xe3,
|
||||
0x14, 0x7a, 0x31, 0x58, 0xd3, 0x9a, 0x79, 0x70, 0xb6, 0xd5, 0xf6, 0x31, 0x89, 0x51, 0xf5, 0x41,
|
||||
0xb7, 0x8b, 0xb3, 0x98, 0xb5, 0x88, 0x9a, 0x9f, 0xa1, 0x7e, 0x53, 0xf2, 0x13, 0xa7, 0x4e, 0x7d,
|
||||
0x07, 0xe5, 0x91, 0x37, 0x40, 0xb6, 0xc0, 0x2f, 0xf6, 0xcb, 0x2b, 0x7a, 0x07, 0x95, 0x47, 0x89,
|
||||
0xc1, 0xb3, 0xe3, 0xfd, 0xb1, 0xb9, 0xe9, 0xb2, 0xe3, 0x04, 0x6e, 0xdf, 0x51, 0xa2, 0xdf, 0x9a,
|
||||
0xbf, 0x47, 0xa8, 0x73, 0x90, 0x9b, 0x0c, 0xae, 0x3d, 0x4a, 0x87, 0x6a, 0x8c, 0xd5, 0x53, 0xb7,
|
||||
0xd9, 0x75, 0x33, 0x2c, 0x38, 0x1b, 0xc5, 0x8a, 0xf8, 0x90, 0xdd, 0x9a, 0x1b, 0x65, 0xac, 0xfb,
|
||||
0x61, 0x57, 0x29, 0x4f, 0x6e, 0x5d, 0xc2, 0xc9, 0x29, 0x39, 0x7a, 0x76, 0x4a, 0x4b, 0xd9, 0x52,
|
||||
0x02, 0x85, 0x1e, 0x58, 0x19, 0xc3, 0x42, 0x0f, 0xcf, 0x1f, 0x50, 0xbe, 0xae, 0x7c, 0xf4, 0x01,
|
||||
0xaa, 0x04, 0x42, 0x89, 0xd8, 0x21, 0x5e, 0xce, 0x62, 0x4f, 0xc8, 0x0f, 0xb4, 0x81, 0x32, 0x7e,
|
||||
0x9d, 0xd6, 0xe2, 0x3f, 0xd3, 0x85, 0x88, 0x1d, 0x11, 0x3f, 0xf4, 0x55, 0xbc, 0xdb, 0xcb, 0x5f,
|
||||
0x00, 0x00, 0x00, 0xff, 0xff, 0x20, 0xb8, 0xfe, 0x98, 0x6c, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: micro/go-micro/debug/proto/debug.proto
|
||||
// source: github.com/micro/go-micro/debug/proto/debug.proto
|
||||
|
||||
package debug
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
func TestErrors(t *testing.T) {
|
||||
testData := []*Error{
|
||||
&Error{
|
||||
{
|
||||
Id: "test",
|
||||
Code: 500,
|
||||
Detail: "Internal server error",
|
||||
|
@@ -7,14 +7,14 @@ import (
|
||||
|
||||
proto "github.com/micro/go-micro/debug/proto"
|
||||
"github.com/micro/go-micro/registry/memory"
|
||||
"github.com/micro/go-micro/util/test"
|
||||
)
|
||||
|
||||
func TestFunction(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
r := memory.NewRegistry()
|
||||
r.(*memory.Registry).Services = testData
|
||||
r := memory.NewRegistry(memory.Services(test.Data))
|
||||
|
||||
// create service
|
||||
fn := NewFunction(
|
||||
|
51
go.mod
51
go.mod
@@ -6,58 +6,43 @@ require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/beevik/ntp v0.2.0
|
||||
github.com/bitly/go-simplejson v0.5.0
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
github.com/bwmarrin/discordgo v0.19.0
|
||||
github.com/cloudflare/cloudflare-go v0.10.4
|
||||
github.com/coreos/bbolt v1.3.3 // indirect
|
||||
github.com/bwmarrin/discordgo v0.20.1
|
||||
github.com/coreos/etcd v3.3.17+incompatible
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/fsouza/go-dockerclient v1.4.4
|
||||
github.com/fsouza/go-dockerclient v1.6.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/go-acme/lego/v3 v3.1.0
|
||||
github.com/go-log/log v0.1.0
|
||||
github.com/go-playground/locales v0.12.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.16.0 // indirect
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
||||
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc // indirect
|
||||
github.com/go-playground/locales v0.13.0 // indirect
|
||||
github.com/golang/protobuf v1.3.2
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/handlers v1.4.2
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/hashicorp/consul/api v1.2.0
|
||||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/imdario/mergo v0.3.8
|
||||
github.com/jonboulle/clockwork v0.1.0 // indirect
|
||||
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1
|
||||
github.com/json-iterator/go v1.1.7
|
||||
github.com/leodido/go-urn v1.1.0 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.12.1
|
||||
github.com/mholt/certmagic v0.7.5
|
||||
github.com/json-iterator/go v1.1.8
|
||||
github.com/kr/pretty v0.1.0
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
github.com/lib/pq v1.2.0
|
||||
github.com/lucas-clemente/quic-go v0.13.1
|
||||
github.com/mholt/certmagic v0.8.3
|
||||
github.com/micro/cli v0.2.0
|
||||
github.com/micro/mdns v0.3.0
|
||||
github.com/micro/protoc-gen-micro v1.0.0 // indirect
|
||||
github.com/miekg/dns v1.1.22
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/nats-io/nats-server/v2 v2.1.0 // indirect
|
||||
github.com/nats-io/nats.go v1.8.1
|
||||
github.com/nats-io/nats.go v1.9.1
|
||||
github.com/nlopes/slack v0.6.0
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/soheilhy/cmux v0.1.4 // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.etcd.io/bbolt v1.3.3 // indirect
|
||||
go.uber.org/multierr v1.2.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||
golang.org/x/net v0.0.0-20191011234655-491137f69257
|
||||
google.golang.org/grpc v1.24.0
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
go.uber.org/zap v1.12.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a
|
||||
golang.org/x/net v0.0.0-20191109021931-daa7c04131f5
|
||||
google.golang.org/grpc v1.25.1
|
||||
gopkg.in/go-playground/validator.v9 v9.30.0
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4
|
||||
sigs.k8s.io/yaml v1.1.0 // indirect
|
||||
)
|
||||
|
238
go.sum
238
go.sum
@@ -3,7 +3,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
|
||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
|
||||
github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw=
|
||||
@@ -19,28 +18,22 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L
|
||||
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE=
|
||||
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/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/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-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
@@ -48,33 +41,41 @@ github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
|
||||
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY=
|
||||
github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
|
||||
github.com/bwmarrin/discordgo v0.20.1 h1:Ihh3/mVoRwy3otmaoPDUioILBJq4fdWkpsi83oj2Lmk=
|
||||
github.com/bwmarrin/discordgo v0.20.1/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.10.2 h1:VBodKICVPnwmDxstcW3biKcDSpFIfS/RELUXsZSBYK4=
|
||||
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
|
||||
github.com/cloudflare/cloudflare-go v0.10.4 h1:7C1D9mtcNFZLCqmhkHK2BlwKKm9fi4cBqY6qpYtQv5E=
|
||||
github.com/cloudflare/cloudflare-go v0.10.4/go.mod h1:4HgmUutVbZTRnHg91bS8lvlA0Wx+TgqttLDcwey2S6E=
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M=
|
||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.3.0 h1:xjvXQWABwS2uiv3TWgQt5Uth60Gu86LTGZXMJkjc7rY=
|
||||
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
|
||||
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/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.17+incompatible h1:f/Z3EoDSx1yjaIjLQGo1diYUlQYSBrrAQ5vP8NjwXwo=
|
||||
github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
@@ -86,15 +87,15 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker 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/docker v1.4.2-0.20191101170500-ac7306503d23 h1:oqgGT9O61YAYvI41EBsLePOr+LE6roB0xY4gpkZuFSE=
|
||||
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
@@ -104,21 +105,22 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c h1:pBgVXWDXju1m8W4lnEeIqTHPOzhTUO81a7yknM/xQR4=
|
||||
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsouza/go-dockerclient v1.4.4 h1:Sd5nD4wdAgiPxvrbYUzT2ZZNmPk3z+GGnZ+frvw8z04=
|
||||
github.com/fsouza/go-dockerclient v1.4.4/go.mod h1:PrwszSL5fbmsESocROrOGq/NULMXRw+bajY0ltzD6MA=
|
||||
github.com/fsouza/go-dockerclient v1.6.0 h1:f7j+AX94143JL1H3TiqSMkM4EcLDI0De1qD4GGn3Hig=
|
||||
github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-acme/lego v2.7.2+incompatible h1:ThhpPBgf6oa9X/vRd0kEmWOsX7+vmYdckmGZSb+FEp0=
|
||||
github.com/go-acme/lego/v3 v3.1.0 h1:yanYFoYW8azFkCvJfIk7edWWfjkYkhDxe45ZsxoW4Xk=
|
||||
github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE=
|
||||
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
||||
@@ -129,25 +131,23 @@ github.com/go-log/log v0.1.0 h1:wudGTNsiGzrD5ZjgIkVZ517ugi2XRe9Q/xRCzwEO4/U=
|
||||
github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
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/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc h1:55rEp52jU6bkyslZ1+C/7NGfpQsEc6pxGLAGDOctqbw=
|
||||
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
@@ -155,16 +155,15 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
@@ -180,52 +179,19 @@ github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/consul/api v1.2.0 h1:oPsuzLp2uk7I7rojPKuncWbZ+m5TMoD4Ivs+2Rkeh4Y=
|
||||
github.com/hashicorp/consul/api v1.2.0/go.mod h1:1SIkFYi2ZTXUE5Kgt179+4hH33djo11+0Eo2XgTAtkw=
|
||||
github.com/hashicorp/consul/sdk v0.2.0 h1:GWFYFmry/k4b1hEoy7kSkmU8e30GAyI4VZHk0fRxeL4=
|
||||
github.com/hashicorp/consul/sdk v0.2.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-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-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
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-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-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
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/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
|
||||
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:anPrsicrIi2ColgWTVPk+TrN42hJIWlfPHSBP9S0ZkM=
|
||||
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
@@ -233,14 +199,14 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1 h1:lnrOS18wZBYrzdDmnUeg1OVk+kQ3rxG8mZWU89DpMIA=
|
||||
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1/go.mod h1:DFXrEwSRX0p/aSvxE21319menCBFeQO0jXpRj7LEZUA=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
@@ -252,7 +218,6 @@ github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
@@ -263,45 +228,47 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=
|
||||
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
|
||||
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA=
|
||||
github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ=
|
||||
github.com/lucas-clemente/quic-go v0.12.1 h1:BPITli+6KnKogtTxBk2aS4okr5dUHz2LtIDAP1b8UL4=
|
||||
github.com/lucas-clemente/quic-go v0.12.1/go.mod h1:UXJJPE4RfFef/xPO5wQm0tITK8gNfqwTxjbE7s3Vb8s=
|
||||
github.com/lucas-clemente/quic-go v0.13.1 h1:CxtJTXQIh2aboCPk0M6vf530XOov6DZjVBiSE3nSj8s=
|
||||
github.com/lucas-clemente/quic-go v0.13.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgVpFfizUfW5WMaqyU=
|
||||
github.com/marten-seemann/chacha20 v0.2.0 h1:f40vqzzx+3GdOmzQoItkLX5WLvHgPgyYqFFIO5Gh4hQ=
|
||||
github.com/marten-seemann/chacha20 v0.2.0/go.mod h1:HSdjFau7GzYRj+ahFNwsO3ouVJr1HFkWoEwNDb4TMtE=
|
||||
github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
|
||||
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/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gtbkks=
|
||||
github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mholt/certmagic v0.7.5 h1:1ZGHwUI4+zg1S17tPUj5Xxb9Q1ghTjLcUZE5G4yV5SM=
|
||||
github.com/mholt/certmagic v0.7.5/go.mod h1:91uJzK5K8IWtYQqTi5R2tsxV1pCde+wdGfaRaOZi6aQ=
|
||||
github.com/mholt/certmagic v0.8.3 h1:JOUiX9IAZbbgyjNP2GY6v/6lorH+9GkZsc7ktMpGCSo=
|
||||
github.com/mholt/certmagic v0.8.3/go.mod h1:91uJzK5K8IWtYQqTi5R2tsxV1pCde+wdGfaRaOZi6aQ=
|
||||
github.com/micro/cli v0.2.0 h1:ut3rV5JWqZjsXIa2MvGF+qMUP8DAUTvHX9Br5gO4afA=
|
||||
github.com/micro/cli v0.2.0/go.mod h1:jRT9gmfVKWSS6pkKcXQ8YhUyj6bzwxK8Fp5b0Y7qNnk=
|
||||
github.com/micro/go-micro v1.16.0/go.mod h1:A0F58bHLh2m0LAI9QyhvmbN8c1cxhAZo3cM6s+iDsrM=
|
||||
github.com/micro/mdns v0.3.0 h1:bYycYe+98AXR3s8Nq5qvt6C573uFTDPIYzJemWON0QE=
|
||||
github.com/micro/mdns v0.3.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/micro/protoc-gen-micro v1.0.0 h1:qKh5S3I1RfenhIs5mqDFJLwRlRDlgin7XWiUKZbpwLM=
|
||||
github.com/micro/protoc-gen-micro v1.0.0/go.mod h1:C8ij4DJhapBmypcT00AXdb0cZ675/3PqUO02buWWqbE=
|
||||
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI=
|
||||
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/miekg/dns v1.1.22 h1:Jm64b3bO9kP43ddLjL2EY3Io6bmy1qGb9Xxz6TqS6rc=
|
||||
github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
|
||||
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
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/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=
|
||||
@@ -309,15 +276,16 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||
github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/nats-server/v2 v2.1.0 h1:Yi0+ZhRPtPAGeIxFn5erIeJIV9wXA+JznfSxK621Fbk=
|
||||
github.com/nats-io/nats-server/v2 v2.1.0/go.mod h1:r5y0WgCag0dTj/qiHkHrXAcKQ/f5GMOZaEGdoxxnJ4I=
|
||||
github.com/nats-io/nats.go v1.8.1 h1:6lF/f1/NN6kzUDBz6pyvQDEXO39jqXcWRLu/tKjtOUQ=
|
||||
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
|
||||
github.com/nats-io/nkeys v0.0.2 h1:+qM7QpgXnvDDixitZtQUBDY9w/s9mu1ghS+JIbsrx6M=
|
||||
github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ=
|
||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
||||
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=
|
||||
@@ -331,22 +299,24 @@ github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2
|
||||
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
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/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -354,34 +324,29 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@@ -392,51 +357,54 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY=
|
||||
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4=
|
||||
go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.12.0 h1:dySoUQPFBGj6xwjmBzageVL8jGi8uxc6bEmJQjA06bw=
|
||||
go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -445,23 +413,28 @@ golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnf
|
||||
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-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a h1:R/qVym5WAxsZWQqZCwDY/8sdVKV1m1WgU4/S5IRQAzc=
|
||||
golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -469,14 +442,16 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191011234655-491137f69257 h1:ry8e2D+cwaV6hk7lb3aRTjjZo24shrbK0e11QEOkTIg=
|
||||
golang.org/x/net v0.0.0-20191011234655-491137f69257/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191109021931-daa7c04131f5 h1:bHNaocaoJxYBo5cw41UyTMLjYlb8wPY7+WFrnklbHOM=
|
||||
golang.org/x/net v0.0.0-20191109021931-daa7c04131f5/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -487,11 +462,9 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -503,11 +476,19 @@ golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM=
|
||||
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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=
|
||||
@@ -526,7 +507,12 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -537,24 +523,26 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
|
||||
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-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
|
||||
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-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
|
||||
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
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/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v9 v9.30.0 h1:Wk0Z37oBmKj9/n+tPyBHZmeL19LaCoK3Qq48VwYENss=
|
||||
gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
@@ -568,13 +556,11 @@ gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4 h1:hpHWhzn4jTCsAJZZ2loNKfy2QWyPDRJVl3aTFXeMW8g=
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
@@ -582,12 +568,12 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
@@ -12,6 +12,7 @@ type metaKey struct{}
|
||||
// from Transport headers.
|
||||
type Metadata map[string]string
|
||||
|
||||
// Copy makes a copy of the metadata
|
||||
func Copy(md Metadata) Metadata {
|
||||
cmd := make(Metadata)
|
||||
for k, v := range md {
|
||||
@@ -20,11 +21,41 @@ func Copy(md Metadata) Metadata {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Get returns a single value from metadata in the context
|
||||
func Get(ctx context.Context, key string) (string, bool) {
|
||||
md, ok := FromContext(ctx)
|
||||
if !ok {
|
||||
return "", ok
|
||||
}
|
||||
val, ok := md[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// FromContext returns metadata from the given context
|
||||
func FromContext(ctx context.Context) (Metadata, bool) {
|
||||
md, ok := ctx.Value(metaKey{}).(Metadata)
|
||||
return md, ok
|
||||
}
|
||||
|
||||
// NewContext creates a new context with the given metadata
|
||||
func NewContext(ctx context.Context, md Metadata) context.Context {
|
||||
return context.WithValue(ctx, metaKey{}, md)
|
||||
}
|
||||
|
||||
// MergeContext merges metadata to existing metadata, overwriting if specified
|
||||
func MergeContext(ctx context.Context, patchMd Metadata, overwrite bool) context.Context {
|
||||
md, _ := ctx.Value(metaKey{}).(Metadata)
|
||||
cmd := make(Metadata)
|
||||
for k, v := range md {
|
||||
cmd[k] = v
|
||||
}
|
||||
for k, v := range patchMd {
|
||||
if _, ok := cmd[k]; ok && !overwrite {
|
||||
// skip
|
||||
} else {
|
||||
cmd[k] = v
|
||||
}
|
||||
}
|
||||
return context.WithValue(ctx, metaKey{}, cmd)
|
||||
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -40,3 +41,42 @@ func TestMetadataContext(t *testing.T) {
|
||||
t.Errorf("Expected metadata length 1 got %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeContext(t *testing.T) {
|
||||
type args struct {
|
||||
existing Metadata
|
||||
append Metadata
|
||||
overwrite bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Metadata
|
||||
}{
|
||||
{
|
||||
name: "matching key, overwrite false",
|
||||
args: args{
|
||||
existing: Metadata{"foo": "bar", "sumo": "demo"},
|
||||
append: Metadata{"sumo": "demo2"},
|
||||
overwrite: false,
|
||||
},
|
||||
want: Metadata{"foo": "bar", "sumo": "demo"},
|
||||
},
|
||||
{
|
||||
name: "matching key, overwrite true",
|
||||
args: args{
|
||||
existing: Metadata{"foo": "bar", "sumo": "demo"},
|
||||
append: Metadata{"sumo": "demo2"},
|
||||
overwrite: true,
|
||||
},
|
||||
want: Metadata{"foo": "bar", "sumo": "demo2"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got, _ := FromContext(MergeContext(NewContext(context.TODO(), tt.args.existing), tt.args.append, tt.args.overwrite)); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("MergeContext() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -78,13 +78,6 @@ func (m *monitor) check(service string) (*Status, error) {
|
||||
client.WithRetries(3),
|
||||
)
|
||||
if err != nil {
|
||||
// reap the dead node
|
||||
m.registry.Deregister(®istry.Service{
|
||||
Name: service.Name,
|
||||
Version: service.Version,
|
||||
Nodes: []*registry.Node{node},
|
||||
})
|
||||
|
||||
// save the error
|
||||
gerr = err
|
||||
continue
|
||||
@@ -140,7 +133,7 @@ func (m *monitor) reap() {
|
||||
defer m.Unlock()
|
||||
|
||||
// range over our watched services
|
||||
for service, _ := range m.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
|
||||
@@ -195,14 +188,14 @@ func (m *monitor) run() {
|
||||
serviceMap := make(map[string]bool)
|
||||
|
||||
m.RLock()
|
||||
for service, _ := range m.services {
|
||||
for service := range m.services {
|
||||
serviceMap[service] = true
|
||||
}
|
||||
m.RUnlock()
|
||||
|
||||
go func() {
|
||||
// check the status of all watched services
|
||||
for service, _ := range serviceMap {
|
||||
for service := range serviceMap {
|
||||
select {
|
||||
case <-m.exit:
|
||||
return
|
||||
@@ -307,7 +300,7 @@ func (m *monitor) Stop() error {
|
||||
return nil
|
||||
default:
|
||||
close(m.exit)
|
||||
for s, _ := range m.services {
|
||||
for s := range m.services {
|
||||
delete(m.services, s)
|
||||
}
|
||||
m.registry.Stop()
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/tunnel"
|
||||
bun "github.com/micro/go-micro/tunnel/broker"
|
||||
tun "github.com/micro/go-micro/tunnel/transport"
|
||||
"github.com/micro/go-micro/util/backoff"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
@@ -37,6 +39,8 @@ var (
|
||||
var (
|
||||
// ErrClientNotFound is returned when client for tunnel channel could not be found
|
||||
ErrClientNotFound = errors.New("client not found")
|
||||
// ErrPeerLinkNotFound is returned when peer link could not be found in tunnel Links
|
||||
ErrPeerLinkNotFound = errors.New("peer link not found")
|
||||
)
|
||||
|
||||
// network implements Network interface
|
||||
@@ -58,6 +62,8 @@ type network struct {
|
||||
|
||||
// tunClient is a map of tunnel clients keyed over tunnel channel names
|
||||
tunClient map[string]transport.Client
|
||||
// peerLinks is a map of links for each peer
|
||||
peerLinks map[string]tunnel.Link
|
||||
|
||||
sync.RWMutex
|
||||
// connected marks the network as connected
|
||||
@@ -74,21 +80,6 @@ func newNetwork(opts ...Option) Network {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
// init tunnel address to the network bind address
|
||||
options.Tunnel.Init(
|
||||
tunnel.Address(options.Address),
|
||||
)
|
||||
|
||||
// init router Id to the network id
|
||||
options.Router.Init(
|
||||
router.Id(options.Id),
|
||||
)
|
||||
|
||||
// create tunnel client with tunnel transport
|
||||
tunTransport := tun.NewTransport(
|
||||
tun.WithTunnel(options.Tunnel),
|
||||
)
|
||||
|
||||
// set the address to a hashed address
|
||||
hasher := fnv.New64()
|
||||
hasher.Write([]byte(options.Address + options.Id))
|
||||
@@ -106,6 +97,27 @@ func newNetwork(opts ...Option) Network {
|
||||
peerAddress = address
|
||||
}
|
||||
|
||||
// init tunnel address to the network bind address
|
||||
options.Tunnel.Init(
|
||||
tunnel.Address(options.Address),
|
||||
)
|
||||
|
||||
// init router Id to the network id
|
||||
options.Router.Init(
|
||||
router.Id(options.Id),
|
||||
router.Address(peerAddress),
|
||||
)
|
||||
|
||||
// create tunnel client with tunnel transport
|
||||
tunTransport := tun.NewTransport(
|
||||
tun.WithTunnel(options.Tunnel),
|
||||
)
|
||||
|
||||
// create the tunnel broker
|
||||
tunBroker := bun.NewBroker(
|
||||
bun.WithTunnel(options.Tunnel),
|
||||
)
|
||||
|
||||
// server is network server
|
||||
server := server.NewServer(
|
||||
server.Id(options.Id),
|
||||
@@ -113,10 +125,12 @@ func newNetwork(opts ...Option) Network {
|
||||
server.Advertise(advertise),
|
||||
server.Name(options.Name),
|
||||
server.Transport(tunTransport),
|
||||
server.Broker(tunBroker),
|
||||
)
|
||||
|
||||
// client is network client
|
||||
client := client.NewClient(
|
||||
client.Broker(tunBroker),
|
||||
client.Transport(tunTransport),
|
||||
client.Selector(
|
||||
rtr.NewSelector(
|
||||
@@ -138,6 +152,7 @@ func newNetwork(opts ...Option) Network {
|
||||
server: server,
|
||||
client: client,
|
||||
tunClient: make(map[string]transport.Client),
|
||||
peerLinks: make(map[string]tunnel.Link),
|
||||
}
|
||||
|
||||
network.node.network = network
|
||||
@@ -246,19 +261,22 @@ func (n *network) resolve() {
|
||||
}
|
||||
|
||||
// handleNetConn handles network announcement messages
|
||||
func (n *network) handleNetConn(sess tunnel.Session, msg chan *transport.Message) {
|
||||
func (n *network) handleNetConn(s tunnel.Session, msg chan *message) {
|
||||
for {
|
||||
m := new(transport.Message)
|
||||
if err := sess.Recv(m); err != nil {
|
||||
if err := s.Recv(m); err != nil {
|
||||
log.Debugf("Network tunnel [%s] receive error: %v", NetworkChannel, err)
|
||||
if sessErr := sess.Close(); sessErr != nil {
|
||||
log.Debugf("Network tunnel [%s] closing connection error: %v", sessErr)
|
||||
if sessionErr := s.Close(); sessionErr != nil {
|
||||
log.Debugf("Network tunnel [%s] closing connection error: %v", NetworkChannel, sessionErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case msg <- m:
|
||||
case msg <- &message{
|
||||
msg: m,
|
||||
session: s,
|
||||
}:
|
||||
case <-n.closed:
|
||||
return
|
||||
}
|
||||
@@ -266,7 +284,7 @@ func (n *network) handleNetConn(sess tunnel.Session, msg chan *transport.Message
|
||||
}
|
||||
|
||||
// acceptNetConn accepts connections from NetworkChannel
|
||||
func (n *network) acceptNetConn(l tunnel.Listener, recv chan *transport.Message) {
|
||||
func (n *network) acceptNetConn(l tunnel.Listener, recv chan *message) {
|
||||
var i int
|
||||
for {
|
||||
// accept a connection
|
||||
@@ -295,10 +313,41 @@ func (n *network) acceptNetConn(l tunnel.Listener, recv chan *transport.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// updatePeerLinks updates link for a given peer
|
||||
func (n *network) updatePeerLinks(peerAddr string, linkId string) error {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
log.Tracef("Network looking up link %s in the peer links", linkId)
|
||||
// lookup the peer link
|
||||
var peerLink tunnel.Link
|
||||
for _, link := range n.tunnel.Links() {
|
||||
if link.Id() == linkId {
|
||||
peerLink = link
|
||||
break
|
||||
}
|
||||
}
|
||||
if peerLink == nil {
|
||||
return ErrPeerLinkNotFound
|
||||
}
|
||||
// if the peerLink is found in the returned links update peerLinks
|
||||
log.Tracef("Network updating peer links for peer %s", peerAddr)
|
||||
// add peerLink to the peerLinks map
|
||||
if link, ok := n.peerLinks[peerAddr]; ok {
|
||||
// if the existing has better Length then the new, replace it
|
||||
if link.Length() < peerLink.Length() {
|
||||
n.peerLinks[peerAddr] = peerLink
|
||||
}
|
||||
} else {
|
||||
n.peerLinks[peerAddr] = peerLink
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processNetChan processes messages received on NetworkChannel
|
||||
func (n *network) processNetChan(listener tunnel.Listener) {
|
||||
// receive network message queue
|
||||
recv := make(chan *transport.Message, 128)
|
||||
recv := make(chan *message, 128)
|
||||
|
||||
// accept NetworkChannel connections
|
||||
go n.acceptNetConn(listener, recv)
|
||||
@@ -307,12 +356,12 @@ func (n *network) processNetChan(listener tunnel.Listener) {
|
||||
select {
|
||||
case m := <-recv:
|
||||
// switch on type of message and take action
|
||||
switch m.Header["Micro-Method"] {
|
||||
switch m.msg.Header["Micro-Method"] {
|
||||
case "connect":
|
||||
// mark the time the message has been received
|
||||
now := time.Now()
|
||||
pbNetConnect := &pbNet.Connect{}
|
||||
if err := proto.Unmarshal(m.Body, pbNetConnect); err != nil {
|
||||
if err := proto.Unmarshal(m.msg.Body, pbNetConnect); err != nil {
|
||||
log.Debugf("Network tunnel [%s] connect unmarshal error: %v", NetworkChannel, err)
|
||||
continue
|
||||
}
|
||||
@@ -327,6 +376,12 @@ func (n *network) processNetChan(listener tunnel.Listener) {
|
||||
peers: make(map[string]*node),
|
||||
lastSeen: now,
|
||||
}
|
||||
// update peer links
|
||||
log.Tracef("Network updating peer link %s for peer: %s", m.session.Link(), pbNetConnect.Node.Address)
|
||||
if err := n.updatePeerLinks(pbNetConnect.Node.Address, m.session.Link()); err != nil {
|
||||
log.Debugf("Network failed updating peer links: %s", err)
|
||||
}
|
||||
// add peer to the list of node peers
|
||||
if err := n.node.AddPeer(peer); err == ErrPeerExists {
|
||||
log.Debugf("Network peer exists, refreshing: %s", peer.id)
|
||||
// update lastSeen time for the existing node
|
||||
@@ -349,7 +404,7 @@ func (n *network) processNetChan(listener tunnel.Listener) {
|
||||
// mark the time the message has been received
|
||||
now := time.Now()
|
||||
pbNetPeer := &pbNet.Peer{}
|
||||
if err := proto.Unmarshal(m.Body, pbNetPeer); err != nil {
|
||||
if err := proto.Unmarshal(m.msg.Body, pbNetPeer); err != nil {
|
||||
log.Debugf("Network tunnel [%s] peer unmarshal error: %v", NetworkChannel, err)
|
||||
continue
|
||||
}
|
||||
@@ -364,6 +419,11 @@ func (n *network) processNetChan(listener tunnel.Listener) {
|
||||
peers: make(map[string]*node),
|
||||
lastSeen: now,
|
||||
}
|
||||
// update peer links
|
||||
log.Tracef("Network updating peer link %s for peer: %s", m.session.Link(), pbNetPeer.Node.Address)
|
||||
if err := n.updatePeerLinks(pbNetPeer.Node.Address, m.session.Link()); err != nil {
|
||||
log.Debugf("Network failed updating peer links: %s", err)
|
||||
}
|
||||
if err := n.node.AddPeer(peer); err == nil {
|
||||
// send a solicit message when discovering new peer
|
||||
msg := &pbRtr.Solicit{
|
||||
@@ -387,13 +447,13 @@ func (n *network) processNetChan(listener tunnel.Listener) {
|
||||
|
||||
// NOTE: we don't unpack MaxDepth toplogy
|
||||
peer = UnpackPeerTopology(pbNetPeer, now, MaxDepth-1)
|
||||
log.Debugf("Network updating topology of node: %s", n.node.id)
|
||||
log.Tracef("Network updating topology of node: %s", n.node.id)
|
||||
if err := n.node.UpdatePeer(peer); err != nil {
|
||||
log.Debugf("Network failed to update peers: %v", err)
|
||||
}
|
||||
case "close":
|
||||
pbNetClose := &pbNet.Close{}
|
||||
if err := proto.Unmarshal(m.Body, pbNetClose); err != nil {
|
||||
if err := proto.Unmarshal(m.msg.Body, pbNetClose); err != nil {
|
||||
log.Debugf("Network tunnel [%s] close unmarshal error: %v", NetworkChannel, err)
|
||||
continue
|
||||
}
|
||||
@@ -412,6 +472,10 @@ func (n *network) processNetChan(listener tunnel.Listener) {
|
||||
if err := n.prunePeerRoutes(peer); err != nil {
|
||||
log.Debugf("Network failed pruning peer %s routes: %v", peer.id, err)
|
||||
}
|
||||
// deelete peer from the peerLinks
|
||||
n.Lock()
|
||||
delete(n.peerLinks, pbNetClose.Node.Address)
|
||||
n.Unlock()
|
||||
}
|
||||
case <-n.closed:
|
||||
return
|
||||
@@ -521,6 +585,9 @@ func (n *network) prune() {
|
||||
pruned := n.PruneStalePeerNodes(PruneTime)
|
||||
for id, peer := range pruned {
|
||||
log.Debugf("Network peer exceeded prune time: %s", id)
|
||||
n.Lock()
|
||||
delete(n.peerLinks, peer.address)
|
||||
n.Unlock()
|
||||
if err := n.prunePeerRoutes(peer); err != nil {
|
||||
log.Debugf("Network failed pruning peer %s routes: %v", id, err)
|
||||
}
|
||||
@@ -528,7 +595,7 @@ func (n *network) prune() {
|
||||
// get a list of all routes
|
||||
routes, err := n.options.Router.Table().List()
|
||||
if err != nil {
|
||||
log.Debugf("Network failed listing routes: %v", err)
|
||||
log.Debugf("Network failed listing routes when pruning peers: %v", err)
|
||||
continue
|
||||
}
|
||||
// collect all the router IDs in the routing table
|
||||
@@ -549,16 +616,22 @@ func (n *network) prune() {
|
||||
}
|
||||
|
||||
// handleCtrlConn handles ControlChannel connections
|
||||
func (n *network) handleCtrlConn(sess tunnel.Session, msg chan *transport.Message) {
|
||||
func (n *network) handleCtrlConn(s tunnel.Session, msg chan *message) {
|
||||
for {
|
||||
m := new(transport.Message)
|
||||
if err := sess.Recv(m); err != nil {
|
||||
log.Debugf("Network tunnel advert receive error: %v", err)
|
||||
if err := s.Recv(m); err != nil {
|
||||
log.Debugf("Network tunnel [%s] receive error: %v", ControlChannel, err)
|
||||
if sessionErr := s.Close(); sessionErr != nil {
|
||||
log.Debugf("Network tunnel [%s] closing connection error: %v", ControlChannel, sessionErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case msg <- m:
|
||||
case msg <- &message{
|
||||
msg: m,
|
||||
session: s,
|
||||
}:
|
||||
case <-n.closed:
|
||||
return
|
||||
}
|
||||
@@ -566,7 +639,7 @@ func (n *network) handleCtrlConn(sess tunnel.Session, msg chan *transport.Messag
|
||||
}
|
||||
|
||||
// acceptCtrlConn accepts connections from ControlChannel
|
||||
func (n *network) acceptCtrlConn(l tunnel.Listener, recv chan *transport.Message) {
|
||||
func (n *network) acceptCtrlConn(l tunnel.Listener, recv chan *message) {
|
||||
var i int
|
||||
for {
|
||||
// accept a connection
|
||||
@@ -596,42 +669,81 @@ func (n *network) acceptCtrlConn(l tunnel.Listener, recv chan *transport.Message
|
||||
}
|
||||
}
|
||||
|
||||
// setRouteMetric calculates metric of the route and updates it in place
|
||||
// - Local route metric is 1
|
||||
// - Routes with ID of adjacent nodes are 10
|
||||
// - Routes by peers of the advertiser are 100
|
||||
// - Routes beyond your neighbourhood are 1000
|
||||
func (n *network) setRouteMetric(route *router.Route) {
|
||||
// getHopCount queries network graph and returns hop count for given router
|
||||
// - Routes for local services have hop count 1
|
||||
// - Routes with ID of adjacent nodes have hop count 2
|
||||
// - Routes by peers of the advertiser have hop count 3
|
||||
// - Routes beyond node neighbourhood have hop count 4
|
||||
func (n *network) getHopCount(rtr string) int {
|
||||
// make sure node.peers are not modified
|
||||
n.node.RLock()
|
||||
defer n.node.RUnlock()
|
||||
|
||||
// we are the origin of the route
|
||||
if route.Router == n.options.Id {
|
||||
route.Metric = 1
|
||||
return
|
||||
if rtr == n.options.Id {
|
||||
return 1
|
||||
}
|
||||
|
||||
// check if the route origin is our peer
|
||||
if _, ok := n.peers[route.Router]; ok {
|
||||
route.Metric = 10
|
||||
return
|
||||
// the route origin is our peer
|
||||
if _, ok := n.peers[rtr]; ok {
|
||||
return 10
|
||||
}
|
||||
|
||||
// check if the route origin is the peer of our peer
|
||||
// the route origin is the peer of our peer
|
||||
for _, peer := range n.peers {
|
||||
for id := range peer.peers {
|
||||
if route.Router == id {
|
||||
route.Metric = 100
|
||||
return
|
||||
if rtr == id {
|
||||
return 100
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise we are three hops away
|
||||
return 1000
|
||||
}
|
||||
|
||||
// the origin of the route is beyond our neighbourhood
|
||||
route.Metric = 1000
|
||||
// getRouteMetric calculates router metric and returns it
|
||||
// Route metric is calculated based on link status and route hopd count
|
||||
func (n *network) getRouteMetric(router string, gateway string, link string) int64 {
|
||||
// set the route metric
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
|
||||
if link == "local" && gateway == "" {
|
||||
return 1
|
||||
}
|
||||
|
||||
if link == "local" && gateway != "" {
|
||||
return 2
|
||||
}
|
||||
|
||||
log.Tracef("Network looking up %s link to gateway: %s", link, gateway)
|
||||
if link, ok := n.peerLinks[gateway]; ok {
|
||||
// maka sure delay is non-zero
|
||||
delay := link.Delay()
|
||||
if delay == 0 {
|
||||
delay = 1
|
||||
}
|
||||
// get the route hop count
|
||||
hops := n.getHopCount(router)
|
||||
// make sure length is non-zero
|
||||
length := link.Length()
|
||||
if length == 0 {
|
||||
log.Debugf("Link length is 0 %v %v", link, link.Length())
|
||||
length = 10e9
|
||||
}
|
||||
log.Tracef("Network calculated metric %v delay %v length %v distance %v", (delay*length*int64(hops))/10e6, delay, length, hops)
|
||||
return (delay * length * int64(hops)) / 10e6
|
||||
}
|
||||
|
||||
log.Debugf("Network failed to find a link to gateway: %s", gateway)
|
||||
|
||||
return math.MaxInt64
|
||||
}
|
||||
|
||||
// processCtrlChan processes messages received on ControlChannel
|
||||
func (n *network) processCtrlChan(listener tunnel.Listener) {
|
||||
// receive control message queue
|
||||
recv := make(chan *transport.Message, 128)
|
||||
recv := make(chan *message, 128)
|
||||
|
||||
// accept ControlChannel cconnections
|
||||
go n.acceptCtrlConn(listener, recv)
|
||||
@@ -640,10 +752,10 @@ func (n *network) processCtrlChan(listener tunnel.Listener) {
|
||||
select {
|
||||
case m := <-recv:
|
||||
// switch on type of message and take action
|
||||
switch m.Header["Micro-Method"] {
|
||||
switch m.msg.Header["Micro-Method"] {
|
||||
case "advert":
|
||||
pbRtrAdvert := &pbRtr.Advert{}
|
||||
if err := proto.Unmarshal(m.Body, pbRtrAdvert); err != nil {
|
||||
if err := proto.Unmarshal(m.msg.Body, pbRtrAdvert); err != nil {
|
||||
log.Debugf("Network fail to unmarshal advert message: %v", err)
|
||||
continue
|
||||
}
|
||||
@@ -678,17 +790,22 @@ func (n *network) processCtrlChan(listener tunnel.Listener) {
|
||||
Network: event.Route.Network,
|
||||
Router: event.Route.Router,
|
||||
Link: event.Route.Link,
|
||||
Metric: int(event.Route.Metric),
|
||||
Metric: event.Route.Metric,
|
||||
}
|
||||
// set the route metric
|
||||
n.node.RLock()
|
||||
n.setRouteMetric(&route)
|
||||
n.node.RUnlock()
|
||||
// throw away metric bigger than 1000
|
||||
if route.Metric > 1000 {
|
||||
log.Debugf("Network route metric %d dropping node: %s", route.Metric, route.Router)
|
||||
continue
|
||||
// calculate route metric and add to the advertised metric
|
||||
// we need to make sure we do not overflow math.MaxInt64
|
||||
metric := n.getRouteMetric(event.Route.Router, event.Route.Gateway, event.Route.Link)
|
||||
log.Tracef("Network metric for router %s and gateway %s: %v", event.Route.Router, event.Route.Gateway, metric)
|
||||
|
||||
// check we don't overflow max int 64
|
||||
if d := route.Metric + metric; d > math.MaxInt64 || d <= 0 {
|
||||
// set to max int64 if we overflow
|
||||
route.Metric = math.MaxInt64
|
||||
} else {
|
||||
// set the combined value of metrics otherwise
|
||||
route.Metric = d
|
||||
}
|
||||
|
||||
// create router event
|
||||
e := &router.Event{
|
||||
Type: router.EventType(event.Type),
|
||||
@@ -699,7 +816,7 @@ func (n *network) processCtrlChan(listener tunnel.Listener) {
|
||||
}
|
||||
// if no events are eligible for processing continue
|
||||
if len(events) == 0 {
|
||||
log.Debugf("Network no events to be processed by router: %s", n.options.Id)
|
||||
log.Tracef("Network no events to be processed by router: %s", n.options.Id)
|
||||
continue
|
||||
}
|
||||
// create an advert and process it
|
||||
@@ -717,7 +834,7 @@ func (n *network) processCtrlChan(listener tunnel.Listener) {
|
||||
}
|
||||
case "solicit":
|
||||
pbRtrSolicit := &pbRtr.Solicit{}
|
||||
if err := proto.Unmarshal(m.Body, pbRtrSolicit); err != nil {
|
||||
if err := proto.Unmarshal(m.msg.Body, pbRtrSolicit); err != nil {
|
||||
log.Debugf("Network fail to unmarshal solicit message: %v", err)
|
||||
continue
|
||||
}
|
||||
@@ -755,12 +872,15 @@ func (n *network) advertise(advertChan <-chan *router.Advert) {
|
||||
if event.Route.Router == advert.Id {
|
||||
// hash the service before advertising it
|
||||
hasher.Reset()
|
||||
hasher.Write([]byte(event.Route.Address + n.node.id))
|
||||
// routes for multiple instances of a service will be collapsed here.
|
||||
// TODO: once we store labels in the table this may need to change
|
||||
// to include the labels in case they differ but highly unlikely
|
||||
hasher.Write([]byte(event.Route.Service + n.node.Address()))
|
||||
address = fmt.Sprintf("%d", hasher.Sum64())
|
||||
}
|
||||
|
||||
// calculate route metric to advertise
|
||||
metric := n.getRouteMetric(event.Route.Router, event.Route.Gateway, event.Route.Link)
|
||||
// NOTE: we override Gateway, Link and Address here
|
||||
// TODO: should we avoid overriding gateway?
|
||||
route := &pbRtr.Route{
|
||||
Service: event.Route.Service,
|
||||
Address: address,
|
||||
@@ -768,7 +888,7 @@ func (n *network) advertise(advertChan <-chan *router.Advert) {
|
||||
Network: event.Route.Network,
|
||||
Router: event.Route.Router,
|
||||
Link: DefaultLink,
|
||||
Metric: int64(event.Route.Metric),
|
||||
Metric: metric,
|
||||
}
|
||||
e := &pbRtr.Event{
|
||||
Type: pbRtr.EventType(event.Type),
|
||||
|
@@ -6,6 +6,8 @@ import (
|
||||
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/tunnel"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -54,6 +56,14 @@ type Network interface {
|
||||
Server() server.Server
|
||||
}
|
||||
|
||||
// message is network message
|
||||
type message struct {
|
||||
// msg is transport message
|
||||
msg *transport.Message
|
||||
// session is tunnel session
|
||||
session tunnel.Session
|
||||
}
|
||||
|
||||
// NewNetwork returns a new network interface
|
||||
func NewNetwork(opts ...Option) Network {
|
||||
return newNetwork(opts...)
|
||||
|
@@ -158,7 +158,7 @@ func (n *node) Nodes() []Node {
|
||||
|
||||
visited := n.walk(untilNoMorePeers, justWalk)
|
||||
|
||||
var nodes []Node
|
||||
nodes := make([]Node, 0, len(visited))
|
||||
// collect all the nodes and return them
|
||||
for _, node := range visited {
|
||||
nodes = append(nodes, node)
|
||||
@@ -282,7 +282,7 @@ func (n *node) Peers() []Node {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
|
||||
var peers []Node
|
||||
peers := make([]Node, 0, len(n.peers))
|
||||
for _, nodePeer := range n.peers {
|
||||
peer := nodePeer.getTopology(MaxDepth)
|
||||
peers = append(peers, peer)
|
||||
|
@@ -4,11 +4,9 @@
|
||||
package go_micro_network
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
proto1 "github.com/micro/go-micro/router/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
@@ -737,217 +735,3 @@ var fileDescriptor_0b7953b26a7c4730 = []byte{
|
||||
0xc9, 0x5c, 0x9e, 0x5d, 0x4f, 0xc0, 0xab, 0x20, 0xb9, 0xe8, 0x4d, 0xc3, 0xfc, 0x49, 0x0f, 0xff,
|
||||
0x06, 0x00, 0x00, 0xff, 0xff, 0x79, 0x8a, 0x5f, 0xf0, 0x24, 0x06, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
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
|
||||
|
||||
// NetworkClient is the client API for Network service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type NetworkClient interface {
|
||||
// Connect to the network
|
||||
Connect(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (*ConnectResponse, error)
|
||||
// Returns the entire network graph
|
||||
Graph(ctx context.Context, in *GraphRequest, opts ...grpc.CallOption) (*GraphResponse, error)
|
||||
// Returns a list of known nodes in the network
|
||||
Nodes(ctx context.Context, in *NodesRequest, opts ...grpc.CallOption) (*NodesResponse, error)
|
||||
// Returns a list of known routes in the network
|
||||
Routes(ctx context.Context, in *RoutesRequest, opts ...grpc.CallOption) (*RoutesResponse, error)
|
||||
// Returns a list of known services based on routes
|
||||
Services(ctx context.Context, in *ServicesRequest, opts ...grpc.CallOption) (*ServicesResponse, error)
|
||||
}
|
||||
|
||||
type networkClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewNetworkClient(cc *grpc.ClientConn) NetworkClient {
|
||||
return &networkClient{cc}
|
||||
}
|
||||
|
||||
func (c *networkClient) Connect(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (*ConnectResponse, error) {
|
||||
out := new(ConnectResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Connect", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *networkClient) Graph(ctx context.Context, in *GraphRequest, opts ...grpc.CallOption) (*GraphResponse, error) {
|
||||
out := new(GraphResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Graph", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *networkClient) Nodes(ctx context.Context, in *NodesRequest, opts ...grpc.CallOption) (*NodesResponse, error) {
|
||||
out := new(NodesResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Nodes", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *networkClient) Routes(ctx context.Context, in *RoutesRequest, opts ...grpc.CallOption) (*RoutesResponse, error) {
|
||||
out := new(RoutesResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Routes", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *networkClient) Services(ctx context.Context, in *ServicesRequest, opts ...grpc.CallOption) (*ServicesResponse, error) {
|
||||
out := new(ServicesResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Services", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NetworkServer is the server API for Network service.
|
||||
type NetworkServer interface {
|
||||
// Connect to the network
|
||||
Connect(context.Context, *ConnectRequest) (*ConnectResponse, error)
|
||||
// Returns the entire network graph
|
||||
Graph(context.Context, *GraphRequest) (*GraphResponse, error)
|
||||
// Returns a list of known nodes in the network
|
||||
Nodes(context.Context, *NodesRequest) (*NodesResponse, error)
|
||||
// Returns a list of known routes in the network
|
||||
Routes(context.Context, *RoutesRequest) (*RoutesResponse, error)
|
||||
// Returns a list of known services based on routes
|
||||
Services(context.Context, *ServicesRequest) (*ServicesResponse, error)
|
||||
}
|
||||
|
||||
func RegisterNetworkServer(s *grpc.Server, srv NetworkServer) {
|
||||
s.RegisterService(&_Network_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Network_Connect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ConnectRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NetworkServer).Connect(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.network.Network/Connect",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NetworkServer).Connect(ctx, req.(*ConnectRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Network_Graph_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GraphRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NetworkServer).Graph(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.network.Network/Graph",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NetworkServer).Graph(ctx, req.(*GraphRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Network_Nodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(NodesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NetworkServer).Nodes(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.network.Network/Nodes",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NetworkServer).Nodes(ctx, req.(*NodesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Network_Routes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RoutesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NetworkServer).Routes(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.network.Network/Routes",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NetworkServer).Routes(ctx, req.(*RoutesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Network_Services_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ServicesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NetworkServer).Services(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.network.Network/Services",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NetworkServer).Services(ctx, req.(*ServicesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Network_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.network.Network",
|
||||
HandlerType: (*NetworkServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Connect",
|
||||
Handler: _Network_Connect_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Graph",
|
||||
Handler: _Network_Graph_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Nodes",
|
||||
Handler: _Network_Nodes_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Routes",
|
||||
Handler: _Network_Routes_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Services",
|
||||
Handler: _Network_Services_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "github.com/micro/go-micro/network/proto/network.proto",
|
||||
}
|
||||
|
@@ -2,13 +2,18 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/micro/go-micro/network/resolver"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Resolver is a DNS network resolve
|
||||
type Resolver struct{}
|
||||
type Resolver struct {
|
||||
// The resolver address to use
|
||||
Address string
|
||||
}
|
||||
|
||||
// Resolve assumes ID is a domain name e.g micro.mu
|
||||
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
|
||||
@@ -22,14 +27,29 @@ func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
|
||||
host = "localhost"
|
||||
}
|
||||
|
||||
addrs, err := net.LookupHost(host)
|
||||
if len(r.Address) == 0 {
|
||||
r.Address = "1.0.0.1:53"
|
||||
}
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(dns.Fqdn(host), dns.TypeA)
|
||||
rec, err := dns.ExchangeContext(context.Background(), m, r.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var records []*resolver.Record
|
||||
|
||||
for _, addr := range addrs {
|
||||
for _, answer := range rec.Answer {
|
||||
h := answer.Header()
|
||||
// check record type matches
|
||||
if h.Rrtype != dns.TypeA {
|
||||
continue
|
||||
}
|
||||
|
||||
arec, _ := answer.(*dns.A)
|
||||
addr := arec.A.String()
|
||||
|
||||
// join resolved record with port
|
||||
address := net.JoinHostPort(addr, port)
|
||||
// append to record set
|
||||
|
@@ -11,5 +11,6 @@ type Resolver interface {
|
||||
|
||||
// A resolved record
|
||||
type Record struct {
|
||||
Address string `json:"address"`
|
||||
Address string `json:"address"`
|
||||
Priority int64 `json:"priority"`
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
var records []*resolver.Record
|
||||
records := make([]*resolver.Record, 0, len(r.Nodes))
|
||||
|
||||
for _, node := range r.Nodes {
|
||||
records = append(records, &resolver.Record{
|
||||
|
@@ -171,7 +171,7 @@ func (n *Network) Routes(ctx context.Context, req *pbNet.RoutesRequest, resp *pb
|
||||
return errors.InternalServerError("go.micro.network", "failed to list routes: %s", err)
|
||||
}
|
||||
|
||||
var respRoutes []*pbRtr.Route
|
||||
respRoutes := make([]*pbRtr.Route, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
respRoute := &pbRtr.Route{
|
||||
Service: route.Service,
|
||||
|
12
options.go
12
options.go
@@ -31,6 +31,8 @@ type Options struct {
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
|
||||
Signal bool
|
||||
}
|
||||
|
||||
func newOptions(opts ...Option) Options {
|
||||
@@ -42,6 +44,7 @@ func newOptions(opts ...Option) Options {
|
||||
Registry: registry.DefaultRegistry,
|
||||
Transport: transport.DefaultTransport,
|
||||
Context: context.Background(),
|
||||
Signal: true,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
@@ -81,6 +84,15 @@ func Context(ctx context.Context) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSignal toggles automatic installation of the signal handler that
|
||||
// traps TERM, INT, and QUIT. Users of this feature to disable the signal
|
||||
// handler, should control liveness of the service through the context.
|
||||
func HandleSignal(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.Signal = b
|
||||
}
|
||||
}
|
||||
|
||||
func Server(s server.Server) Option {
|
||||
return func(o *Options) {
|
||||
o.Server = s
|
||||
|
@@ -121,5 +121,7 @@ func (p *plugin) Build(path string, c *Config) error {
|
||||
return fmt.Errorf("Failed to create dir %s: %v", filepath.Dir(path), err)
|
||||
}
|
||||
cmd := exec.Command("go", "build", "-buildmode=plugin", "-o", path+".so", goFile)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/micro/go-micro/client/grpc"
|
||||
"github.com/micro/go-micro/codec"
|
||||
"github.com/micro/go-micro/config/options"
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/proxy"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
@@ -62,8 +61,14 @@ func readLoop(r server.Request, s client.Stream) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
|
||||
return errors.InternalServerError("go.micro.proxy.grpc", "SendRequest is unsupported")
|
||||
// ProcessMessage acts as a message exchange and forwards messages to ongoing topics
|
||||
// TODO: should we look at p.Endpoint and only send to the local endpoint? probably
|
||||
func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error {
|
||||
// TODO: check that we're not broadcast storming by sending to the same topic
|
||||
// that we're actually subscribed to
|
||||
|
||||
// directly publish to the local client
|
||||
return p.Client.Publish(ctx, msg)
|
||||
}
|
||||
|
||||
// ServeRequest honours the server.Proxy interface
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/config/options"
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/proxy"
|
||||
@@ -45,8 +44,69 @@ func getEndpoint(hdr map[string]string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
|
||||
return errors.InternalServerError("go.micro.proxy.http", "SendRequest is unsupported")
|
||||
func getTopic(hdr map[string]string) string {
|
||||
ep := hdr["Micro-Topic"]
|
||||
if len(ep) > 0 && ep[0] == '/' {
|
||||
return ep
|
||||
}
|
||||
return "/" + hdr["Micro-Topic"]
|
||||
}
|
||||
|
||||
// ProcessMessage handles incoming asynchronous messages
|
||||
func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error {
|
||||
if p.Endpoint == "" {
|
||||
p.Endpoint = proxy.DefaultEndpoint
|
||||
}
|
||||
|
||||
// get the header
|
||||
hdr := msg.Header()
|
||||
|
||||
// get topic
|
||||
// use /topic as endpoint
|
||||
endpoint := getTopic(hdr)
|
||||
|
||||
// set the endpoint
|
||||
if len(endpoint) == 0 {
|
||||
endpoint = p.Endpoint
|
||||
} else {
|
||||
// add endpoint to backend
|
||||
u, err := url.Parse(p.Endpoint)
|
||||
if err != nil {
|
||||
return errors.InternalServerError(msg.Topic(), err.Error())
|
||||
}
|
||||
u.Path = path.Join(u.Path, endpoint)
|
||||
endpoint = u.String()
|
||||
}
|
||||
|
||||
// send to backend
|
||||
hreq, err := http.NewRequest("POST", endpoint, bytes.NewReader(msg.Body()))
|
||||
if err != nil {
|
||||
return errors.InternalServerError(msg.Topic(), err.Error())
|
||||
}
|
||||
|
||||
// set the headers
|
||||
for k, v := range hdr {
|
||||
hreq.Header.Set(k, v)
|
||||
}
|
||||
|
||||
// make the call
|
||||
hrsp, err := http.DefaultClient.Do(hreq)
|
||||
if err != nil {
|
||||
return errors.InternalServerError(msg.Topic(), err.Error())
|
||||
}
|
||||
|
||||
// read body
|
||||
b, err := ioutil.ReadAll(hrsp.Body)
|
||||
hrsp.Body.Close()
|
||||
if err != nil {
|
||||
return errors.InternalServerError(msg.Topic(), err.Error())
|
||||
}
|
||||
|
||||
if hrsp.StatusCode != 200 {
|
||||
return errors.New(msg.Topic(), string(b), int32(hrsp.StatusCode))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServeRequest honours the server.Router interface
|
||||
@@ -113,7 +173,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
|
||||
|
||||
// set response headers
|
||||
hdr = map[string]string{}
|
||||
for k, _ := range hrsp.Header {
|
||||
for k := range hrsp.Header {
|
||||
hdr[k] = hrsp.Header.Get(k)
|
||||
}
|
||||
// write the header
|
||||
|
@@ -11,13 +11,16 @@ import (
|
||||
"time"
|
||||
|
||||
"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/bytes"
|
||||
"github.com/micro/go-micro/config/options"
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/metadata"
|
||||
"github.com/micro/go-micro/proxy"
|
||||
"github.com/micro/go-micro/router"
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
// Proxy will transparently proxy requests to an endpoint.
|
||||
@@ -41,9 +44,6 @@ type Proxy struct {
|
||||
// A fib of routes service:address
|
||||
sync.RWMutex
|
||||
Routes map[string]map[uint64]router.Route
|
||||
|
||||
// The channel to monitor watcher errors
|
||||
errChan chan error
|
||||
}
|
||||
|
||||
// read client request and write to server
|
||||
@@ -94,6 +94,75 @@ func toNodes(routes []router.Route) []string {
|
||||
return nodes
|
||||
}
|
||||
|
||||
func toSlice(r map[uint64]router.Route) []router.Route {
|
||||
routes := make([]router.Route, 0, len(r))
|
||||
for _, v := range r {
|
||||
routes = append(routes, v)
|
||||
}
|
||||
|
||||
// sort the routes in order of metric
|
||||
sort.Slice(routes, func(i, j int) bool { return routes[i].Metric < routes[j].Metric })
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
func (p *Proxy) filterRoutes(ctx context.Context, routes []router.Route) []router.Route {
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
if !ok {
|
||||
return routes
|
||||
}
|
||||
|
||||
var filteredRoutes []router.Route
|
||||
|
||||
// filter the routes based on our headers
|
||||
for _, route := range routes {
|
||||
// process only routes for this id
|
||||
if id := md["Micro-Router"]; len(id) > 0 {
|
||||
if route.Router != id {
|
||||
// skip routes that don't mwatch
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// only process routes with this network
|
||||
if net := md["Micro-Network"]; len(net) > 0 {
|
||||
if route.Network != net {
|
||||
// skip routes that don't mwatch
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// process only this gateway
|
||||
if gw := md["Micro-Gateway"]; len(gw) > 0 {
|
||||
// if the gateway matches our address
|
||||
// special case, take the routes with no gateway
|
||||
// TODO: should we strip the gateway from the context?
|
||||
if gw == p.Router.Options().Address {
|
||||
if len(route.Gateway) > 0 && route.Gateway != gw {
|
||||
continue
|
||||
}
|
||||
// otherwise its a local route and we're keeping it
|
||||
} else {
|
||||
// gateway does not match our own
|
||||
if route.Gateway != gw {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: address based filtering
|
||||
// address := md["Micro-Address"]
|
||||
|
||||
// TODO: label based filtering
|
||||
// requires new field in routing table : route.Labels
|
||||
|
||||
// passed the filter checks
|
||||
filteredRoutes = append(filteredRoutes, route)
|
||||
}
|
||||
|
||||
return filteredRoutes
|
||||
}
|
||||
|
||||
func (p *Proxy) getLink(r router.Route) (client.Client, error) {
|
||||
if r.Link == "local" || len(p.Links) == 0 {
|
||||
return p.Client, nil
|
||||
@@ -105,28 +174,27 @@ func (p *Proxy) getLink(r router.Route) (client.Client, error) {
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (p *Proxy) getRoute(service string) ([]router.Route, error) {
|
||||
toSlice := func(r map[uint64]router.Route) []router.Route {
|
||||
var routes []router.Route
|
||||
for _, v := range r {
|
||||
routes = append(routes, v)
|
||||
}
|
||||
|
||||
// sort the routes in order of metric
|
||||
sort.Slice(routes, func(i, j int) bool { return routes[i].Metric < routes[j].Metric })
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
func (p *Proxy) getRoute(ctx context.Context, service string) ([]router.Route, error) {
|
||||
// lookup the route cache first
|
||||
p.Lock()
|
||||
routes, ok := p.Routes[service]
|
||||
cached, ok := p.Routes[service]
|
||||
if ok {
|
||||
p.Unlock()
|
||||
return toSlice(routes), nil
|
||||
routes := toSlice(cached)
|
||||
return p.filterRoutes(ctx, routes), nil
|
||||
}
|
||||
p.Unlock()
|
||||
|
||||
// cache routes for the service
|
||||
routes, err := p.cacheRoutes(service)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.filterRoutes(ctx, routes), nil
|
||||
}
|
||||
|
||||
func (p *Proxy) cacheRoutes(service string) ([]router.Route, error) {
|
||||
// lookup the routes in the router
|
||||
results, err := p.Router.Lookup(router.QueryService(service))
|
||||
if err != nil {
|
||||
@@ -147,18 +215,40 @@ func (p *Proxy) getRoute(service string) ([]router.Route, error) {
|
||||
}
|
||||
p.Routes[service][route.Hash()] = route
|
||||
}
|
||||
routes = p.Routes[service]
|
||||
routes := p.Routes[service]
|
||||
p.Unlock()
|
||||
|
||||
return toSlice(routes), nil
|
||||
}
|
||||
|
||||
// manageRouteCache applies action on a given route to Proxy route cache
|
||||
func (p *Proxy) manageRouteCache(route router.Route, action string) error {
|
||||
// refreshMetrics will refresh any metrics for our local cached routes.
|
||||
// we may not receive new watch events for these as they change.
|
||||
func (p *Proxy) refreshMetrics() {
|
||||
services := make([]string, 0, len(p.Routes))
|
||||
|
||||
// get a list of services to update
|
||||
p.RLock()
|
||||
for service := range p.Routes {
|
||||
services = append(services, service)
|
||||
}
|
||||
p.RUnlock()
|
||||
|
||||
// get and cache the routes for the service
|
||||
for _, service := range services {
|
||||
p.cacheRoutes(service)
|
||||
}
|
||||
}
|
||||
|
||||
// manageRoutes applies action on a given route to Proxy route cache
|
||||
func (p *Proxy) manageRoutes(route router.Route, action string) error {
|
||||
// we only cache what we are actually concerned with
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
switch action {
|
||||
case "create", "update":
|
||||
if _, ok := p.Routes[route.Service]; !ok {
|
||||
p.Routes[route.Service] = make(map[uint64]router.Route)
|
||||
return fmt.Errorf("not called %s", route.Service)
|
||||
}
|
||||
p.Routes[route.Service][route.Hash()] = route
|
||||
case "delete":
|
||||
@@ -172,36 +262,53 @@ func (p *Proxy) manageRouteCache(route router.Route, action string) error {
|
||||
|
||||
// watchRoutes watches service routes and updates proxy cache
|
||||
func (p *Proxy) watchRoutes() {
|
||||
// this is safe to do as the only way watchRoutes returns is
|
||||
// when some error is written into error channel - we want to bail then
|
||||
defer close(p.errChan)
|
||||
|
||||
// route watcher
|
||||
w, err := p.Router.Watch()
|
||||
if err != nil {
|
||||
p.errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
event, err := w.Next()
|
||||
if err != nil {
|
||||
p.errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
p.Lock()
|
||||
if err := p.manageRouteCache(event.Route, fmt.Sprintf("%s", event.Type)); err != nil {
|
||||
if err := p.manageRoutes(event.Route, fmt.Sprintf("%s", event.Type)); err != nil {
|
||||
// TODO: should we bail here?
|
||||
p.Unlock()
|
||||
continue
|
||||
}
|
||||
p.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
|
||||
return errors.InternalServerError("go.micro.proxy", "SendRequest is unsupported")
|
||||
// ProcessMessage acts as a message exchange and forwards messages to ongoing topics
|
||||
// TODO: should we look at p.Endpoint and only send to the local endpoint? probably
|
||||
func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error {
|
||||
// TODO: check that we're not broadcast storming by sending to the same topic
|
||||
// that we're actually subscribed to
|
||||
|
||||
log.Tracef("Received message for %s", msg.Topic())
|
||||
|
||||
var errors []string
|
||||
|
||||
// directly publish to the local client
|
||||
if err := p.Client.Publish(ctx, msg); err != nil {
|
||||
errors = append(errors, err.Error())
|
||||
}
|
||||
|
||||
// publish to all links
|
||||
for _, client := range p.Links {
|
||||
if err := client.Publish(ctx, msg); err != nil {
|
||||
errors = append(errors, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// there is no error...muahaha
|
||||
return fmt.Errorf("Message processing error: %s", strings.Join(errors, "\n"))
|
||||
}
|
||||
|
||||
// ServeRequest honours the server.Router interface
|
||||
@@ -221,6 +328,8 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
|
||||
return errors.BadRequest("go.micro.proxy", "service name is blank")
|
||||
}
|
||||
|
||||
log.Tracef("Received request for %s", service)
|
||||
|
||||
// are we network routing or local routing
|
||||
if len(p.Links) == 0 {
|
||||
local = true
|
||||
@@ -233,7 +342,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
|
||||
addresses = []string{p.Endpoint}
|
||||
} else {
|
||||
// get route for endpoint from router
|
||||
addr, err := p.getRoute(p.Endpoint)
|
||||
addr, err := p.getRoute(ctx, p.Endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -245,18 +354,25 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
|
||||
} else {
|
||||
// no endpoint was specified just lookup the route
|
||||
// get route for endpoint from router
|
||||
addr, err := p.getRoute(service)
|
||||
addr, err := p.getRoute(ctx, service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
routes = addr
|
||||
}
|
||||
|
||||
var opts []client.CallOption
|
||||
|
||||
// set strategy to round robin
|
||||
opts = append(opts, client.WithSelectOption(selector.WithStrategy(selector.RoundRobin)))
|
||||
|
||||
// if the address is already set just serve it
|
||||
// TODO: figure it out if we should know to pick a link
|
||||
if len(addresses) > 0 {
|
||||
opts = append(opts, client.WithAddress(addresses...))
|
||||
|
||||
// serve the normal way
|
||||
return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, client.WithAddress(addresses...))
|
||||
return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...)
|
||||
}
|
||||
|
||||
// there's no links e.g we're local routing then just serve it with addresses
|
||||
@@ -286,11 +402,14 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Proxy using route %+v\n", route)
|
||||
|
||||
// set the address to call
|
||||
addresses := toNodes([]router.Route{route})
|
||||
opts = append(opts, client.WithAddress(addresses...))
|
||||
|
||||
// do the request with the link
|
||||
gerr = p.serveRequest(ctx, link, service, endpoint, req, rsp, client.WithAddress(addresses...))
|
||||
gerr = p.serveRequest(ctx, link, service, endpoint, req, rsp, opts...)
|
||||
// return on no error since we succeeded
|
||||
if gerr == nil {
|
||||
return nil
|
||||
@@ -348,39 +467,28 @@ func (p *Proxy) serveRequest(ctx context.Context, link client.Client, service, e
|
||||
// get raw response
|
||||
resp := stream.Response()
|
||||
|
||||
// route watcher error
|
||||
var watchErr error
|
||||
|
||||
// create server response write loop
|
||||
for {
|
||||
select {
|
||||
case err := <-p.errChan:
|
||||
if err != nil {
|
||||
watchErr = err
|
||||
}
|
||||
return watchErr
|
||||
default:
|
||||
// read backend response body
|
||||
body, err := resp.Read()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
// read backend response body
|
||||
body, err := resp.Read()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// read backend response header
|
||||
hdr := resp.Header()
|
||||
// read backend response header
|
||||
hdr := resp.Header()
|
||||
|
||||
// write raw response header to client
|
||||
rsp.WriteHeader(hdr)
|
||||
// write raw response header to client
|
||||
rsp.WriteHeader(hdr)
|
||||
|
||||
// write raw response body to client
|
||||
err = rsp.Write(body)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
// write raw response body to client
|
||||
err = rsp.Write(body)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,9 +547,6 @@ func NewProxy(opts ...options.Option) proxy.Proxy {
|
||||
// routes cache
|
||||
p.Routes = make(map[string]map[uint64]router.Route)
|
||||
|
||||
// watch router service routes
|
||||
p.errChan = make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
// continuously attempt to watch routes
|
||||
for {
|
||||
@@ -452,5 +557,19 @@ func NewProxy(opts ...options.Option) proxy.Proxy {
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
t := time.NewTicker(time.Minute)
|
||||
defer t.Stop()
|
||||
|
||||
// we must refresh route metrics since they do not trigger new events
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
// refresh route metrics
|
||||
p.refreshMetrics()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return p
|
||||
}
|
||||
|
@@ -13,9 +13,9 @@ import (
|
||||
// Proxy can be used as a proxy server for go-micro services
|
||||
type Proxy interface {
|
||||
options.Options
|
||||
// SendRequest honours the client.Router interface
|
||||
SendRequest(context.Context, client.Request, client.Response) error
|
||||
// ServeRequest honours the server.Router interface
|
||||
// ProcessMessage handles inbound messages
|
||||
ProcessMessage(context.Context, server.Message) error
|
||||
// ServeRequest handles inbound requests
|
||||
ServeRequest(context.Context, server.Request, server.Response) error
|
||||
}
|
||||
|
||||
|
46
registry/cache/cache.go
vendored
46
registry/cache/cache.go
vendored
@@ -39,6 +39,8 @@ type cache struct {
|
||||
// used to stop the cache
|
||||
exit chan bool
|
||||
|
||||
// indicate whether its running
|
||||
running bool
|
||||
// status of the registry
|
||||
// used to hold onto the cache
|
||||
// in failure state
|
||||
@@ -81,7 +83,7 @@ func (c *cache) isValid(services []*registry.Service, ttl time.Time) bool {
|
||||
}
|
||||
|
||||
// time since ttl is longer than timeout
|
||||
if time.Since(ttl) > c.opts.TTL {
|
||||
if time.Since(ttl) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -157,13 +159,26 @@ func (c *cache) get(service string) ([]*registry.Service, error) {
|
||||
}
|
||||
|
||||
// watch service if not watched
|
||||
if _, ok := c.watched[service]; !ok {
|
||||
go c.run(service)
|
||||
}
|
||||
_, ok := c.watched[service]
|
||||
|
||||
// unlock the read lock
|
||||
c.RUnlock()
|
||||
|
||||
// check if its being watched
|
||||
if !ok {
|
||||
c.Lock()
|
||||
|
||||
// set to watched
|
||||
c.watched[service] = true
|
||||
|
||||
// only kick it off if not running
|
||||
if !c.running {
|
||||
go c.run()
|
||||
}
|
||||
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
// get and return services
|
||||
return get(service, cp)
|
||||
}
|
||||
@@ -181,6 +196,11 @@ func (c *cache) update(res *registry.Result) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
// only save watched services
|
||||
if _, ok := c.watched[res.Service.Name]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
services, ok := c.cache[res.Service.Name]
|
||||
if !ok {
|
||||
// we're not going to cache anything
|
||||
@@ -283,16 +303,16 @@ func (c *cache) update(res *registry.Result) {
|
||||
|
||||
// run starts the cache watcher loop
|
||||
// it creates a new watcher if there's a problem
|
||||
func (c *cache) run(service string) {
|
||||
// set watcher
|
||||
func (c *cache) run() {
|
||||
c.Lock()
|
||||
c.watched[service] = true
|
||||
c.running = true
|
||||
c.Unlock()
|
||||
|
||||
// delete watcher on exit
|
||||
// reset watcher on exit
|
||||
defer func() {
|
||||
c.Lock()
|
||||
delete(c.watched, service)
|
||||
c.watched = make(map[string]bool)
|
||||
c.running = false
|
||||
c.Unlock()
|
||||
}()
|
||||
|
||||
@@ -309,10 +329,7 @@ func (c *cache) run(service string) {
|
||||
time.Sleep(time.Duration(j) * time.Millisecond)
|
||||
|
||||
// create new watcher
|
||||
w, err := c.Registry.Watch(
|
||||
registry.WatchService(service),
|
||||
)
|
||||
|
||||
w, err := c.Registry.Watch()
|
||||
if err != nil {
|
||||
if c.quit() {
|
||||
return
|
||||
@@ -414,6 +431,9 @@ func (c *cache) GetService(service string) ([]*registry.Service, error) {
|
||||
}
|
||||
|
||||
func (c *cache) Stop() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
select {
|
||||
case <-c.exit:
|
||||
return
|
||||
|
@@ -1,438 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
"github.com/micro/go-micro/registry"
|
||||
mnet "github.com/micro/go-micro/util/net"
|
||||
hash "github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
type consulRegistry struct {
|
||||
Address []string
|
||||
opts registry.Options
|
||||
|
||||
client *consul.Client
|
||||
config *consul.Config
|
||||
|
||||
// connect enabled
|
||||
connect bool
|
||||
|
||||
queryOptions *consul.QueryOptions
|
||||
|
||||
sync.Mutex
|
||||
register map[string]uint64
|
||||
// lastChecked tracks when a node was last checked as existing in Consul
|
||||
lastChecked map[string]time.Time
|
||||
}
|
||||
|
||||
func getDeregisterTTL(t time.Duration) time.Duration {
|
||||
// splay slightly for the watcher?
|
||||
splay := time.Second * 5
|
||||
deregTTL := t + splay
|
||||
|
||||
// consul has a minimum timeout on deregistration of 1 minute.
|
||||
if t < time.Minute {
|
||||
deregTTL = time.Minute + splay
|
||||
}
|
||||
|
||||
return deregTTL
|
||||
}
|
||||
|
||||
func newTransport(config *tls.Config) *http.Transport {
|
||||
if config == nil {
|
||||
config = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
}
|
||||
|
||||
t := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
TLSClientConfig: config,
|
||||
}
|
||||
runtime.SetFinalizer(&t, func(tr **http.Transport) {
|
||||
(*tr).CloseIdleConnections()
|
||||
})
|
||||
return t
|
||||
}
|
||||
|
||||
func configure(c *consulRegistry, opts ...registry.Option) {
|
||||
// set opts
|
||||
for _, o := range opts {
|
||||
o(&c.opts)
|
||||
}
|
||||
|
||||
// use default config
|
||||
config := consul.DefaultConfig()
|
||||
|
||||
if c.opts.Context != nil {
|
||||
// Use the consul config passed in the options, if available
|
||||
if co, ok := c.opts.Context.Value("consul_config").(*consul.Config); ok {
|
||||
config = co
|
||||
}
|
||||
if cn, ok := c.opts.Context.Value("consul_connect").(bool); ok {
|
||||
c.connect = cn
|
||||
}
|
||||
|
||||
// Use the consul query options passed in the options, if available
|
||||
if qo, ok := c.opts.Context.Value("consul_query_options").(*consul.QueryOptions); ok && qo != nil {
|
||||
c.queryOptions = qo
|
||||
}
|
||||
if as, ok := c.opts.Context.Value("consul_allow_stale").(bool); ok {
|
||||
c.queryOptions.AllowStale = as
|
||||
}
|
||||
}
|
||||
|
||||
// check if there are any addrs
|
||||
var addrs []string
|
||||
|
||||
// iterate the options addresses
|
||||
for _, address := range c.opts.Addrs {
|
||||
// check we have a port
|
||||
addr, port, err := net.SplitHostPort(address)
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "8500"
|
||||
addr = address
|
||||
addrs = append(addrs, net.JoinHostPort(addr, port))
|
||||
} else if err == nil {
|
||||
addrs = append(addrs, net.JoinHostPort(addr, port))
|
||||
}
|
||||
}
|
||||
|
||||
// set the addrs
|
||||
if len(addrs) > 0 {
|
||||
c.Address = addrs
|
||||
config.Address = c.Address[0]
|
||||
}
|
||||
|
||||
if config.HttpClient == nil {
|
||||
config.HttpClient = new(http.Client)
|
||||
}
|
||||
|
||||
// requires secure connection?
|
||||
if c.opts.Secure || c.opts.TLSConfig != nil {
|
||||
config.Scheme = "https"
|
||||
// We're going to support InsecureSkipVerify
|
||||
config.HttpClient.Transport = newTransport(c.opts.TLSConfig)
|
||||
}
|
||||
|
||||
// set timeout
|
||||
if c.opts.Timeout > 0 {
|
||||
config.HttpClient.Timeout = c.opts.Timeout
|
||||
}
|
||||
|
||||
// set the config
|
||||
c.config = config
|
||||
|
||||
// remove client
|
||||
c.client = nil
|
||||
|
||||
// setup the client
|
||||
c.Client()
|
||||
}
|
||||
|
||||
func (c *consulRegistry) Init(opts ...registry.Option) error {
|
||||
configure(c, opts...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *consulRegistry) Deregister(s *registry.Service) error {
|
||||
if len(s.Nodes) == 0 {
|
||||
return errors.New("Require at least one node")
|
||||
}
|
||||
|
||||
// delete our hash and time check of the service
|
||||
c.Lock()
|
||||
delete(c.register, s.Name)
|
||||
delete(c.lastChecked, s.Name)
|
||||
c.Unlock()
|
||||
|
||||
node := s.Nodes[0]
|
||||
return c.Client().Agent().ServiceDeregister(node.Id)
|
||||
}
|
||||
|
||||
func (c *consulRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
|
||||
if len(s.Nodes) == 0 {
|
||||
return errors.New("Require at least one node")
|
||||
}
|
||||
|
||||
var regTCPCheck bool
|
||||
var regInterval time.Duration
|
||||
|
||||
var options registry.RegisterOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
if c.opts.Context != nil {
|
||||
if tcpCheckInterval, ok := c.opts.Context.Value("consul_tcp_check").(time.Duration); ok {
|
||||
regTCPCheck = true
|
||||
regInterval = tcpCheckInterval
|
||||
}
|
||||
}
|
||||
|
||||
// create hash of service; uint64
|
||||
h, err := hash.Hash(s, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// use first node
|
||||
node := s.Nodes[0]
|
||||
|
||||
// get existing hash and last checked time
|
||||
c.Lock()
|
||||
v, ok := c.register[s.Name]
|
||||
lastChecked := c.lastChecked[s.Name]
|
||||
c.Unlock()
|
||||
|
||||
// if it's already registered and matches then just pass the check
|
||||
if ok && v == h {
|
||||
if options.TTL == time.Duration(0) {
|
||||
// ensure that our service hasn't been deregistered by Consul
|
||||
if time.Since(lastChecked) <= getDeregisterTTL(regInterval) {
|
||||
return nil
|
||||
}
|
||||
services, _, err := c.Client().Health().Checks(s.Name, c.queryOptions)
|
||||
if err == nil {
|
||||
for _, v := range services {
|
||||
if v.ServiceID == node.Id {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if the err is nil we're all good, bail out
|
||||
// if not, we don't know what the state is, so full re-register
|
||||
if err := c.Client().Agent().PassTTL("service:"+node.Id, ""); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// encode the tags
|
||||
tags := encodeMetadata(node.Metadata)
|
||||
tags = append(tags, encodeEndpoints(s.Endpoints)...)
|
||||
tags = append(tags, encodeVersion(s.Version)...)
|
||||
|
||||
var check *consul.AgentServiceCheck
|
||||
|
||||
if regTCPCheck {
|
||||
deregTTL := getDeregisterTTL(regInterval)
|
||||
|
||||
check = &consul.AgentServiceCheck{
|
||||
TCP: node.Address,
|
||||
Interval: fmt.Sprintf("%v", regInterval),
|
||||
DeregisterCriticalServiceAfter: fmt.Sprintf("%v", deregTTL),
|
||||
}
|
||||
|
||||
// if the TTL is greater than 0 create an associated check
|
||||
} else if options.TTL > time.Duration(0) {
|
||||
deregTTL := getDeregisterTTL(options.TTL)
|
||||
|
||||
check = &consul.AgentServiceCheck{
|
||||
TTL: fmt.Sprintf("%v", options.TTL),
|
||||
DeregisterCriticalServiceAfter: fmt.Sprintf("%v", deregTTL),
|
||||
}
|
||||
}
|
||||
|
||||
host, pt, _ := net.SplitHostPort(node.Address)
|
||||
if host == "" {
|
||||
host = node.Address
|
||||
}
|
||||
port, _ := strconv.Atoi(pt)
|
||||
|
||||
// register the service
|
||||
asr := &consul.AgentServiceRegistration{
|
||||
ID: node.Id,
|
||||
Name: s.Name,
|
||||
Tags: tags,
|
||||
Port: port,
|
||||
Address: host,
|
||||
Check: check,
|
||||
}
|
||||
|
||||
// Specify consul connect
|
||||
if c.connect {
|
||||
asr.Connect = &consul.AgentServiceConnect{
|
||||
Native: true,
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.Client().Agent().ServiceRegister(asr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save our hash and time check of the service
|
||||
c.Lock()
|
||||
c.register[s.Name] = h
|
||||
c.lastChecked[s.Name] = time.Now()
|
||||
c.Unlock()
|
||||
|
||||
// if the TTL is 0 we don't mess with the checks
|
||||
if options.TTL == time.Duration(0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// pass the healthcheck
|
||||
return c.Client().Agent().PassTTL("service:"+node.Id, "")
|
||||
}
|
||||
|
||||
func (c *consulRegistry) GetService(name string) ([]*registry.Service, error) {
|
||||
var rsp []*consul.ServiceEntry
|
||||
var err error
|
||||
|
||||
// if we're connect enabled only get connect services
|
||||
if c.connect {
|
||||
rsp, _, err = c.Client().Health().Connect(name, "", false, c.queryOptions)
|
||||
} else {
|
||||
rsp, _, err = c.Client().Health().Service(name, "", false, c.queryOptions)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serviceMap := map[string]*registry.Service{}
|
||||
|
||||
for _, s := range rsp {
|
||||
if s.Service.Service != name {
|
||||
continue
|
||||
}
|
||||
|
||||
// version is now a tag
|
||||
version, _ := decodeVersion(s.Service.Tags)
|
||||
// service ID is now the node id
|
||||
id := s.Service.ID
|
||||
// key is always the version
|
||||
key := version
|
||||
|
||||
// address is service address
|
||||
address := s.Service.Address
|
||||
|
||||
// use node address
|
||||
if len(address) == 0 {
|
||||
address = s.Node.Address
|
||||
}
|
||||
|
||||
svc, ok := serviceMap[key]
|
||||
if !ok {
|
||||
svc = ®istry.Service{
|
||||
Endpoints: decodeEndpoints(s.Service.Tags),
|
||||
Name: s.Service.Service,
|
||||
Version: version,
|
||||
}
|
||||
serviceMap[key] = svc
|
||||
}
|
||||
|
||||
var del bool
|
||||
|
||||
for _, check := range s.Checks {
|
||||
// delete the node if the status is critical
|
||||
if check.Status == "critical" {
|
||||
del = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if delete then skip the node
|
||||
if del {
|
||||
continue
|
||||
}
|
||||
|
||||
svc.Nodes = append(svc.Nodes, ®istry.Node{
|
||||
Id: id,
|
||||
Address: mnet.HostPort(address, s.Service.Port),
|
||||
Metadata: decodeMetadata(s.Service.Tags),
|
||||
})
|
||||
}
|
||||
|
||||
var services []*registry.Service
|
||||
for _, service := range serviceMap {
|
||||
services = append(services, service)
|
||||
}
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func (c *consulRegistry) ListServices() ([]*registry.Service, error) {
|
||||
rsp, _, err := c.Client().Catalog().Services(c.queryOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var services []*registry.Service
|
||||
|
||||
for service := range rsp {
|
||||
services = append(services, ®istry.Service{Name: service})
|
||||
}
|
||||
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func (c *consulRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
|
||||
return newConsulWatcher(c, opts...)
|
||||
}
|
||||
|
||||
func (c *consulRegistry) String() string {
|
||||
return "consul"
|
||||
}
|
||||
|
||||
func (c *consulRegistry) Options() registry.Options {
|
||||
return c.opts
|
||||
}
|
||||
|
||||
func (c *consulRegistry) Client() *consul.Client {
|
||||
if c.client != nil {
|
||||
return c.client
|
||||
}
|
||||
|
||||
for _, addr := range c.Address {
|
||||
// set the address
|
||||
c.config.Address = addr
|
||||
|
||||
// create a new client
|
||||
tmpClient, _ := consul.NewClient(c.config)
|
||||
|
||||
// test the client
|
||||
_, err := tmpClient.Agent().Host()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// set the client
|
||||
c.client = tmpClient
|
||||
return c.client
|
||||
}
|
||||
|
||||
// set the default
|
||||
c.client, _ = consul.NewClient(c.config)
|
||||
|
||||
// return the client
|
||||
return c.client
|
||||
}
|
||||
|
||||
func NewRegistry(opts ...registry.Option) registry.Registry {
|
||||
cr := &consulRegistry{
|
||||
opts: registry.Options{},
|
||||
register: make(map[string]uint64),
|
||||
lastChecked: make(map[string]time.Time),
|
||||
queryOptions: &consul.QueryOptions{
|
||||
AllowStale: true,
|
||||
},
|
||||
}
|
||||
configure(cr, opts...)
|
||||
return cr
|
||||
}
|
@@ -1,170 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
func encode(buf []byte) string {
|
||||
var b bytes.Buffer
|
||||
defer b.Reset()
|
||||
|
||||
w := zlib.NewWriter(&b)
|
||||
if _, err := w.Write(buf); err != nil {
|
||||
return ""
|
||||
}
|
||||
w.Close()
|
||||
|
||||
return hex.EncodeToString(b.Bytes())
|
||||
}
|
||||
|
||||
func decode(d string) []byte {
|
||||
hr, err := hex.DecodeString(d)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
br := bytes.NewReader(hr)
|
||||
zr, err := zlib.NewReader(br)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
rbuf, err := ioutil.ReadAll(zr)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return rbuf
|
||||
}
|
||||
|
||||
func encodeEndpoints(en []*registry.Endpoint) []string {
|
||||
var tags []string
|
||||
for _, e := range en {
|
||||
if b, err := json.Marshal(e); err == nil {
|
||||
tags = append(tags, "e-"+encode(b))
|
||||
}
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func decodeEndpoints(tags []string) []*registry.Endpoint {
|
||||
var en []*registry.Endpoint
|
||||
|
||||
// use the first format you find
|
||||
var ver byte
|
||||
|
||||
for _, tag := range tags {
|
||||
if len(tag) == 0 || tag[0] != 'e' {
|
||||
continue
|
||||
}
|
||||
|
||||
// check version
|
||||
if ver > 0 && tag[1] != ver {
|
||||
continue
|
||||
}
|
||||
|
||||
var e *registry.Endpoint
|
||||
var buf []byte
|
||||
|
||||
// Old encoding was plain
|
||||
if tag[1] == '=' {
|
||||
buf = []byte(tag[2:])
|
||||
}
|
||||
|
||||
// New encoding is hex
|
||||
if tag[1] == '-' {
|
||||
buf = decode(tag[2:])
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(buf, &e); err == nil {
|
||||
en = append(en, e)
|
||||
}
|
||||
|
||||
// set version
|
||||
ver = tag[1]
|
||||
}
|
||||
return en
|
||||
}
|
||||
|
||||
func encodeMetadata(md map[string]string) []string {
|
||||
var tags []string
|
||||
for k, v := range md {
|
||||
if b, err := json.Marshal(map[string]string{
|
||||
k: v,
|
||||
}); err == nil {
|
||||
// new encoding
|
||||
tags = append(tags, "t-"+encode(b))
|
||||
}
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func decodeMetadata(tags []string) map[string]string {
|
||||
md := make(map[string]string)
|
||||
|
||||
var ver byte
|
||||
|
||||
for _, tag := range tags {
|
||||
if len(tag) == 0 || tag[0] != 't' {
|
||||
continue
|
||||
}
|
||||
|
||||
// check version
|
||||
if ver > 0 && tag[1] != ver {
|
||||
continue
|
||||
}
|
||||
|
||||
var kv map[string]string
|
||||
var buf []byte
|
||||
|
||||
// Old encoding was plain
|
||||
if tag[1] == '=' {
|
||||
buf = []byte(tag[2:])
|
||||
}
|
||||
|
||||
// New encoding is hex
|
||||
if tag[1] == '-' {
|
||||
buf = decode(tag[2:])
|
||||
}
|
||||
|
||||
// Now unmarshal
|
||||
if err := json.Unmarshal(buf, &kv); err == nil {
|
||||
for k, v := range kv {
|
||||
md[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// set version
|
||||
ver = tag[1]
|
||||
}
|
||||
return md
|
||||
}
|
||||
|
||||
func encodeVersion(v string) []string {
|
||||
return []string{"v-" + encode([]byte(v))}
|
||||
}
|
||||
|
||||
func decodeVersion(tags []string) (string, bool) {
|
||||
for _, tag := range tags {
|
||||
if len(tag) < 2 || tag[0] != 'v' {
|
||||
continue
|
||||
}
|
||||
|
||||
// Old encoding was plain
|
||||
if tag[1] == '=' {
|
||||
return tag[2:], true
|
||||
}
|
||||
|
||||
// New encoding is hex
|
||||
if tag[1] == '-' {
|
||||
return string(decode(tag[2:])), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
@@ -1,147 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
func TestEncodingEndpoints(t *testing.T) {
|
||||
eps := []*registry.Endpoint{
|
||||
®istry.Endpoint{
|
||||
Name: "endpoint1",
|
||||
Request: ®istry.Value{
|
||||
Name: "request",
|
||||
Type: "request",
|
||||
},
|
||||
Response: ®istry.Value{
|
||||
Name: "response",
|
||||
Type: "response",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"foo1": "bar1",
|
||||
},
|
||||
},
|
||||
®istry.Endpoint{
|
||||
Name: "endpoint2",
|
||||
Request: ®istry.Value{
|
||||
Name: "request",
|
||||
Type: "request",
|
||||
},
|
||||
Response: ®istry.Value{
|
||||
Name: "response",
|
||||
Type: "response",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"foo2": "bar2",
|
||||
},
|
||||
},
|
||||
®istry.Endpoint{
|
||||
Name: "endpoint3",
|
||||
Request: ®istry.Value{
|
||||
Name: "request",
|
||||
Type: "request",
|
||||
},
|
||||
Response: ®istry.Value{
|
||||
Name: "response",
|
||||
Type: "response",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"foo3": "bar3",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testEp := func(ep *registry.Endpoint, enc string) {
|
||||
// encode endpoint
|
||||
e := encodeEndpoints([]*registry.Endpoint{ep})
|
||||
|
||||
// check there are two tags; old and new
|
||||
if len(e) != 1 {
|
||||
t.Fatalf("Expected 1 encoded tags, got %v", e)
|
||||
}
|
||||
|
||||
// check old encoding
|
||||
var seen bool
|
||||
|
||||
for _, en := range e {
|
||||
if en == enc {
|
||||
seen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !seen {
|
||||
t.Fatalf("Expected %s but not found", enc)
|
||||
}
|
||||
|
||||
// decode
|
||||
d := decodeEndpoints([]string{enc})
|
||||
if len(d) == 0 {
|
||||
t.Fatalf("Expected %v got %v", ep, d)
|
||||
}
|
||||
|
||||
// check name
|
||||
if d[0].Name != ep.Name {
|
||||
t.Fatalf("Expected ep %s got %s", ep.Name, d[0].Name)
|
||||
}
|
||||
|
||||
// check all the metadata exists
|
||||
for k, v := range ep.Metadata {
|
||||
if gv := d[0].Metadata[k]; gv != v {
|
||||
t.Fatalf("Expected key %s val %s got val %s", k, v, gv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, ep := range eps {
|
||||
// JSON encoded
|
||||
jencoded, err := json.Marshal(ep)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// HEX encoded
|
||||
hencoded := encode(jencoded)
|
||||
// endpoint tag
|
||||
hepTag := "e-" + hencoded
|
||||
testEp(ep, hepTag)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodingVersion(t *testing.T) {
|
||||
testData := []struct {
|
||||
decoded string
|
||||
encoded string
|
||||
}{
|
||||
{"1.0.0", "v-789c32d433d03300040000ffff02ce00ee"},
|
||||
{"latest", "v-789cca492c492d2e01040000ffff08cc028e"},
|
||||
}
|
||||
|
||||
for _, data := range testData {
|
||||
e := encodeVersion(data.decoded)
|
||||
|
||||
if e[0] != data.encoded {
|
||||
t.Fatalf("Expected %s got %s", data.encoded, e)
|
||||
}
|
||||
|
||||
d, ok := decodeVersion(e)
|
||||
if !ok {
|
||||
t.Fatalf("Unexpected %t for %s", ok, data.encoded)
|
||||
}
|
||||
|
||||
if d != data.decoded {
|
||||
t.Fatalf("Expected %s got %s", data.decoded, d)
|
||||
}
|
||||
|
||||
d, ok = decodeVersion([]string{data.encoded})
|
||||
if !ok {
|
||||
t.Fatalf("Unexpected %t for %s", ok, data.encoded)
|
||||
}
|
||||
|
||||
if d != data.decoded {
|
||||
t.Fatalf("Expected %s got %s", data.decoded, d)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
// Connect specifies services should be registered as Consul Connect services
|
||||
func Connect() registry.Option {
|
||||
return func(o *registry.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, "consul_connect", true)
|
||||
}
|
||||
}
|
||||
|
||||
func Config(c *consul.Config) registry.Option {
|
||||
return func(o *registry.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, "consul_config", c)
|
||||
}
|
||||
}
|
||||
|
||||
// AllowStale sets whether any Consul server (non-leader) can service
|
||||
// a read. This allows for lower latency and higher throughput
|
||||
// at the cost of potentially stale data.
|
||||
// Works similar to Consul DNS Config option [1].
|
||||
// Defaults to true.
|
||||
//
|
||||
// [1] https://www.consul.io/docs/agent/options.html#allow_stale
|
||||
//
|
||||
func AllowStale(v bool) registry.Option {
|
||||
return func(o *registry.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, "consul_allow_stale", v)
|
||||
}
|
||||
}
|
||||
|
||||
// QueryOptions specifies the QueryOptions to be used when calling
|
||||
// Consul. See `Consul API` for more information [1].
|
||||
//
|
||||
// [1] https://godoc.org/github.com/hashicorp/consul/api#QueryOptions
|
||||
//
|
||||
func QueryOptions(q *consul.QueryOptions) registry.Option {
|
||||
return func(o *registry.Options) {
|
||||
if q == nil {
|
||||
return
|
||||
}
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, "consul_query_options", q)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TCPCheck will tell the service provider to check the service address
|
||||
// and port every `t` interval. It will enabled only if `t` is greater than 0.
|
||||
// See `TCP + Interval` for more information [1].
|
||||
//
|
||||
// [1] https://www.consul.io/docs/agent/checks.html
|
||||
//
|
||||
func TCPCheck(t time.Duration) registry.Option {
|
||||
return func(o *registry.Options) {
|
||||
if t <= time.Duration(0) {
|
||||
return
|
||||
}
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, "consul_tcp_check", t)
|
||||
}
|
||||
}
|
@@ -1,208 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
type mockRegistry struct {
|
||||
body []byte
|
||||
status int
|
||||
err error
|
||||
url string
|
||||
}
|
||||
|
||||
func encodeData(obj interface{}) ([]byte, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
enc := json.NewEncoder(buf)
|
||||
if err := enc.Encode(obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func newMockServer(rg *mockRegistry, l net.Listener) error {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(rg.url, func(w http.ResponseWriter, r *http.Request) {
|
||||
if rg.err != nil {
|
||||
http.Error(w, rg.err.Error(), 500)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(rg.status)
|
||||
w.Write(rg.body)
|
||||
})
|
||||
return http.Serve(l, mux)
|
||||
}
|
||||
|
||||
func newConsulTestRegistry(r *mockRegistry) (*consulRegistry, func()) {
|
||||
l, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
// blurgh?!!
|
||||
panic(err.Error())
|
||||
}
|
||||
cfg := consul.DefaultConfig()
|
||||
cfg.Address = l.Addr().String()
|
||||
|
||||
go newMockServer(r, l)
|
||||
|
||||
var cr = &consulRegistry{
|
||||
config: cfg,
|
||||
Address: []string{cfg.Address},
|
||||
opts: registry.Options{},
|
||||
register: make(map[string]uint64),
|
||||
lastChecked: make(map[string]time.Time),
|
||||
queryOptions: &consul.QueryOptions{
|
||||
AllowStale: true,
|
||||
},
|
||||
}
|
||||
cr.Client()
|
||||
|
||||
return cr, func() {
|
||||
l.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func newServiceList(svc []*consul.ServiceEntry) []byte {
|
||||
bts, _ := encodeData(svc)
|
||||
return bts
|
||||
}
|
||||
|
||||
func TestConsul_GetService_WithError(t *testing.T) {
|
||||
cr, cl := newConsulTestRegistry(&mockRegistry{
|
||||
err: errors.New("client-error"),
|
||||
url: "/v1/health/service/service-name",
|
||||
})
|
||||
defer cl()
|
||||
|
||||
if _, err := cr.GetService("test-service"); err == nil {
|
||||
t.Fatalf("Expected error not to be `nil`")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsul_GetService_WithHealthyServiceNodes(t *testing.T) {
|
||||
// warning is still seen as healthy, critical is not
|
||||
svcs := []*consul.ServiceEntry{
|
||||
newServiceEntry(
|
||||
"node-name-1", "node-address-1", "service-name", "v1.0.0",
|
||||
[]*consul.HealthCheck{
|
||||
newHealthCheck("node-name-1", "service-name", "passing"),
|
||||
newHealthCheck("node-name-1", "service-name", "warning"),
|
||||
},
|
||||
),
|
||||
newServiceEntry(
|
||||
"node-name-2", "node-address-2", "service-name", "v1.0.0",
|
||||
[]*consul.HealthCheck{
|
||||
newHealthCheck("node-name-2", "service-name", "passing"),
|
||||
newHealthCheck("node-name-2", "service-name", "warning"),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
cr, cl := newConsulTestRegistry(&mockRegistry{
|
||||
status: 200,
|
||||
body: newServiceList(svcs),
|
||||
url: "/v1/health/service/service-name",
|
||||
})
|
||||
defer cl()
|
||||
|
||||
svc, err := cr.GetService("service-name")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error", err)
|
||||
}
|
||||
|
||||
if exp, act := 1, len(svc); exp != act {
|
||||
t.Fatalf("Expected len of svc to be `%d`, got `%d`.", exp, act)
|
||||
}
|
||||
|
||||
if exp, act := 2, len(svc[0].Nodes); exp != act {
|
||||
t.Fatalf("Expected len of nodes to be `%d`, got `%d`.", exp, act)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsul_GetService_WithUnhealthyServiceNode(t *testing.T) {
|
||||
// warning is still seen as healthy, critical is not
|
||||
svcs := []*consul.ServiceEntry{
|
||||
newServiceEntry(
|
||||
"node-name-1", "node-address-1", "service-name", "v1.0.0",
|
||||
[]*consul.HealthCheck{
|
||||
newHealthCheck("node-name-1", "service-name", "passing"),
|
||||
newHealthCheck("node-name-1", "service-name", "warning"),
|
||||
},
|
||||
),
|
||||
newServiceEntry(
|
||||
"node-name-2", "node-address-2", "service-name", "v1.0.0",
|
||||
[]*consul.HealthCheck{
|
||||
newHealthCheck("node-name-2", "service-name", "passing"),
|
||||
newHealthCheck("node-name-2", "service-name", "critical"),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
cr, cl := newConsulTestRegistry(&mockRegistry{
|
||||
status: 200,
|
||||
body: newServiceList(svcs),
|
||||
url: "/v1/health/service/service-name",
|
||||
})
|
||||
defer cl()
|
||||
|
||||
svc, err := cr.GetService("service-name")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error", err)
|
||||
}
|
||||
|
||||
if exp, act := 1, len(svc); exp != act {
|
||||
t.Fatalf("Expected len of svc to be `%d`, got `%d`.", exp, act)
|
||||
}
|
||||
|
||||
if exp, act := 1, len(svc[0].Nodes); exp != act {
|
||||
t.Fatalf("Expected len of nodes to be `%d`, got `%d`.", exp, act)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsul_GetService_WithUnhealthyServiceNodes(t *testing.T) {
|
||||
// warning is still seen as healthy, critical is not
|
||||
svcs := []*consul.ServiceEntry{
|
||||
newServiceEntry(
|
||||
"node-name-1", "node-address-1", "service-name", "v1.0.0",
|
||||
[]*consul.HealthCheck{
|
||||
newHealthCheck("node-name-1", "service-name", "passing"),
|
||||
newHealthCheck("node-name-1", "service-name", "critical"),
|
||||
},
|
||||
),
|
||||
newServiceEntry(
|
||||
"node-name-2", "node-address-2", "service-name", "v1.0.0",
|
||||
[]*consul.HealthCheck{
|
||||
newHealthCheck("node-name-2", "service-name", "passing"),
|
||||
newHealthCheck("node-name-2", "service-name", "critical"),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
cr, cl := newConsulTestRegistry(&mockRegistry{
|
||||
status: 200,
|
||||
body: newServiceList(svcs),
|
||||
url: "/v1/health/service/service-name",
|
||||
})
|
||||
defer cl()
|
||||
|
||||
svc, err := cr.GetService("service-name")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error", err)
|
||||
}
|
||||
|
||||
if exp, act := 1, len(svc); exp != act {
|
||||
t.Fatalf("Expected len of svc to be `%d`, got `%d`.", exp, act)
|
||||
}
|
||||
|
||||
if exp, act := 0, len(svc[0].Nodes); exp != act {
|
||||
t.Fatalf("Expected len of nodes to be `%d`, got `%d`.", exp, act)
|
||||
}
|
||||
}
|
@@ -1,290 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/api/watch"
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
type consulWatcher struct {
|
||||
r *consulRegistry
|
||||
wo registry.WatchOptions
|
||||
wp *watch.Plan
|
||||
watchers map[string]*watch.Plan
|
||||
|
||||
next chan *registry.Result
|
||||
exit chan bool
|
||||
|
||||
sync.RWMutex
|
||||
services map[string][]*registry.Service
|
||||
}
|
||||
|
||||
func newConsulWatcher(cr *consulRegistry, opts ...registry.WatchOption) (registry.Watcher, error) {
|
||||
var wo registry.WatchOptions
|
||||
for _, o := range opts {
|
||||
o(&wo)
|
||||
}
|
||||
|
||||
cw := &consulWatcher{
|
||||
r: cr,
|
||||
wo: wo,
|
||||
exit: make(chan bool),
|
||||
next: make(chan *registry.Result, 10),
|
||||
watchers: make(map[string]*watch.Plan),
|
||||
services: make(map[string][]*registry.Service),
|
||||
}
|
||||
|
||||
wp, err := watch.Parse(map[string]interface{}{"type": "services"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wp.Handler = cw.handle
|
||||
go wp.RunWithClientAndLogger(cr.Client(), log.New(os.Stderr, "", log.LstdFlags))
|
||||
cw.wp = wp
|
||||
|
||||
return cw, nil
|
||||
}
|
||||
|
||||
func (cw *consulWatcher) serviceHandler(idx uint64, data interface{}) {
|
||||
entries, ok := data.([]*api.ServiceEntry)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
serviceMap := map[string]*registry.Service{}
|
||||
serviceName := ""
|
||||
|
||||
for _, e := range entries {
|
||||
serviceName = e.Service.Service
|
||||
// version is now a tag
|
||||
version, _ := decodeVersion(e.Service.Tags)
|
||||
// service ID is now the node id
|
||||
id := e.Service.ID
|
||||
// key is always the version
|
||||
key := version
|
||||
// address is service address
|
||||
address := e.Service.Address
|
||||
|
||||
// use node address
|
||||
if len(address) == 0 {
|
||||
address = e.Node.Address
|
||||
}
|
||||
|
||||
svc, ok := serviceMap[key]
|
||||
if !ok {
|
||||
svc = ®istry.Service{
|
||||
Endpoints: decodeEndpoints(e.Service.Tags),
|
||||
Name: e.Service.Service,
|
||||
Version: version,
|
||||
}
|
||||
serviceMap[key] = svc
|
||||
}
|
||||
|
||||
var del bool
|
||||
|
||||
for _, check := range e.Checks {
|
||||
// delete the node if the status is critical
|
||||
if check.Status == "critical" {
|
||||
del = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if delete then skip the node
|
||||
if del {
|
||||
continue
|
||||
}
|
||||
|
||||
svc.Nodes = append(svc.Nodes, ®istry.Node{
|
||||
Id: id,
|
||||
Address: fmt.Sprintf("%s:%d", address, e.Service.Port),
|
||||
Metadata: decodeMetadata(e.Service.Tags),
|
||||
})
|
||||
}
|
||||
|
||||
cw.RLock()
|
||||
// make a copy
|
||||
rservices := make(map[string][]*registry.Service)
|
||||
for k, v := range cw.services {
|
||||
rservices[k] = v
|
||||
}
|
||||
cw.RUnlock()
|
||||
|
||||
var newServices []*registry.Service
|
||||
|
||||
// serviceMap is the new set of services keyed by name+version
|
||||
for _, newService := range serviceMap {
|
||||
// append to the new set of cached services
|
||||
newServices = append(newServices, newService)
|
||||
|
||||
// check if the service exists in the existing cache
|
||||
oldServices, ok := rservices[serviceName]
|
||||
if !ok {
|
||||
// does not exist? then we're creating brand new entries
|
||||
cw.next <- ®istry.Result{Action: "create", Service: newService}
|
||||
continue
|
||||
}
|
||||
|
||||
// service exists. ok let's figure out what to update and delete version wise
|
||||
action := "create"
|
||||
|
||||
for _, oldService := range oldServices {
|
||||
// does this version exist?
|
||||
// no? then default to create
|
||||
if oldService.Version != newService.Version {
|
||||
continue
|
||||
}
|
||||
|
||||
// yes? then it's an update
|
||||
action = "update"
|
||||
|
||||
var nodes []*registry.Node
|
||||
// check the old nodes to see if they've been deleted
|
||||
for _, oldNode := range oldService.Nodes {
|
||||
var seen bool
|
||||
for _, newNode := range newService.Nodes {
|
||||
if newNode.Id == oldNode.Id {
|
||||
seen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// does the old node exist in the new set of nodes
|
||||
// no? then delete that shit
|
||||
if !seen {
|
||||
nodes = append(nodes, oldNode)
|
||||
}
|
||||
}
|
||||
|
||||
// it's an update rather than creation
|
||||
if len(nodes) > 0 {
|
||||
delService := registry.CopyService(oldService)
|
||||
delService.Nodes = nodes
|
||||
cw.next <- ®istry.Result{Action: "delete", Service: delService}
|
||||
}
|
||||
}
|
||||
|
||||
cw.next <- ®istry.Result{Action: action, Service: newService}
|
||||
}
|
||||
|
||||
// Now check old versions that may not be in new services map
|
||||
for _, old := range rservices[serviceName] {
|
||||
// old version does not exist in new version map
|
||||
// kill it with fire!
|
||||
if _, ok := serviceMap[old.Version]; !ok {
|
||||
cw.next <- ®istry.Result{Action: "delete", Service: old}
|
||||
}
|
||||
}
|
||||
|
||||
cw.Lock()
|
||||
cw.services[serviceName] = newServices
|
||||
cw.Unlock()
|
||||
}
|
||||
|
||||
func (cw *consulWatcher) handle(idx uint64, data interface{}) {
|
||||
services, ok := data.(map[string][]string)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// add new watchers
|
||||
for service, _ := range services {
|
||||
// Filter on watch options
|
||||
// wo.Service: Only watch services we care about
|
||||
if len(cw.wo.Service) > 0 && service != cw.wo.Service {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := cw.watchers[service]; ok {
|
||||
continue
|
||||
}
|
||||
wp, err := watch.Parse(map[string]interface{}{
|
||||
"type": "service",
|
||||
"service": service,
|
||||
})
|
||||
if err == nil {
|
||||
wp.Handler = cw.serviceHandler
|
||||
go wp.RunWithClientAndLogger(cw.r.Client(), log.New(os.Stderr, "", log.LstdFlags))
|
||||
cw.watchers[service] = wp
|
||||
cw.next <- ®istry.Result{Action: "create", Service: ®istry.Service{Name: service}}
|
||||
}
|
||||
}
|
||||
|
||||
cw.RLock()
|
||||
// make a copy
|
||||
rservices := make(map[string][]*registry.Service)
|
||||
for k, v := range cw.services {
|
||||
rservices[k] = v
|
||||
}
|
||||
cw.RUnlock()
|
||||
|
||||
// remove unknown services from registry
|
||||
// save the things we want to delete
|
||||
deleted := make(map[string][]*registry.Service)
|
||||
|
||||
for service, _ := range rservices {
|
||||
if _, ok := services[service]; !ok {
|
||||
cw.Lock()
|
||||
// save this before deleting
|
||||
deleted[service] = cw.services[service]
|
||||
delete(cw.services, service)
|
||||
cw.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// remove unknown services from watchers
|
||||
for service, w := range cw.watchers {
|
||||
if _, ok := services[service]; !ok {
|
||||
w.Stop()
|
||||
delete(cw.watchers, service)
|
||||
for _, oldService := range deleted[service] {
|
||||
// send a delete for the service nodes that we're removing
|
||||
cw.next <- ®istry.Result{Action: "delete", Service: oldService}
|
||||
}
|
||||
// sent the empty list as the last resort to indicate to delete the entire service
|
||||
cw.next <- ®istry.Result{Action: "delete", Service: ®istry.Service{Name: service}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cw *consulWatcher) Next() (*registry.Result, error) {
|
||||
select {
|
||||
case <-cw.exit:
|
||||
return nil, registry.ErrWatcherStopped
|
||||
case r, ok := <-cw.next:
|
||||
if !ok {
|
||||
return nil, registry.ErrWatcherStopped
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
// NOTE: This is a dead code path: e.g. it will never be reached
|
||||
// as we return in all previous code paths never leading to this return
|
||||
return nil, registry.ErrWatcherStopped
|
||||
}
|
||||
|
||||
func (cw *consulWatcher) Stop() {
|
||||
select {
|
||||
case <-cw.exit:
|
||||
return
|
||||
default:
|
||||
close(cw.exit)
|
||||
if cw.wp == nil {
|
||||
return
|
||||
}
|
||||
cw.wp.Stop()
|
||||
|
||||
// drain results
|
||||
for {
|
||||
select {
|
||||
case <-cw.next:
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,86 +0,0 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
func TestHealthyServiceHandler(t *testing.T) {
|
||||
watcher := newWatcher()
|
||||
serviceEntry := newServiceEntry(
|
||||
"node-name", "node-address", "service-name", "v1.0.0",
|
||||
[]*api.HealthCheck{
|
||||
newHealthCheck("node-name", "service-name", "passing"),
|
||||
},
|
||||
)
|
||||
|
||||
watcher.serviceHandler(1234, []*api.ServiceEntry{serviceEntry})
|
||||
|
||||
if len(watcher.services["service-name"][0].Nodes) != 1 {
|
||||
t.Errorf("Expected length of the service nodes to be 1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnhealthyServiceHandler(t *testing.T) {
|
||||
watcher := newWatcher()
|
||||
serviceEntry := newServiceEntry(
|
||||
"node-name", "node-address", "service-name", "v1.0.0",
|
||||
[]*api.HealthCheck{
|
||||
newHealthCheck("node-name", "service-name", "critical"),
|
||||
},
|
||||
)
|
||||
|
||||
watcher.serviceHandler(1234, []*api.ServiceEntry{serviceEntry})
|
||||
|
||||
if len(watcher.services["service-name"][0].Nodes) != 0 {
|
||||
t.Errorf("Expected length of the service nodes to be 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnhealthyNodeServiceHandler(t *testing.T) {
|
||||
watcher := newWatcher()
|
||||
serviceEntry := newServiceEntry(
|
||||
"node-name", "node-address", "service-name", "v1.0.0",
|
||||
[]*api.HealthCheck{
|
||||
newHealthCheck("node-name", "service-name", "passing"),
|
||||
newHealthCheck("node-name", "serfHealth", "critical"),
|
||||
},
|
||||
)
|
||||
|
||||
watcher.serviceHandler(1234, []*api.ServiceEntry{serviceEntry})
|
||||
|
||||
if len(watcher.services["service-name"][0].Nodes) != 0 {
|
||||
t.Errorf("Expected length of the service nodes to be 0")
|
||||
}
|
||||
}
|
||||
|
||||
func newWatcher() *consulWatcher {
|
||||
return &consulWatcher{
|
||||
exit: make(chan bool),
|
||||
next: make(chan *registry.Result, 10),
|
||||
services: make(map[string][]*registry.Service),
|
||||
}
|
||||
}
|
||||
|
||||
func newHealthCheck(node, name, status string) *api.HealthCheck {
|
||||
return &api.HealthCheck{
|
||||
Node: node,
|
||||
Name: name,
|
||||
Status: status,
|
||||
ServiceName: name,
|
||||
}
|
||||
}
|
||||
|
||||
func newServiceEntry(node, address, name, version string, checks []*api.HealthCheck) *api.ServiceEntry {
|
||||
return &api.ServiceEntry{
|
||||
Node: &api.Node{Node: node, Address: name},
|
||||
Service: &api.AgentService{
|
||||
Service: name,
|
||||
Address: address,
|
||||
Tags: encodeVersion(version),
|
||||
},
|
||||
Checks: checks,
|
||||
}
|
||||
}
|
@@ -6,13 +6,13 @@ import (
|
||||
|
||||
func TestEncoding(t *testing.T) {
|
||||
testData := []*mdnsTxt{
|
||||
&mdnsTxt{
|
||||
{
|
||||
Version: "1.0.0",
|
||||
Metadata: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Endpoints: []*Endpoint{
|
||||
&Endpoint{
|
||||
{
|
||||
Name: "endpoint1",
|
||||
Request: &Value{
|
||||
Name: "request",
|
||||
|
@@ -341,7 +341,7 @@ func (e *etcdRegistry) GetService(name string) ([]*registry.Service, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var services []*registry.Service
|
||||
services := make([]*registry.Service, 0, len(serviceMap))
|
||||
for _, service := range serviceMap {
|
||||
services = append(services, service)
|
||||
}
|
||||
@@ -350,7 +350,6 @@ func (e *etcdRegistry) GetService(name string) ([]*registry.Service, error) {
|
||||
}
|
||||
|
||||
func (e *etcdRegistry) ListServices() ([]*registry.Service, error) {
|
||||
var services []*registry.Service
|
||||
versions := make(map[string]*registry.Service)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
|
||||
@@ -379,6 +378,7 @@ func (e *etcdRegistry) ListServices() ([]*registry.Service, error) {
|
||||
v.Nodes = append(v.Nodes, sn.Nodes...)
|
||||
}
|
||||
|
||||
services := make([]*registry.Service, 0, len(versions))
|
||||
for _, service := range versions {
|
||||
services = append(services, service)
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/mdns"
|
||||
)
|
||||
|
||||
@@ -37,12 +38,20 @@ type mdnsRegistry struct {
|
||||
|
||||
sync.Mutex
|
||||
services map[string][]*mdnsEntry
|
||||
|
||||
mtx sync.RWMutex
|
||||
|
||||
// watchers
|
||||
watchers map[string]*mdnsWatcher
|
||||
|
||||
// listener
|
||||
listener chan *mdns.ServiceEntry
|
||||
}
|
||||
|
||||
func newRegistry(opts ...Option) Registry {
|
||||
options := Options{
|
||||
Context: context.Background(),
|
||||
Timeout: time.Millisecond * 100,
|
||||
Timeout: time.Millisecond * 10,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
@@ -61,6 +70,7 @@ func newRegistry(opts ...Option) Registry {
|
||||
opts: options,
|
||||
domain: domain,
|
||||
services: make(map[string][]*mdnsEntry),
|
||||
watchers: make(map[string]*mdnsWatcher),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +292,7 @@ func (m *mdnsRegistry) GetService(service string) ([]*Service, error) {
|
||||
<-done
|
||||
|
||||
// create list and return
|
||||
var services []*Service
|
||||
services := make([]*Service, 0, len(serviceMap))
|
||||
|
||||
for _, service := range serviceMap {
|
||||
services = append(services, service)
|
||||
@@ -346,15 +356,88 @@ func (m *mdnsRegistry) Watch(opts ...WatchOption) (Watcher, error) {
|
||||
}
|
||||
|
||||
md := &mdnsWatcher{
|
||||
wo: wo,
|
||||
ch: make(chan *mdns.ServiceEntry, 32),
|
||||
exit: make(chan struct{}),
|
||||
domain: m.domain,
|
||||
id: uuid.New().String(),
|
||||
wo: wo,
|
||||
ch: make(chan *mdns.ServiceEntry, 32),
|
||||
exit: make(chan struct{}),
|
||||
domain: m.domain,
|
||||
registry: m,
|
||||
}
|
||||
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
// save the watcher
|
||||
m.watchers[md.id] = md
|
||||
|
||||
// check of the listener exists
|
||||
if m.listener != nil {
|
||||
return md, nil
|
||||
}
|
||||
|
||||
// start the listener
|
||||
go func() {
|
||||
if err := mdns.Listen(md.ch, md.exit); err != nil {
|
||||
md.Stop()
|
||||
// go to infinity
|
||||
for {
|
||||
m.mtx.Lock()
|
||||
|
||||
// just return if there are no watchers
|
||||
if len(m.watchers) == 0 {
|
||||
m.listener = nil
|
||||
m.mtx.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// check existing listener
|
||||
if m.listener != nil {
|
||||
m.mtx.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// reset the listener
|
||||
exit := make(chan struct{})
|
||||
ch := make(chan *mdns.ServiceEntry, 32)
|
||||
m.listener = ch
|
||||
|
||||
m.mtx.Unlock()
|
||||
|
||||
// send messages to the watchers
|
||||
go func() {
|
||||
send := func(w *mdnsWatcher, e *mdns.ServiceEntry) {
|
||||
select {
|
||||
case w.ch <- e:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-exit:
|
||||
return
|
||||
case e, ok := <-ch:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
m.mtx.RLock()
|
||||
// send service entry to all watchers
|
||||
for _, w := range m.watchers {
|
||||
send(w, e)
|
||||
}
|
||||
m.mtx.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
// start listening, blocking call
|
||||
mdns.Listen(ch, exit)
|
||||
|
||||
// mdns.Listen has unblocked
|
||||
// kill the saved listener
|
||||
m.mtx.Lock()
|
||||
m.listener = nil
|
||||
close(ch)
|
||||
m.mtx.Unlock()
|
||||
}
|
||||
}()
|
||||
|
||||
|
@@ -1,17 +1,18 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMDNS(t *testing.T) {
|
||||
testData := []*Service{
|
||||
&Service{
|
||||
{
|
||||
Name: "test1",
|
||||
Version: "1.0.1",
|
||||
Nodes: []*Node{
|
||||
&Node{
|
||||
{
|
||||
Id: "test1-1",
|
||||
Address: "10.0.0.1:10001",
|
||||
Metadata: map[string]string{
|
||||
@@ -20,11 +21,11 @@ func TestMDNS(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&Service{
|
||||
{
|
||||
Name: "test2",
|
||||
Version: "1.0.2",
|
||||
Nodes: []*Node{
|
||||
&Node{
|
||||
{
|
||||
Id: "test2-1",
|
||||
Address: "10.0.0.2:10002",
|
||||
Metadata: map[string]string{
|
||||
@@ -33,11 +34,11 @@ func TestMDNS(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&Service{
|
||||
{
|
||||
Name: "test3",
|
||||
Version: "1.0.3",
|
||||
Nodes: []*Node{
|
||||
&Node{
|
||||
{
|
||||
Id: "test3-1",
|
||||
Address: "10.0.0.3:10003",
|
||||
Metadata: map[string]string{
|
||||
@@ -48,8 +49,16 @@ func TestMDNS(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
travis := os.Getenv("TRAVIS")
|
||||
|
||||
var opts []Option
|
||||
|
||||
if travis == "true" {
|
||||
opts = append(opts, Timeout(time.Millisecond*100))
|
||||
}
|
||||
|
||||
// new registry
|
||||
r := NewRegistry()
|
||||
r := NewRegistry(opts...)
|
||||
|
||||
for _, service := range testData {
|
||||
// register service
|
||||
|
@@ -8,11 +8,14 @@ import (
|
||||
)
|
||||
|
||||
type mdnsWatcher struct {
|
||||
id string
|
||||
wo WatchOptions
|
||||
ch chan *mdns.ServiceEntry
|
||||
exit chan struct{}
|
||||
// the mdns domain
|
||||
domain string
|
||||
// the registry
|
||||
registry *mdnsRegistry
|
||||
}
|
||||
|
||||
func (m *mdnsWatcher) Next() (*Result, error) {
|
||||
@@ -76,5 +79,9 @@ func (m *mdnsWatcher) Stop() {
|
||||
return
|
||||
default:
|
||||
close(m.exit)
|
||||
// remove self from the registry
|
||||
m.registry.mtx.Lock()
|
||||
delete(m.registry.watchers, m.id)
|
||||
m.registry.mtx.Unlock()
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -14,23 +13,29 @@ import (
|
||||
|
||||
var (
|
||||
sendEventTime = 10 * time.Millisecond
|
||||
ttlPruneTime = 1 * time.Minute
|
||||
DefaultTTL = 1 * time.Minute
|
||||
ttlPruneTime = time.Second
|
||||
)
|
||||
|
||||
// node tracks node registration timestamp and TTL
|
||||
type node struct {
|
||||
lastSeen time.Time
|
||||
ttl time.Duration
|
||||
*registry.Node
|
||||
TTL time.Duration
|
||||
LastSeen time.Time
|
||||
}
|
||||
|
||||
type record struct {
|
||||
Name string
|
||||
Version string
|
||||
Metadata map[string]string
|
||||
Nodes map[string]*node
|
||||
Endpoints []*registry.Endpoint
|
||||
}
|
||||
|
||||
type Registry struct {
|
||||
options registry.Options
|
||||
|
||||
sync.RWMutex
|
||||
Services map[string][]*registry.Service
|
||||
nodes map[string]*node
|
||||
Watchers map[string]*Watcher
|
||||
records map[string]map[string]*record
|
||||
watchers map[string]*Watcher
|
||||
}
|
||||
|
||||
func NewRegistry(opts ...registry.Option) registry.Registry {
|
||||
@@ -42,16 +47,15 @@ func NewRegistry(opts ...registry.Option) registry.Registry {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
services := getServices(options.Context)
|
||||
if services == nil {
|
||||
services = make(map[string][]*registry.Service)
|
||||
records := getServiceRecords(options.Context)
|
||||
if records == nil {
|
||||
records = make(map[string]map[string]*record)
|
||||
}
|
||||
|
||||
reg := &Registry{
|
||||
options: options,
|
||||
Services: services,
|
||||
nodes: make(map[string]*node),
|
||||
Watchers: make(map[string]*Watcher),
|
||||
records: records,
|
||||
watchers: make(map[string]*Watcher),
|
||||
}
|
||||
|
||||
go reg.ttlPrune()
|
||||
@@ -59,11 +63,6 @@ func NewRegistry(opts ...registry.Option) registry.Registry {
|
||||
return reg
|
||||
}
|
||||
|
||||
// nodeTrackId returns a string we use to track a node of a given service
|
||||
func nodeTrackId(svcName, svcVersion, nodeId string) string {
|
||||
return svcName + "+" + svcVersion + "+" + nodeId
|
||||
}
|
||||
|
||||
func (m *Registry) ttlPrune() {
|
||||
prune := time.NewTicker(ttlPruneTime)
|
||||
defer prune.Stop()
|
||||
@@ -72,49 +71,26 @@ func (m *Registry) ttlPrune() {
|
||||
select {
|
||||
case <-prune.C:
|
||||
m.Lock()
|
||||
for nodeTrackId, node := range m.nodes {
|
||||
// if the TTL has been set and we exceed the hresholdset by it we stop tracking the node
|
||||
if node.ttl.Seconds() != 0.0 && time.Since(node.lastSeen) > node.ttl {
|
||||
// split nodeTrackID into service Name, Version and Node Id
|
||||
trackIdSplit := strings.Split(nodeTrackId, "+")
|
||||
svcName, svcVersion, nodeId := trackIdSplit[0], trackIdSplit[1], trackIdSplit[2]
|
||||
log.Debugf("[memory] Registry TTL expired for service %s, node %s", svcName, nodeId)
|
||||
// we need to find a node that expired and delete it from service nodes
|
||||
if _, ok := m.Services[svcName]; ok {
|
||||
for _, service := range m.Services[svcName] {
|
||||
if service.Version != svcVersion {
|
||||
continue
|
||||
}
|
||||
// find expired service node and delete it
|
||||
var nodes []*registry.Node
|
||||
for _, n := range service.Nodes {
|
||||
var del bool
|
||||
if n.Id == nodeId {
|
||||
del = true
|
||||
}
|
||||
if !del {
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
}
|
||||
service.Nodes = nodes
|
||||
for name, records := range m.records {
|
||||
for version, record := range records {
|
||||
for id, n := range record.Nodes {
|
||||
if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL {
|
||||
log.Debugf("Registry TTL expired for node %s of service %s", n.Id, name)
|
||||
delete(m.records[name][version].Nodes, id)
|
||||
}
|
||||
}
|
||||
// stop tracking the node
|
||||
delete(m.nodes, nodeTrackId)
|
||||
}
|
||||
}
|
||||
m.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Registry) sendEvent(r *registry.Result) {
|
||||
var watchers []*Watcher
|
||||
watchers := make([]*Watcher, 0, len(m.watchers))
|
||||
|
||||
m.RLock()
|
||||
for _, w := range m.Watchers {
|
||||
for _, w := range m.watchers {
|
||||
watchers = append(watchers, w)
|
||||
}
|
||||
m.RUnlock()
|
||||
@@ -123,7 +99,7 @@ func (m *Registry) sendEvent(r *registry.Result) {
|
||||
select {
|
||||
case <-w.exit:
|
||||
m.Lock()
|
||||
delete(m.Watchers, w.id)
|
||||
delete(m.watchers, w.id)
|
||||
m.Unlock()
|
||||
default:
|
||||
select {
|
||||
@@ -141,11 +117,24 @@ func (m *Registry) Init(opts ...registry.Option) error {
|
||||
|
||||
// add services
|
||||
m.Lock()
|
||||
for k, v := range getServices(m.options.Context) {
|
||||
s := m.Services[k]
|
||||
m.Services[k] = registry.Merge(s, v)
|
||||
defer m.Unlock()
|
||||
|
||||
records := getServiceRecords(m.options.Context)
|
||||
for name, record := range records {
|
||||
// add a whole new service including all of its versions
|
||||
if _, ok := m.records[name]; !ok {
|
||||
m.records[name] = record
|
||||
continue
|
||||
}
|
||||
// add the versions of the service we dont track yet
|
||||
for version, r := range record {
|
||||
if _, ok := m.records[name][version]; !ok {
|
||||
m.records[name][version] = r
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
m.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -153,104 +142,61 @@ func (m *Registry) Options() registry.Options {
|
||||
return m.options
|
||||
}
|
||||
|
||||
func (m *Registry) GetService(name string) ([]*registry.Service, error) {
|
||||
m.RLock()
|
||||
service, ok := m.Services[name]
|
||||
m.RUnlock()
|
||||
if !ok {
|
||||
return nil, registry.ErrNotFound
|
||||
}
|
||||
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (m *Registry) ListServices() ([]*registry.Service, error) {
|
||||
var services []*registry.Service
|
||||
m.RLock()
|
||||
for _, service := range m.Services {
|
||||
services = append(services, service...)
|
||||
}
|
||||
m.RUnlock()
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
log.Debugf("[memory] Registry registering service: %s", s.Name)
|
||||
|
||||
var options registry.RegisterOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
if service, ok := m.Services[s.Name]; !ok {
|
||||
m.Services[s.Name] = []*registry.Service{s}
|
||||
// add all nodes into nodes map to track their TTL
|
||||
for _, n := range s.Nodes {
|
||||
log.Debugf("[memory] Registry tracking new service: %s, node %s", s.Name, n.Id)
|
||||
m.nodes[nodeTrackId(s.Name, s.Version, n.Id)] = &node{
|
||||
lastSeen: time.Now(),
|
||||
ttl: options.TTL,
|
||||
}
|
||||
}
|
||||
r := serviceToRecord(s, options.TTL)
|
||||
|
||||
if _, ok := m.records[s.Name]; !ok {
|
||||
m.records[s.Name] = make(map[string]*record)
|
||||
}
|
||||
|
||||
if _, ok := m.records[s.Name][s.Version]; !ok {
|
||||
m.records[s.Name][s.Version] = r
|
||||
log.Debugf("Registry added new service: %s, version: %s", s.Name, s.Version)
|
||||
go m.sendEvent(®istry.Result{Action: "update", Service: s})
|
||||
return nil
|
||||
} else {
|
||||
// svcCount keeps the count of all versions of particular service
|
||||
//svcCount := len(service)
|
||||
// svcNodes maintains a list of node Ids per particular service version
|
||||
svcNodes := make(map[string]map[string][]string)
|
||||
// collect all service ids for all service versions
|
||||
for _, s := range service {
|
||||
if _, ok := svcNodes[s.Name]; !ok {
|
||||
svcNodes[s.Name] = make(map[string][]string)
|
||||
}
|
||||
if _, ok := svcNodes[s.Name][s.Version]; !ok {
|
||||
for _, n := range s.Nodes {
|
||||
svcNodes[s.Name][s.Version] = append(svcNodes[s.Name][s.Version], n.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
// if merged count and original service counts changed we know we are adding a new version of the service
|
||||
merged := registry.Merge(service, []*registry.Service{s})
|
||||
// if the node count of any service [version] changed we know we are adding a new node to the service
|
||||
for _, s := range merged {
|
||||
// we know that if the node counts have changed we need to track new nodes
|
||||
if len(s.Nodes) != len(svcNodes[s.Name][s.Version]) {
|
||||
for _, n := range s.Nodes {
|
||||
var found bool
|
||||
for _, id := range svcNodes[s.Name][s.Version] {
|
||||
if n.Id == id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
log.Debugf("[memory] Registry tracking new node: %s for service %s", n.Id, s.Name)
|
||||
m.nodes[nodeTrackId(s.Name, s.Version, n.Id)] = &node{
|
||||
lastSeen: time.Now(),
|
||||
ttl: options.TTL,
|
||||
}
|
||||
}
|
||||
}
|
||||
m.Services[s.Name] = merged
|
||||
go m.sendEvent(®istry.Result{Action: "update", Service: s})
|
||||
return nil
|
||||
}
|
||||
// refresh the timestamp and TTL of the service node
|
||||
for _, n := range s.Nodes {
|
||||
trackId := nodeTrackId(s.Name, s.Version, n.Id)
|
||||
log.Debugf("[memory] Registry refreshing TTL for node %s for service %s", n.Id, s.Name)
|
||||
if trackedNode, ok := m.nodes[trackId]; ok {
|
||||
trackedNode.lastSeen = time.Now()
|
||||
trackedNode.ttl = options.TTL
|
||||
}
|
||||
|
||||
addedNodes := false
|
||||
for _, n := range s.Nodes {
|
||||
if _, ok := m.records[s.Name][s.Version].Nodes[n.Id]; !ok {
|
||||
addedNodes = true
|
||||
metadata := make(map[string]string)
|
||||
for k, v := range n.Metadata {
|
||||
metadata[k] = v
|
||||
m.records[s.Name][s.Version].Nodes[n.Id] = &node{
|
||||
Node: ®istry.Node{
|
||||
Id: n.Id,
|
||||
Address: n.Address,
|
||||
Metadata: metadata,
|
||||
},
|
||||
TTL: options.TTL,
|
||||
LastSeen: time.Now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if addedNodes {
|
||||
log.Debugf("Registry added new node to service: %s, version: %s", s.Name, s.Version)
|
||||
go m.sendEvent(®istry.Result{Action: "update", Service: s})
|
||||
return nil
|
||||
}
|
||||
|
||||
// refresh TTL and timestamp
|
||||
for _, n := range s.Nodes {
|
||||
log.Debugf("Updated registration for service: %s, version: %s", s.Name, s.Version)
|
||||
m.records[s.Name][s.Version].Nodes[n.Id].TTL = options.TTL
|
||||
m.records[s.Name][s.Version].Nodes[n.Id].LastSeen = time.Now()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -258,57 +204,62 @@ func (m *Registry) Deregister(s *registry.Service) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
log.Debugf("[memory] Registry deregistering service: %s", s.Name)
|
||||
|
||||
if service, ok := m.Services[s.Name]; ok {
|
||||
// svcNodes collects the list of all node Ids for each service version
|
||||
svcNodes := make(map[string]map[string][]string)
|
||||
// collect all service node ids for all service versions
|
||||
for _, svc := range service {
|
||||
if _, ok := svcNodes[svc.Name]; !ok {
|
||||
svcNodes[svc.Name] = make(map[string][]string)
|
||||
}
|
||||
if _, ok := svcNodes[svc.Name][svc.Version]; !ok {
|
||||
for _, n := range svc.Nodes {
|
||||
svcNodes[svc.Name][svc.Version] = append(svcNodes[svc.Name][svc.Version], n.Id)
|
||||
if _, ok := m.records[s.Name]; ok {
|
||||
if _, ok := m.records[s.Name][s.Version]; ok {
|
||||
for _, n := range s.Nodes {
|
||||
if _, ok := m.records[s.Name][s.Version].Nodes[n.Id]; ok {
|
||||
log.Debugf("Registry removed node from service: %s, version: %s", s.Name, s.Version)
|
||||
delete(m.records[s.Name][s.Version].Nodes, n.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
// if there are no more services we know we have either removed all nodes or there were no nodes
|
||||
if updatedService := registry.Remove(service, []*registry.Service{s}); len(updatedService) == 0 {
|
||||
for _, id := range svcNodes[s.Name][s.Version] {
|
||||
log.Debugf("[memory] Registry stopped tracking node %s for service %s", id, s.Name)
|
||||
delete(m.nodes, nodeTrackId(s.Name, s.Version, id))
|
||||
go m.sendEvent(®istry.Result{Action: "delete", Service: s})
|
||||
}
|
||||
log.Debugf("[memory] Registry deleting service %s: no service nodes", s.Name)
|
||||
delete(m.Services, s.Name)
|
||||
return nil
|
||||
} else {
|
||||
// find out which nodes have been removed
|
||||
for _, id := range svcNodes[s.Name][s.Version] {
|
||||
for _, svc := range updatedService {
|
||||
var found bool
|
||||
for _, n := range svc.Nodes {
|
||||
if id == n.Id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
log.Debugf("[memory] Registry stopped tracking node %s for service %s", id, s.Name)
|
||||
delete(m.nodes, nodeTrackId(s.Name, s.Version, id))
|
||||
go m.sendEvent(®istry.Result{Action: "delete", Service: s})
|
||||
}
|
||||
}
|
||||
m.Services[s.Name] = updatedService
|
||||
if len(m.records[s.Name][s.Version].Nodes) == 0 {
|
||||
delete(m.records[s.Name], s.Version)
|
||||
log.Debugf("Registry removed service: %s, version: %s", s.Name, s.Version)
|
||||
}
|
||||
}
|
||||
if len(m.records[s.Name]) == 0 {
|
||||
delete(m.records, s.Name)
|
||||
log.Debugf("Registry removed service: %s", s.Name)
|
||||
}
|
||||
go m.sendEvent(®istry.Result{Action: "delete", Service: s})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Registry) GetService(name string) ([]*registry.Service, error) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
records, ok := m.records[name]
|
||||
if !ok {
|
||||
return nil, registry.ErrNotFound
|
||||
}
|
||||
|
||||
services := make([]*registry.Service, len(m.records[name]))
|
||||
i := 0
|
||||
for _, record := range records {
|
||||
services[i] = recordToService(record)
|
||||
i++
|
||||
}
|
||||
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func (m *Registry) ListServices() ([]*registry.Service, error) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
var services []*registry.Service
|
||||
for _, records := range m.records {
|
||||
for _, record := range records {
|
||||
services = append(services, recordToService(record))
|
||||
}
|
||||
}
|
||||
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func (m *Registry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
|
||||
var wo registry.WatchOptions
|
||||
for _, o := range opts {
|
||||
@@ -323,8 +274,9 @@ func (m *Registry) Watch(opts ...registry.WatchOption) (registry.Watcher, error)
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
m.Watchers[w.id] = w
|
||||
m.watchers[w.id] = w
|
||||
m.Unlock()
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
|
@@ -1,14 +1,16 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
var (
|
||||
testData = map[string][]*registry.Service{
|
||||
"foo": []*registry.Service{
|
||||
"foo": {
|
||||
{
|
||||
Name: "foo",
|
||||
Version: "1.0.0",
|
||||
@@ -44,7 +46,7 @@ var (
|
||||
},
|
||||
},
|
||||
},
|
||||
"bar": []*registry.Service{
|
||||
"bar": {
|
||||
{
|
||||
Name: "bar",
|
||||
Version: "default",
|
||||
@@ -102,10 +104,20 @@ func TestMemoryRegistry(t *testing.T) {
|
||||
|
||||
// register data
|
||||
for _, v := range testData {
|
||||
serviceCount := 0
|
||||
for _, service := range v {
|
||||
if err := m.Register(service); err != nil {
|
||||
t.Errorf("Unexpected register error: %v", err)
|
||||
}
|
||||
serviceCount++
|
||||
// after the service has been registered we should be able to query it
|
||||
services, err := m.GetService(service.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error getting service %s: %v", service.Name, err)
|
||||
}
|
||||
if len(services) != serviceCount {
|
||||
t.Errorf("Expected %d services for %s, got %d", serviceCount, service.Name, len(services))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +126,22 @@ func TestMemoryRegistry(t *testing.T) {
|
||||
fn(k, v)
|
||||
}
|
||||
|
||||
services, err := m.ListServices()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when listing services: %v", err)
|
||||
}
|
||||
|
||||
totalServiceCount := 0
|
||||
for _, testSvc := range testData {
|
||||
for range testSvc {
|
||||
totalServiceCount++
|
||||
}
|
||||
}
|
||||
|
||||
if len(services) != totalServiceCount {
|
||||
t.Errorf("Expected total service count: %d, got: %d", totalServiceCount, len(services))
|
||||
}
|
||||
|
||||
// deregister
|
||||
for _, v := range testData {
|
||||
for _, service := range v {
|
||||
@@ -122,4 +150,94 @@ func TestMemoryRegistry(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// after all the service nodes have been deregistered we should not get any results
|
||||
for _, v := range testData {
|
||||
for _, service := range v {
|
||||
services, err := m.GetService(service.Name)
|
||||
if err != registry.ErrNotFound {
|
||||
t.Errorf("Expected error: %v, got: %v", registry.ErrNotFound, err)
|
||||
}
|
||||
if len(services) != 0 {
|
||||
t.Errorf("Expected %d services for %s, got %d", 0, service.Name, len(services))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryRegistryTTL(t *testing.T) {
|
||||
m := NewRegistry()
|
||||
|
||||
for _, v := range testData {
|
||||
for _, service := range v {
|
||||
if err := m.Register(service, registry.RegisterTTL(time.Millisecond)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(ttlPruneTime * 2)
|
||||
|
||||
for name := range testData {
|
||||
svcs, err := m.GetService(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
if len(svc.Nodes) > 0 {
|
||||
t.Fatalf("Service %q still has nodes registered", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryRegistryTTLConcurrent(t *testing.T) {
|
||||
concurrency := 1000
|
||||
waitTime := ttlPruneTime * 2
|
||||
m := NewRegistry()
|
||||
|
||||
for _, v := range testData {
|
||||
for _, service := range v {
|
||||
if err := m.Register(service, registry.RegisterTTL(waitTime/2)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("test will wait %v, then check TTL timeouts", waitTime)
|
||||
|
||||
errChan := make(chan error, concurrency)
|
||||
syncChan := make(chan struct{})
|
||||
|
||||
for i := 0; i < concurrency; i++ {
|
||||
go func() {
|
||||
<-syncChan
|
||||
for name := range testData {
|
||||
svcs, err := m.GetService(name)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
if len(svc.Nodes) > 0 {
|
||||
errChan <- fmt.Errorf("Service %q still has nodes registered", name)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errChan <- nil
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
close(syncChan)
|
||||
|
||||
for i := 0; i < concurrency; i++ {
|
||||
if err := <-errChan; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,12 +8,25 @@ import (
|
||||
|
||||
type servicesKey struct{}
|
||||
|
||||
func getServices(ctx context.Context) map[string][]*registry.Service {
|
||||
s, ok := ctx.Value(servicesKey{}).(map[string][]*registry.Service)
|
||||
func getServiceRecords(ctx context.Context) map[string]map[string]*record {
|
||||
memServices, ok := ctx.Value(servicesKey{}).(map[string][]*registry.Service)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
|
||||
services := make(map[string]map[string]*record)
|
||||
|
||||
for name, svc := range memServices {
|
||||
if _, ok := services[name]; !ok {
|
||||
services[name] = make(map[string]*record)
|
||||
}
|
||||
// go through every version of the service
|
||||
for _, s := range svc {
|
||||
services[s.Name][s.Version] = serviceToRecord(s, 0)
|
||||
}
|
||||
}
|
||||
|
||||
return services
|
||||
}
|
||||
|
||||
// Services is an option that preloads service data
|
||||
|
87
registry/memory/util.go
Normal file
87
registry/memory/util.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
func serviceToRecord(s *registry.Service, ttl time.Duration) *record {
|
||||
metadata := make(map[string]string)
|
||||
for k, v := range s.Metadata {
|
||||
metadata[k] = v
|
||||
}
|
||||
|
||||
nodes := make(map[string]*node)
|
||||
for _, n := range s.Nodes {
|
||||
nodes[n.Id] = &node{
|
||||
Node: n,
|
||||
TTL: ttl,
|
||||
LastSeen: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
endpoints := make([]*registry.Endpoint, len(s.Endpoints))
|
||||
for i, e := range s.Endpoints {
|
||||
endpoints[i] = e
|
||||
}
|
||||
|
||||
return &record{
|
||||
Name: s.Name,
|
||||
Version: s.Version,
|
||||
Metadata: metadata,
|
||||
Nodes: nodes,
|
||||
Endpoints: endpoints,
|
||||
}
|
||||
}
|
||||
|
||||
func recordToService(r *record) *registry.Service {
|
||||
metadata := make(map[string]string)
|
||||
for k, v := range r.Metadata {
|
||||
metadata[k] = v
|
||||
}
|
||||
|
||||
endpoints := make([]*registry.Endpoint, len(r.Endpoints))
|
||||
for i, e := range r.Endpoints {
|
||||
request := new(registry.Value)
|
||||
request = e.Request
|
||||
response := new(registry.Value)
|
||||
response = e.Response
|
||||
|
||||
metadata := make(map[string]string)
|
||||
for k, v := range e.Metadata {
|
||||
metadata[k] = v
|
||||
}
|
||||
|
||||
endpoints[i] = ®istry.Endpoint{
|
||||
Name: e.Name,
|
||||
Request: request,
|
||||
Response: response,
|
||||
Metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
nodes := make([]*registry.Node, len(r.Nodes))
|
||||
i := 0
|
||||
for _, n := range r.Nodes {
|
||||
metadata := make(map[string]string)
|
||||
for k, v := range n.Metadata {
|
||||
metadata[k] = v
|
||||
}
|
||||
|
||||
nodes[i] = ®istry.Node{
|
||||
Id: n.Id,
|
||||
Address: n.Address,
|
||||
Metadata: metadata,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return ®istry.Service{
|
||||
Name: r.Name,
|
||||
Version: r.Version,
|
||||
Metadata: metadata,
|
||||
Endpoints: endpoints,
|
||||
Nodes: nodes,
|
||||
}
|
||||
}
|
@@ -4,10 +4,8 @@
|
||||
package go_micro_registry
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
@@ -768,235 +766,3 @@ var fileDescriptor_2f73432195c6499a = []byte{
|
||||
0x6f, 0x39, 0xac, 0xec, 0x3b, 0x1f, 0xeb, 0xfa, 0x37, 0x7d, 0xff, 0x57, 0x00, 0x00, 0x00, 0xff,
|
||||
0xff, 0x69, 0x33, 0x08, 0xdb, 0xde, 0x07, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// RegistryClient is the client API for Registry service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type RegistryClient interface {
|
||||
GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error)
|
||||
Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error)
|
||||
Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error)
|
||||
ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
|
||||
Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error)
|
||||
}
|
||||
|
||||
type registryClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewRegistryClient(cc *grpc.ClientConn) RegistryClient {
|
||||
return ®istryClient{cc}
|
||||
}
|
||||
|
||||
func (c *registryClient) GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
|
||||
out := new(GetResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/GetService", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *registryClient) Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) {
|
||||
out := new(EmptyResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Register", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *registryClient) Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) {
|
||||
out := new(EmptyResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Deregister", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *registryClient) ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
|
||||
out := new(ListResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/ListServices", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *registryClient) Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_Registry_serviceDesc.Streams[0], "/go.micro.registry.Registry/Watch", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := ®istryWatchClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Registry_WatchClient interface {
|
||||
Recv() (*Result, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type registryWatchClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *registryWatchClient) Recv() (*Result, error) {
|
||||
m := new(Result)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// RegistryServer is the server API for Registry service.
|
||||
type RegistryServer interface {
|
||||
GetService(context.Context, *GetRequest) (*GetResponse, error)
|
||||
Register(context.Context, *Service) (*EmptyResponse, error)
|
||||
Deregister(context.Context, *Service) (*EmptyResponse, error)
|
||||
ListServices(context.Context, *ListRequest) (*ListResponse, error)
|
||||
Watch(*WatchRequest, Registry_WatchServer) error
|
||||
}
|
||||
|
||||
func RegisterRegistryServer(s *grpc.Server, srv RegistryServer) {
|
||||
s.RegisterService(&_Registry_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Registry_GetService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RegistryServer).GetService(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.registry.Registry/GetService",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RegistryServer).GetService(ctx, req.(*GetRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Registry_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Service)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RegistryServer).Register(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.registry.Registry/Register",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RegistryServer).Register(ctx, req.(*Service))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Registry_Deregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Service)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RegistryServer).Deregister(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.registry.Registry/Deregister",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RegistryServer).Deregister(ctx, req.(*Service))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Registry_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RegistryServer).ListServices(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.registry.Registry/ListServices",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RegistryServer).ListServices(ctx, req.(*ListRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Registry_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(WatchRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(RegistryServer).Watch(m, ®istryWatchServer{stream})
|
||||
}
|
||||
|
||||
type Registry_WatchServer interface {
|
||||
Send(*Result) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type registryWatchServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *registryWatchServer) Send(m *Result) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
var _Registry_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.registry.Registry",
|
||||
HandlerType: (*RegistryServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetService",
|
||||
Handler: _Registry_GetService_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Register",
|
||||
Handler: _Registry_Register_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Deregister",
|
||||
Handler: _Registry_Deregister_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListServices",
|
||||
Handler: _Registry_ListServices_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Watch",
|
||||
Handler: _Registry_Watch_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "micro/go-micro/registry/service/proto/registry.proto",
|
||||
}
|
||||
|
@@ -89,7 +89,7 @@ func (s *serviceRegistry) GetService(name string) ([]*registry.Service, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var services []*registry.Service
|
||||
services := make([]*registry.Service, 0, len(rsp.Services))
|
||||
for _, service := range rsp.Services {
|
||||
services = append(services, ToService(service))
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func (s *serviceRegistry) ListServices() ([]*registry.Service, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var services []*registry.Service
|
||||
services := make([]*registry.Service, 0, len(rsp.Services))
|
||||
for _, service := range rsp.Services {
|
||||
services = append(services, ToService(service))
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ func values(v []*registry.Value) []*pb.Value {
|
||||
return []*pb.Value{}
|
||||
}
|
||||
|
||||
var vs []*pb.Value
|
||||
vs := make([]*pb.Value, 0, len(v))
|
||||
for _, vi := range v {
|
||||
vs = append(vs, &pb.Value{
|
||||
Name: vi.Name,
|
||||
@@ -26,7 +26,7 @@ func toValues(v []*pb.Value) []*registry.Value {
|
||||
return []*registry.Value{}
|
||||
}
|
||||
|
||||
var vs []*registry.Value
|
||||
vs := make([]*registry.Value, 0, len(v))
|
||||
for _, vi := range v {
|
||||
vs = append(vs, ®istry.Value{
|
||||
Name: vi.Name,
|
||||
@@ -38,7 +38,7 @@ func toValues(v []*pb.Value) []*registry.Value {
|
||||
}
|
||||
|
||||
func ToProto(s *registry.Service) *pb.Service {
|
||||
var endpoints []*pb.Endpoint
|
||||
endpoints := make([]*pb.Endpoint, 0, len(s.Endpoints))
|
||||
for _, ep := range s.Endpoints {
|
||||
var request, response *pb.Value
|
||||
|
||||
@@ -66,7 +66,7 @@ func ToProto(s *registry.Service) *pb.Service {
|
||||
})
|
||||
}
|
||||
|
||||
var nodes []*pb.Node
|
||||
nodes := make([]*pb.Node, 0, len(s.Nodes))
|
||||
|
||||
for _, node := range s.Nodes {
|
||||
nodes = append(nodes, &pb.Node{
|
||||
@@ -86,7 +86,7 @@ func ToProto(s *registry.Service) *pb.Service {
|
||||
}
|
||||
|
||||
func ToService(s *pb.Service) *registry.Service {
|
||||
var endpoints []*registry.Endpoint
|
||||
endpoints := make([]*registry.Endpoint, 0, len(s.Endpoints))
|
||||
for _, ep := range s.Endpoints {
|
||||
var request, response *registry.Value
|
||||
|
||||
@@ -114,7 +114,7 @@ func ToService(s *pb.Service) *registry.Service {
|
||||
})
|
||||
}
|
||||
|
||||
var nodes []*registry.Node
|
||||
nodes := make([]*registry.Node, 0, len(s.Nodes))
|
||||
for _, node := range s.Nodes {
|
||||
nodes = append(nodes, ®istry.Node{
|
||||
Id: node.Id,
|
||||
|
@@ -1,16 +1,18 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWatcher(t *testing.T) {
|
||||
testData := []*Service{
|
||||
&Service{
|
||||
{
|
||||
Name: "test1",
|
||||
Version: "1.0.1",
|
||||
Nodes: []*Node{
|
||||
&Node{
|
||||
{
|
||||
Id: "test1-1",
|
||||
Address: "10.0.0.1:10001",
|
||||
Metadata: map[string]string{
|
||||
@@ -19,11 +21,11 @@ func TestWatcher(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&Service{
|
||||
{
|
||||
Name: "test2",
|
||||
Version: "1.0.2",
|
||||
Nodes: []*Node{
|
||||
&Node{
|
||||
{
|
||||
Id: "test2-1",
|
||||
Address: "10.0.0.2:10002",
|
||||
Metadata: map[string]string{
|
||||
@@ -32,11 +34,11 @@ func TestWatcher(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&Service{
|
||||
{
|
||||
Name: "test3",
|
||||
Version: "1.0.3",
|
||||
Nodes: []*Node{
|
||||
&Node{
|
||||
{
|
||||
Id: "test3-1",
|
||||
Address: "10.0.0.3:10003",
|
||||
Metadata: map[string]string{
|
||||
@@ -76,8 +78,16 @@ func TestWatcher(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
travis := os.Getenv("TRAVIS")
|
||||
|
||||
var opts []Option
|
||||
|
||||
if travis == "true" {
|
||||
opts = append(opts, Timeout(time.Millisecond*100))
|
||||
}
|
||||
|
||||
// new registry
|
||||
r := NewRegistry()
|
||||
r := NewRegistry(opts...)
|
||||
|
||||
w, err := r.Watch()
|
||||
if err != nil {
|
||||
|
@@ -13,30 +13,25 @@ import (
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
// AdvertiseEventsTick is time interval in which the router advertises route updates
|
||||
AdvertiseEventsTick = 5 * time.Second
|
||||
// AdvertiseTableTick is time interval in which router advertises all routes found in routing table
|
||||
AdvertiseTableTick = 1 * time.Minute
|
||||
// AdvertiseFlushTick is time the yet unconsumed advertisements are flush i.e. discarded
|
||||
AdvertiseFlushTick = 15 * time.Second
|
||||
// AdvertSuppress is advert suppression threshold
|
||||
AdvertSuppress = 2000.0
|
||||
// AdvertRecover is advert recovery threshold
|
||||
AdvertRecover = 750.0
|
||||
// DefaultAdvertTTL is default advertisement TTL
|
||||
DefaultAdvertTTL = 1 * time.Minute
|
||||
// DeletePenalty penalises route deletion
|
||||
DeletePenalty = 1000.0
|
||||
// UpdatePenalty penalises route updates
|
||||
UpdatePenalty = 500.0
|
||||
// AdvertSuppress is advert suppression threshold
|
||||
AdvertSuppress = 200.0
|
||||
// AdvertRecover is advert recovery threshold
|
||||
AdvertRecover = 120.0
|
||||
// Penalty for routes processed multiple times
|
||||
Penalty = 100.0
|
||||
// PenaltyHalfLife is the time the advert penalty decays to half its value
|
||||
PenaltyHalfLife = 2.0
|
||||
PenaltyHalfLife = 30.0
|
||||
// MaxSuppressTime defines time after which the suppressed advert is deleted
|
||||
MaxSuppressTime = 5 * time.Minute
|
||||
)
|
||||
|
||||
var (
|
||||
// PenaltyDecay is a coefficient which controls the speed the advert penalty decays
|
||||
PenaltyDecay = math.Log(2) / PenaltyHalfLife
|
||||
)
|
||||
@@ -54,6 +49,7 @@ type router struct {
|
||||
wg *sync.WaitGroup
|
||||
|
||||
// advert subscribers
|
||||
sub sync.RWMutex
|
||||
subscribers map[string]chan *Advert
|
||||
}
|
||||
|
||||
@@ -191,7 +187,6 @@ func (r *router) watchRegistry(w registry.Watcher) error {
|
||||
defer func() {
|
||||
// close the exit channel when the go routine finishes
|
||||
close(exit)
|
||||
r.wg.Done()
|
||||
}()
|
||||
|
||||
// wait in the background for the router to stop
|
||||
@@ -199,6 +194,7 @@ func (r *router) watchRegistry(w registry.Watcher) error {
|
||||
r.wg.Add(1)
|
||||
go func() {
|
||||
defer w.Stop()
|
||||
defer r.wg.Done()
|
||||
|
||||
select {
|
||||
case <-r.exit:
|
||||
@@ -235,7 +231,6 @@ func (r *router) watchTable(w Watcher) error {
|
||||
defer func() {
|
||||
// close the exit channel when the go routine finishes
|
||||
close(exit)
|
||||
r.wg.Done()
|
||||
}()
|
||||
|
||||
// wait in the background for the router to stop
|
||||
@@ -243,6 +238,7 @@ func (r *router) watchTable(w Watcher) error {
|
||||
r.wg.Add(1)
|
||||
go func() {
|
||||
defer w.Stop()
|
||||
defer r.wg.Done()
|
||||
|
||||
select {
|
||||
case <-r.exit:
|
||||
@@ -278,10 +274,7 @@ func (r *router) watchTable(w Watcher) error {
|
||||
}
|
||||
|
||||
// publishAdvert publishes router advert to advert channel
|
||||
// NOTE: this might cease to be a dedicated method in the future
|
||||
func (r *router) publishAdvert(advType AdvertType, events []*Event) {
|
||||
defer r.advertWg.Done()
|
||||
|
||||
a := &Advert{
|
||||
Id: r.options.Id,
|
||||
Type: advType,
|
||||
@@ -290,24 +283,17 @@ func (r *router) publishAdvert(advType AdvertType, events []*Event) {
|
||||
Events: events,
|
||||
}
|
||||
|
||||
log.Debugf("Router publishing advert; %+v", a)
|
||||
r.RLock()
|
||||
r.sub.RLock()
|
||||
for _, sub := range r.subscribers {
|
||||
// check the exit chan first
|
||||
select {
|
||||
case <-r.exit:
|
||||
r.RUnlock()
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
// now send the message
|
||||
select {
|
||||
case sub <- a:
|
||||
default:
|
||||
case <-r.exit:
|
||||
r.sub.RUnlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
r.RUnlock()
|
||||
r.sub.RUnlock()
|
||||
}
|
||||
|
||||
// advertiseTable advertises the whole routing table to the network
|
||||
@@ -329,7 +315,10 @@ func (r *router) advertiseTable() error {
|
||||
if len(events) > 0 {
|
||||
log.Debugf("Router flushing table with %d events: %s", len(events), r.options.Id)
|
||||
r.advertWg.Add(1)
|
||||
go r.publishAdvert(RouteUpdate, events)
|
||||
go func() {
|
||||
defer r.advertWg.Done()
|
||||
r.publishAdvert(RouteUpdate, events)
|
||||
}()
|
||||
}
|
||||
case <-r.exit:
|
||||
return nil
|
||||
@@ -337,12 +326,12 @@ func (r *router) advertiseTable() error {
|
||||
}
|
||||
}
|
||||
|
||||
// routeAdvert contains a route event to be advertised
|
||||
type routeAdvert struct {
|
||||
// advert contains a route event to be advertised
|
||||
type advert struct {
|
||||
// event received from routing table
|
||||
event *Event
|
||||
// lastUpdate records the time of the last advert update
|
||||
lastUpdate time.Time
|
||||
// lastSeen records the time of the last advert update
|
||||
lastSeen time.Time
|
||||
// penalty is current advert penalty
|
||||
penalty float64
|
||||
// isSuppressed flags the advert suppression
|
||||
@@ -351,6 +340,51 @@ type routeAdvert struct {
|
||||
suppressTime time.Time
|
||||
}
|
||||
|
||||
// adverts maintains a map of router adverts
|
||||
type adverts map[uint64]*advert
|
||||
|
||||
// process processes advert
|
||||
// It updates advert timestamp, increments its penalty and
|
||||
// marks upresses or recovers it if it reaches configured thresholds
|
||||
func (m adverts) process(a *advert) error {
|
||||
// lookup advert in adverts
|
||||
hash := a.event.Route.Hash()
|
||||
a, ok := m[hash]
|
||||
if !ok {
|
||||
return fmt.Errorf("advert not found")
|
||||
}
|
||||
|
||||
// decay the event penalty
|
||||
delta := time.Since(a.lastSeen).Seconds()
|
||||
|
||||
// decay advert penalty
|
||||
a.penalty = a.penalty * math.Exp(-delta*PenaltyDecay)
|
||||
service := a.event.Route.Service
|
||||
address := a.event.Route.Address
|
||||
|
||||
// suppress/recover the event based on its penalty level
|
||||
switch {
|
||||
case a.penalty > AdvertSuppress && !a.isSuppressed:
|
||||
log.Debugf("Router suppressing advert %d %.2f for route %s %s", hash, a.penalty, service, address)
|
||||
a.isSuppressed = true
|
||||
a.suppressTime = time.Now()
|
||||
case a.penalty < AdvertRecover && a.isSuppressed:
|
||||
log.Debugf("Router recovering advert %d %.2f for route %s %s", hash, a.penalty, service, address)
|
||||
a.isSuppressed = false
|
||||
}
|
||||
|
||||
// if suppressed, checked how long has it been suppressed for
|
||||
if a.isSuppressed {
|
||||
// max suppression time threshold has been reached, delete the advert
|
||||
if time.Since(a.suppressTime) > MaxSuppressTime {
|
||||
delete(m, hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// advertiseEvents advertises routing table events
|
||||
// It suppresses unhealthy flapping events and advertises healthy events upstream.
|
||||
func (r *router) advertiseEvents() error {
|
||||
@@ -358,8 +392,8 @@ func (r *router) advertiseEvents() error {
|
||||
ticker := time.NewTicker(AdvertiseEventsTick)
|
||||
defer ticker.Stop()
|
||||
|
||||
// advertMap is a map of advert events
|
||||
advertMap := make(map[uint64]*routeAdvert)
|
||||
// adverts is a map of advert events
|
||||
adverts := make(adverts)
|
||||
|
||||
// routing table watcher
|
||||
tableWatcher, err := r.Watch()
|
||||
@@ -379,84 +413,98 @@ func (r *router) advertiseEvents() error {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
var events []*Event
|
||||
// collect all events which are not flapping
|
||||
for key, advert := range advertMap {
|
||||
// decay the event penalty
|
||||
delta := time.Since(advert.lastUpdate).Seconds()
|
||||
advert.penalty = advert.penalty * math.Exp(-delta*PenaltyDecay)
|
||||
|
||||
// suppress/recover the event based on its penalty level
|
||||
switch {
|
||||
case advert.penalty > AdvertSuppress && !advert.isSuppressed:
|
||||
log.Debugf("Router supressing advert %d for route %s", key, advert.event.Route.Address)
|
||||
advert.isSuppressed = true
|
||||
advert.suppressTime = time.Now()
|
||||
case advert.penalty < AdvertRecover && advert.isSuppressed:
|
||||
log.Debugf("Router recovering advert %d for route %s", key, advert.event.Route.Address)
|
||||
advert.isSuppressed = false
|
||||
}
|
||||
|
||||
// max suppression time threshold has been reached, delete the advert
|
||||
if advert.isSuppressed {
|
||||
if time.Since(advert.suppressTime) > MaxSuppressTime {
|
||||
delete(advertMap, key)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !advert.isSuppressed {
|
||||
e := new(Event)
|
||||
*e = *(advert.event)
|
||||
events = append(events, e)
|
||||
// delete the advert from the advertMap
|
||||
delete(advertMap, key)
|
||||
}
|
||||
// If we're not advertising any events then sip processing them entirely
|
||||
if r.options.Advertise == AdvertiseNone {
|
||||
continue
|
||||
}
|
||||
|
||||
// advertise all Update events to subscribers
|
||||
var events []*Event
|
||||
|
||||
// collect all events which are not flapping
|
||||
for key, advert := range adverts {
|
||||
// process the advert
|
||||
if err := adverts.process(advert); err != nil {
|
||||
log.Debugf("Router failed processing advert %d: %v", key, err)
|
||||
continue
|
||||
}
|
||||
// if suppressed go to the next advert
|
||||
if advert.isSuppressed {
|
||||
continue
|
||||
}
|
||||
|
||||
// if we only advertise local routes skip processing anything not link local
|
||||
if r.options.Advertise == AdvertiseLocal && advert.event.Route.Link != "local" {
|
||||
continue
|
||||
}
|
||||
|
||||
// copy the event and append
|
||||
e := new(Event)
|
||||
// this is ok, because router.Event only contains builtin types
|
||||
// and no references so this creates a deep copy of struct Event
|
||||
*e = *(advert.event)
|
||||
events = append(events, e)
|
||||
// delete the advert from adverts
|
||||
delete(adverts, key)
|
||||
}
|
||||
|
||||
// advertise events to subscribers
|
||||
if len(events) > 0 {
|
||||
r.advertWg.Add(1)
|
||||
log.Debugf("Router publishing %d events", len(events))
|
||||
go r.publishAdvert(RouteUpdate, events)
|
||||
r.advertWg.Add(1)
|
||||
go func() {
|
||||
defer r.advertWg.Done()
|
||||
r.publishAdvert(RouteUpdate, events)
|
||||
}()
|
||||
}
|
||||
case e := <-r.eventChan:
|
||||
// if event is nil, continue
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
log.Debugf("Router processing table event %s for service %s", e.Type, e.Route.Address)
|
||||
// determine the event penalty
|
||||
var penalty float64
|
||||
switch e.Type {
|
||||
case Update:
|
||||
penalty = UpdatePenalty
|
||||
case Delete:
|
||||
penalty = DeletePenalty
|
||||
}
|
||||
|
||||
// check if we have already registered the route
|
||||
hash := e.Route.Hash()
|
||||
advert, ok := advertMap[hash]
|
||||
if !ok {
|
||||
advert = &routeAdvert{
|
||||
event: e,
|
||||
penalty: penalty,
|
||||
lastUpdate: time.Now(),
|
||||
}
|
||||
advertMap[hash] = advert
|
||||
// If we're not advertising any events then skip processing them entirely
|
||||
if r.options.Advertise == AdvertiseNone {
|
||||
continue
|
||||
}
|
||||
|
||||
// override the route event only if the last event was different
|
||||
if advert.event.Type != e.Type {
|
||||
advert.event = e
|
||||
// if we only advertise local routes skip processing anything not link local
|
||||
if r.options.Advertise == AdvertiseLocal && e.Route.Link != "local" {
|
||||
continue
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
log.Debugf("Router processing table event %s for service %s %s", e.Type, e.Route.Service, e.Route.Address)
|
||||
|
||||
// check if we have already registered the route
|
||||
hash := e.Route.Hash()
|
||||
a, ok := adverts[hash]
|
||||
if !ok {
|
||||
a = &advert{
|
||||
event: e,
|
||||
penalty: Penalty,
|
||||
lastSeen: now,
|
||||
}
|
||||
adverts[hash] = a
|
||||
continue
|
||||
}
|
||||
|
||||
// override the route event only if the previous event was different
|
||||
if a.event.Type != e.Type {
|
||||
a.event = e
|
||||
}
|
||||
|
||||
// process the advert
|
||||
if err := adverts.process(a); err != nil {
|
||||
log.Debugf("Router error processing advert %d: %v", hash, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// update event penalty and timestamp
|
||||
advert.lastUpdate = time.Now()
|
||||
advert.penalty += penalty
|
||||
log.Debugf("Router advert %d for route %s event penalty: %f", hash, advert.event.Route.Address, advert.penalty)
|
||||
a.lastSeen = now
|
||||
// increment the penalty
|
||||
a.penalty += Penalty
|
||||
log.Debugf("Router advert %d for route %s %s event penalty: %f", hash, a.event.Route.Service, a.event.Route.Address, a.penalty)
|
||||
case <-r.exit:
|
||||
// first wait for the advertiser to finish
|
||||
r.advertWg.Wait()
|
||||
@@ -476,6 +524,7 @@ func (r *router) close() {
|
||||
for range r.eventChan {
|
||||
}
|
||||
|
||||
r.sub.RLock()
|
||||
// close advert subscribers
|
||||
for id, sub := range r.subscribers {
|
||||
// close the channel
|
||||
@@ -484,6 +533,7 @@ func (r *router) close() {
|
||||
// delete the subscriber
|
||||
delete(r.subscribers, id)
|
||||
}
|
||||
r.sub.RUnlock()
|
||||
}
|
||||
|
||||
// mark the router as Stopped and set its Error to nil
|
||||
@@ -605,9 +655,16 @@ func (r *router) Advertise() (<-chan *Advert, error) {
|
||||
// create event channels
|
||||
r.eventChan = make(chan *Event)
|
||||
|
||||
// create advert channel
|
||||
advertChan := make(chan *Advert, 128)
|
||||
r.subscribers[uuid.New().String()] = advertChan
|
||||
|
||||
// advertise your presence
|
||||
r.advertWg.Add(1)
|
||||
go r.publishAdvert(Announce, events)
|
||||
go func() {
|
||||
defer r.advertWg.Done()
|
||||
r.publishAdvert(Announce, events)
|
||||
}()
|
||||
|
||||
r.wg.Add(1)
|
||||
go func() {
|
||||
@@ -631,10 +688,7 @@ func (r *router) Advertise() (<-chan *Advert, error) {
|
||||
// mark router as Running and set its Error to nil
|
||||
r.status = Status{Code: Advertising, Error: nil}
|
||||
|
||||
// create advert channel
|
||||
advertChan := make(chan *Advert, 128)
|
||||
r.subscribers[uuid.New().String()] = advertChan
|
||||
|
||||
log.Debugf("Router starting to advertise")
|
||||
return advertChan, nil
|
||||
case Stopped:
|
||||
return nil, fmt.Errorf("not running")
|
||||
@@ -665,7 +719,7 @@ func (r *router) Process(a *Advert) error {
|
||||
// create a copy of the route
|
||||
route := event.Route
|
||||
action := event.Type
|
||||
log.Debugf("Router %s applying %s from router %s for address: %s", r.options.Id, action, route.Router, route.Address)
|
||||
log.Debugf("Router %s applying %s from router %s for service %s %s", r.options.Id, action, route.Router, route.Service, route.Address)
|
||||
if err := r.manageRoute(route, fmt.Sprintf("%s", action)); err != nil {
|
||||
return fmt.Errorf("failed applying action %s to routing table: %s", action, err)
|
||||
}
|
||||
@@ -676,12 +730,18 @@ func (r *router) Process(a *Advert) error {
|
||||
|
||||
// flushRouteEvents returns a slice of events, one per each route in the routing table
|
||||
func (r *router) flushRouteEvents(evType EventType) ([]*Event, error) {
|
||||
// Do not advertise anything
|
||||
if r.options.Advertise == AdvertiseNone {
|
||||
return []*Event{}, nil
|
||||
}
|
||||
|
||||
// list all routes
|
||||
routes, err := r.table.List()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed listing routes: %s", err)
|
||||
}
|
||||
|
||||
// Return all the routes
|
||||
if r.options.Advertise == AdvertiseAll {
|
||||
// build a list of events to advertise
|
||||
events := make([]*Event, len(routes))
|
||||
@@ -696,24 +756,34 @@ func (r *router) flushRouteEvents(evType EventType) ([]*Event, error) {
|
||||
return events, nil
|
||||
}
|
||||
|
||||
// routeMap stores optimal routes per service
|
||||
// routeMap stores the routes we're going to advertise
|
||||
bestRoutes := make(map[string]Route)
|
||||
|
||||
// set whether we're advertising only local
|
||||
advertiseLocal := r.options.Advertise == AdvertiseLocal
|
||||
|
||||
// go through all routes found in the routing table and collapse them to optimal routes
|
||||
for _, route := range routes {
|
||||
// if we're only advertising local routes
|
||||
if advertiseLocal && route.Link != "local" {
|
||||
continue
|
||||
}
|
||||
|
||||
// now we're going to find the best routes
|
||||
|
||||
routeKey := route.Service + "@" + route.Network
|
||||
optimal, ok := bestRoutes[routeKey]
|
||||
current, ok := bestRoutes[routeKey]
|
||||
if !ok {
|
||||
bestRoutes[routeKey] = route
|
||||
continue
|
||||
}
|
||||
// if the current optimal route metric is higher than routing table route, replace it
|
||||
if optimal.Metric > route.Metric {
|
||||
if current.Metric > route.Metric {
|
||||
bestRoutes[routeKey] = route
|
||||
continue
|
||||
}
|
||||
// if the metrics are the same, prefer advertising your own route
|
||||
if optimal.Metric == route.Metric {
|
||||
if current.Metric == route.Metric {
|
||||
if route.Router == r.options.Id {
|
||||
bestRoutes[routeKey] = route
|
||||
continue
|
||||
@@ -721,7 +791,7 @@ func (r *router) flushRouteEvents(evType EventType) ([]*Event, error) {
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Router advertising %d best routes out of %d", len(bestRoutes), len(routes))
|
||||
log.Debugf("Router advertising %d %s routes out of %d", len(bestRoutes), r.options.Advertise, len(routes))
|
||||
|
||||
// build a list of events to advertise
|
||||
events := make([]*Event, len(bestRoutes))
|
||||
@@ -749,7 +819,10 @@ func (r *router) Solicit() error {
|
||||
|
||||
// advertise the routes
|
||||
r.advertWg.Add(1)
|
||||
go r.publishAdvert(RouteUpdate, events)
|
||||
go func() {
|
||||
defer r.advertWg.Done()
|
||||
r.publishAdvert(RouteUpdate, events)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -778,19 +851,27 @@ func (r *router) Status() Status {
|
||||
// Stop stops the router
|
||||
func (r *router) Stop() error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
log.Debugf("Router shutting down")
|
||||
|
||||
switch r.status.Code {
|
||||
case Stopped, Error:
|
||||
r.Unlock()
|
||||
return r.status.Error
|
||||
case Running, Advertising:
|
||||
// close all the channels
|
||||
// NOTE: close marks the router status as Stopped
|
||||
r.close()
|
||||
}
|
||||
r.Unlock()
|
||||
|
||||
log.Debugf("Router waiting for all goroutines to finish")
|
||||
|
||||
// wait for all goroutines to finish
|
||||
r.wg.Wait()
|
||||
|
||||
log.Debugf("Router successfully stopped")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
132
router/default_test.go
Normal file
132
router/default_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/registry/memory"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
func routerTestSetup() Router {
|
||||
r := memory.NewRegistry()
|
||||
return newRouter(Registry(r))
|
||||
}
|
||||
|
||||
func TestRouterStartStop(t *testing.T) {
|
||||
r := routerTestSetup()
|
||||
|
||||
log.Debugf("TestRouterStartStop STARTING")
|
||||
if err := r.Start(); err != nil {
|
||||
t.Errorf("failed to start router: %v", err)
|
||||
}
|
||||
|
||||
_, err := r.Advertise()
|
||||
if err != nil {
|
||||
t.Errorf("failed to start advertising: %v", err)
|
||||
}
|
||||
|
||||
if err := r.Stop(); err != nil {
|
||||
t.Errorf("failed to stop router: %v", err)
|
||||
}
|
||||
log.Debugf("TestRouterStartStop STOPPED")
|
||||
}
|
||||
|
||||
func TestRouterAdvertise(t *testing.T) {
|
||||
r := routerTestSetup()
|
||||
|
||||
// lower the advertise interval
|
||||
AdvertiseEventsTick = 500 * time.Millisecond
|
||||
AdvertiseTableTick = 1 * time.Second
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
t.Errorf("failed to start router: %v", err)
|
||||
}
|
||||
|
||||
ch, err := r.Advertise()
|
||||
if err != nil {
|
||||
t.Errorf("failed to start advertising: %v", err)
|
||||
}
|
||||
|
||||
// receive announce event
|
||||
ann := <-ch
|
||||
log.Debugf("received announce advert: %v", ann)
|
||||
|
||||
// Generate random unique routes
|
||||
nrRoutes := 5
|
||||
routes := make([]Route, nrRoutes)
|
||||
route := Route{
|
||||
Service: "dest.svc",
|
||||
Address: "dest.addr",
|
||||
Gateway: "dest.gw",
|
||||
Network: "dest.network",
|
||||
Router: "src.router",
|
||||
Link: "det.link",
|
||||
Metric: 10,
|
||||
}
|
||||
|
||||
for i := 0; i < nrRoutes; i++ {
|
||||
testRoute := route
|
||||
testRoute.Service = fmt.Sprintf("%s-%d", route.Service, i)
|
||||
routes[i] = testRoute
|
||||
}
|
||||
|
||||
var advertErr error
|
||||
|
||||
createDone := make(chan bool)
|
||||
errChan := make(chan error)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
wg.Done()
|
||||
defer close(createDone)
|
||||
for _, route := range routes {
|
||||
log.Debugf("Creating route %v", route)
|
||||
if err := r.Table().Create(route); err != nil {
|
||||
log.Debugf("Failed to create route: %v", err)
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var adverts int
|
||||
readDone := make(chan bool)
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
readDone <- true
|
||||
}()
|
||||
for advert := range ch {
|
||||
select {
|
||||
case advertErr = <-errChan:
|
||||
t.Errorf("failed advertising events: %v", advertErr)
|
||||
default:
|
||||
// do nothing for now
|
||||
log.Debugf("Router advert received: %v", advert)
|
||||
adverts += len(advert.Events)
|
||||
}
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// done adding routes to routing table
|
||||
<-createDone
|
||||
// done reading adverts from the routing table
|
||||
<-readDone
|
||||
|
||||
if adverts != nrRoutes {
|
||||
t.Errorf("Expected %d adverts, received: %d", nrRoutes, adverts)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if err := r.Stop(); err != nil {
|
||||
t.Errorf("failed to stop router: %v", err)
|
||||
}
|
||||
}
|
@@ -22,7 +22,7 @@ func (r *Router) Lookup(ctx context.Context, req *pb.LookupRequest, resp *pb.Loo
|
||||
return errors.InternalServerError("go.micro.router", "failed to lookup routes: %v", err)
|
||||
}
|
||||
|
||||
var respRoutes []*pb.Route
|
||||
respRoutes := make([]*pb.Route, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
respRoute := &pb.Route{
|
||||
Service: route.Service,
|
||||
@@ -31,7 +31,7 @@ func (r *Router) Lookup(ctx context.Context, req *pb.LookupRequest, resp *pb.Loo
|
||||
Network: route.Network,
|
||||
Router: route.Router,
|
||||
Link: route.Link,
|
||||
Metric: int64(route.Metric),
|
||||
Metric: route.Metric,
|
||||
}
|
||||
respRoutes = append(respRoutes, respRoute)
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func (r *Router) Advertise(ctx context.Context, req *pb.Request, stream pb.Route
|
||||
Network: event.Route.Network,
|
||||
Router: event.Route.Router,
|
||||
Link: event.Route.Link,
|
||||
Metric: int64(event.Route.Metric),
|
||||
Metric: event.Route.Metric,
|
||||
}
|
||||
e := &pb.Event{
|
||||
Type: pb.EventType(event.Type),
|
||||
@@ -108,7 +108,7 @@ func (r *Router) Process(ctx context.Context, req *pb.Advert, rsp *pb.ProcessRes
|
||||
Network: event.Route.Network,
|
||||
Router: event.Route.Router,
|
||||
Link: event.Route.Link,
|
||||
Metric: int(event.Route.Metric),
|
||||
Metric: event.Route.Metric,
|
||||
}
|
||||
|
||||
events[i] = &router.Event{
|
||||
@@ -174,7 +174,7 @@ func (r *Router) Watch(ctx context.Context, req *pb.WatchRequest, stream pb.Rout
|
||||
Network: event.Route.Network,
|
||||
Router: event.Route.Router,
|
||||
Link: event.Route.Link,
|
||||
Metric: int64(event.Route.Metric),
|
||||
Metric: event.Route.Metric,
|
||||
}
|
||||
|
||||
tableEvent := &pb.Event{
|
||||
|
@@ -20,7 +20,7 @@ func (t *Table) Create(ctx context.Context, route *pb.Route, resp *pb.CreateResp
|
||||
Network: route.Network,
|
||||
Router: route.Router,
|
||||
Link: route.Link,
|
||||
Metric: int(route.Metric),
|
||||
Metric: route.Metric,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.router", "failed to create route: %s", err)
|
||||
@@ -37,7 +37,7 @@ func (t *Table) Update(ctx context.Context, route *pb.Route, resp *pb.UpdateResp
|
||||
Network: route.Network,
|
||||
Router: route.Router,
|
||||
Link: route.Link,
|
||||
Metric: int(route.Metric),
|
||||
Metric: route.Metric,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.router", "failed to update route: %s", err)
|
||||
@@ -54,7 +54,7 @@ func (t *Table) Delete(ctx context.Context, route *pb.Route, resp *pb.DeleteResp
|
||||
Network: route.Network,
|
||||
Router: route.Router,
|
||||
Link: route.Link,
|
||||
Metric: int(route.Metric),
|
||||
Metric: route.Metric,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.router", "failed to delete route: %s", err)
|
||||
@@ -70,7 +70,7 @@ func (t *Table) List(ctx context.Context, req *pb.Request, resp *pb.ListResponse
|
||||
return errors.InternalServerError("go.micro.router", "failed to list routes: %s", err)
|
||||
}
|
||||
|
||||
var respRoutes []*pb.Route
|
||||
respRoutes := make([]*pb.Route, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
respRoute := &pb.Route{
|
||||
Service: route.Service,
|
||||
@@ -79,7 +79,7 @@ func (t *Table) List(ctx context.Context, req *pb.Request, resp *pb.ListResponse
|
||||
Network: route.Network,
|
||||
Router: route.Router,
|
||||
Link: route.Link,
|
||||
Metric: int64(route.Metric),
|
||||
Metric: route.Metric,
|
||||
}
|
||||
respRoutes = append(respRoutes, respRoute)
|
||||
}
|
||||
@@ -95,7 +95,7 @@ func (t *Table) Query(ctx context.Context, req *pb.QueryRequest, resp *pb.QueryR
|
||||
return errors.InternalServerError("go.micro.router", "failed to lookup routes: %s", err)
|
||||
}
|
||||
|
||||
var respRoutes []*pb.Route
|
||||
respRoutes := make([]*pb.Route, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
respRoute := &pb.Route{
|
||||
Service: route.Service,
|
||||
@@ -104,7 +104,7 @@ func (t *Table) Query(ctx context.Context, req *pb.QueryRequest, resp *pb.QueryR
|
||||
Network: route.Network,
|
||||
Router: route.Router,
|
||||
Link: route.Link,
|
||||
Metric: int64(route.Metric),
|
||||
Metric: route.Metric,
|
||||
}
|
||||
respRoutes = append(respRoutes, respRoute)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user