Compare commits

..

560 Commits

Author SHA1 Message Date
Asim Aslam
d3140c0fc2 Merge pull request #867 from milosgajdos83/rlock-mess
Avoid recursive RLock()
2019-10-18 11:35:08 +01:00
Milos Gajdos
3d5d9be02a Avoid recursive calls to RLock()
Topology calls itsel recursively invoking RLock. This, according to go
documentation is wrong. This commit moves the body of Topology function
to a non-thread safe unexported function to keep locsk at check!
2019-10-18 11:26:43 +01:00
Asim Aslam
5c38f38dd9 No need to lock here since Topology read locks and makes copies 2019-10-18 11:26:43 +01:00
Asim Aslam
63fd8b9d1b Merge pull request #864 from micro/strip-topic
strip topic from http broker subscribe service name
2019-10-17 18:48:46 +01:00
Asim Aslam
3aedea4c56 strip topic from http broker subscribe service name 2019-10-17 18:37:37 +01:00
Asim Aslam
0da9dff077 Merge pull request #863 from micro/certmagice2e
E2E tests for certmagic ACME provider
2019-10-17 16:42:33 +01:00
Jake Sanders
05774f2c76 Don't touch go.mod 2019-10-17 16:35:09 +01:00
Jake Sanders
4885bba2ac E2E tests for certmagic ACME provider
* Actually set the CA
* Fix the certmangic.storage interface to return the correct error type
* Write an e2e test for certmagic against the let's encrypt staging CA
2019-10-17 16:31:02 +01:00
Asim Aslam
9d559848c2 Merge pull request #862 from milosgajdos83/tunnel-cleanup
Cleanup of tunnel.Dial(). Clean up network channel processors
2019-10-16 21:19:30 +01:00
Milos Gajdos
2ae583ce94 Cleanup of tunnel dial code. Clean up network channel processors 2019-10-16 20:44:22 +01:00
Asim Aslam
7c1e22b607 Merge pull request #861 from micro/certmagicstorage
Distributed storage for certmagic
2019-10-16 14:10:02 +01:00
Jake Sanders
7d2afa34a0 Implementation and tests for certmagic.Storage interface 2019-10-16 12:58:14 +01:00
Jake Sanders
a6e95d389f Implementation of certmagic storage using micro's store and sync packages 2019-10-15 19:32:20 +01:00
Asim Aslam
b1d5dc20fa Merge pull request #860 from micro/tunnel-mode
Tunnel mode
2019-10-15 16:14:38 +01:00
Asim Aslam
be5093798b Use DialMode/ListenMode 2019-10-15 16:08:38 +01:00
Asim Aslam
3759c9c091 Merge pull request #859 from milosgajdos83/handle-channel-conn-errors
Handle tunnel session Accept errors gracefully
2019-10-15 16:05:19 +01:00
Milos Gajdos
4936a2e1a5 Exponential backoff for failed accept connections 2019-10-15 15:58:33 +01:00
Asim Aslam
ca934951ad Use multicast on network/control channels 2019-10-15 15:57:13 +01:00
Asim Aslam
ca18089382 Fix bugs related to needing to send Broadcast 2019-10-15 15:55:08 +01:00
Asim Aslam
7b1f5584ab Tunnel mode 2019-10-15 15:40:04 +01:00
Milos Gajdos
fed5af68e6 Handle Accept errors gracefully.
Originally when Accept fails we log the error and let the program flow
continue. This can lead to us spawning handling connection go routines
on nil connections which in turn leads to Go panics.
2019-10-15 15:07:28 +01:00
Asim Aslam
fdfeb437f9 Merge pull request #856 from micro/cloudflare
Cloudflare Store implementation for workers KV
2019-10-15 14:29:34 +01:00
Jake Sanders
a46133f059 cloudflare workers KV Store implementation 2019-10-15 12:35:45 +01:00
Jake Sanders
9bd0a8f3b5 Update go.mod for cloudflare 2019-10-15 12:35:20 +01:00
Asim Aslam
44b794722e rcache becomes cache 2019-10-14 22:39:26 +01:00
Asim Aslam
247249050b move mutex to memory 2019-10-14 22:38:22 +01:00
Asim Aslam
b1fed01752 add network name to node 2019-10-14 22:26:23 +01:00
Asim Aslam
df1e680256 Merge pull request #854 from micro/lock-http
Lock http
2019-10-14 22:01:25 +01:00
Asim Aslam
854b01c20c Add acquire/release to http path 2019-10-14 21:52:18 +01:00
Asim Aslam
745299bce5 add http lock implementation 2019-10-14 21:39:25 +01:00
Asim Aslam
607fdb3fcb Merge pull request #852 from micro/mutex
add mutex lock implementation
2019-10-14 15:23:59 +01:00
Asim Aslam
a1342c23fb add mutex lock implementation 2019-10-14 15:17:25 +01:00
Asim Aslam
1cea2f5bba Merge pull request #850 from micro/acmetypo
TLS -> ToS
2019-10-14 12:10:55 +01:00
Jake Sanders
a1b4786682 TLS -> ToS 2019-10-14 12:04:49 +01:00
Asim Aslam
b701da6d69 Merge pull request #849 from micro/connect-init
Connect init
2019-10-13 18:40:11 +01:00
Asim Aslam
f77df51f60 Support reconnects 2019-10-13 18:36:22 +01:00
Asim Aslam
d6c6e7815e Spaces not tabs 2019-10-13 12:40:53 +01:00
Asim Aslam
01492997ea add Network.Init method 2019-10-13 12:38:13 +01:00
Asim Aslam
174f1b857c Network handler moves to service/handler 2019-10-13 12:37:56 +01:00
Asim Aslam
5029d80e68 add Network.Connect handler and network/metadata fields to node 2019-10-13 12:37:39 +01:00
Asim Aslam
b59c5a4488 move network handler to service/handler 2019-10-13 12:37:13 +01:00
Asim Aslam
f7f65b82e6 Cleanup registry handler/service 2019-10-13 12:23:13 +01:00
Asim Aslam
2e47fdc6f5 Check the node map to avoid dupes in resolved nodes 2019-10-12 20:26:06 +01:00
Asim Aslam
18ea19a122 Regenerate go.mod 2019-10-12 12:04:55 +01:00
Asim Aslam
4d75b936f8 Merge pull request #846 from theophanous/master
fix: bumped quic-go version to v0.12.1
2019-10-11 18:24:45 +01:00
Peter Theophanous
62aaa72715 fix: bumped quic-go version to v0.12.1 2019-10-11 18:16:56 +01:00
Asim Aslam
8c344ed55b Merge pull request #839 from theophanous/master
bumped quic-go version to v0.12.1
2019-10-11 17:26:48 +01:00
Peter Theophanous
db843c8d87 reset orig 2019-10-11 17:15:20 +01:00
Asim Aslam
dd7677e6cc Add nil check for acme provider 2019-10-11 16:52:57 +01:00
Asim Aslam
a4f0dd8939 Merge pull request #845 from micro/certmagic
Implementation of CertMagic as the ACME provider
2019-10-11 16:52:21 +01:00
Jake Sanders
591e87448b Travis doesn't let us bind :443 2019-10-11 16:47:12 +01:00
Jake Sanders
09a202ccf0 Merge branch 'master' of https://github.com/micro/go-micro into certmagic 2019-10-11 16:25:28 +01:00
Jake Sanders
723c17fdd7 Implementation of certmagic as an ACME provider 2019-10-11 16:25:15 +01:00
Jake Sanders
9bd96d4cc1 Update go.mod for ACME changes 2019-10-11 16:24:25 +01:00
Asim Aslam
9bfe4d9bf7 Merge pull request #844 from micro/store
Store service implementation
2019-10-11 14:49:44 +01:00
Asim Aslam
76eee089e3 Add store service client 2019-10-11 14:44:42 +01:00
Asim Aslam
cfa2b668e2 go fmt 2019-10-11 14:44:34 +01:00
Asim Aslam
a96f6adf07 store handler implementation 2019-10-11 14:08:50 +01:00
Asim Aslam
49fe5d9fd5 Merge pull request #843 from milosgajdos83/dead-code
Clean up dead tunnel code
2019-10-11 11:15:29 +01:00
Milos Gajdos
21469a0427 Clean up dead tunnel code
Running go vet on tunnel package returns:
$ go vet ./...
./default.go:929:2: unreachable code
./link.go:104:2: unreachable code
./listener.go:184:2: unreachable code
./session.go:241:2: unreachable code
2019-10-11 11:02:45 +01:00
Asim Aslam
e351e9518f Merge pull request #842 from milosgajdos83/cache-status
Check cache status error
2019-10-11 10:55:35 +01:00
Milos Gajdos
fc89c9831e heck cache status error 2019-10-11 10:47:42 +01:00
Peter Theophanous
5e5d57d954 bumped quic-go version to v0.12.1 2019-10-10 22:23:33 +01:00
Asim Aslam
98e1f2c2d3 Merge pull request #838 from micro/etcd
Use etcd serializable option
2019-10-10 19:22:10 +01:00
Asim Aslam
59a3e7d4f4 Use etcd serializable option 2019-10-10 19:16:31 +01:00
Asim Aslam
1be6ec9b3c Merge pull request #837 from milosgajdos83/prune-dead-router-peers
Prune routes from routers that are not in your peer graph
2019-10-10 16:12:56 +01:00
Milos Gajdos
f6931f3da7 Prune routes from routers that are not in your peer graph 2019-10-10 15:28:27 +01:00
Asim Aslam
b2f99a27b7 Visit all the nodes in flatten 2019-10-10 14:35:11 +01:00
Asim Aslam
1f5ebf330d Merge pull request #836 from micro/prune-address
Prune the peer address
2019-10-10 11:43:19 +01:00
Asim Aslam
0dee11e006 Prune the peer address 2019-10-10 11:25:28 +01:00
Asim Aslam
b55018eaa1 Merge pull request #833 from orbli/patch-1
Add dialoptions and calloptions
2019-10-10 07:40:48 +01:00
orb li
77108771db Conceptual deliverable 2019-10-10 13:55:16 +08:00
Asim Aslam
5a6e73d4a8 Merge pull request #835 from milosgajdos83/router-strategy
Router strategy
2019-10-09 19:18:59 +01:00
Milos Gajdos
7a4bff4e9f Changed names of some variables. 2019-10-09 19:08:24 +01:00
Milos Gajdos
d5ce96da24 Avoid locking on reading strategy for now 2019-10-09 18:19:48 +01:00
Milos Gajdos
837597fe6f Make Optimal strategy default. Collapse routing tables based on strategy 2019-10-09 17:24:38 +01:00
Milos Gajdos
96e564e402 Add router advertisement Strategy option to router. 2019-10-09 17:24:38 +01:00
Asim Aslam
fe94237448 Update router querying method (#834)
* Add address to router query options. Drop Query interface for QueryOptions

* Cleanup isMatch function

* Update network proto
2019-10-09 17:13:52 +01:00
Jake Sanders
107b7419b7 Start abstracting away the ACME provider (#830)
* Start abstracting away the ACME provider

* Move ACME to interface with sub-package implementations

* Addressing comments

* Library -> Provider

* Missed a couple of Library -> Provider

* One more Library -> Provider

* remove constants
2019-10-09 16:42:05 +01:00
orb li
226d55d752 Adding dependency 2019-10-09 16:48:45 +08:00
orb li
88ef785127 Add dialoptions and calloptions 2019-10-09 15:56:39 +08:00
Asim Aslam
44473f954f Merge pull request #829 from milosgajdos83/limit-net-connections
Limit the number of outbound connections to MaxConnections
2019-10-08 15:56:45 +01:00
Milos Gajdos
fe5846603a Only limit the number of nodes returned by network resolver. 2019-10-08 15:48:52 +01:00
Milos Gajdos
61800fb7d7 Fix typo: MaxCconnections -> MaxConnections 2019-10-08 15:15:50 +01:00
Milos Gajdos
ec2fbde979 Limit the number of outbound connections to MaxConnections
This commit also fixes control channel shenanigans:
- recover error in control channel accept
2019-10-08 14:48:04 +01:00
Asim Aslam
b886dd4b8f Merge pull request #828 from micro/net-lookup
Use dns resolver on peer nodes
2019-10-08 10:36:04 +01:00
Asim Aslam
94adeebed4 Use dns resolver on peer nodes 2019-10-08 09:25:23 +01:00
Asim Aslam
d043ca15c1 Merge pull request #827 from micro/resolver
Add dns net.LookupHost resolver!
2019-10-08 09:08:34 +01:00
Asim Aslam
ad823d5177 Add dns net.LookupHost resolver! 2019-10-08 09:04:13 +01:00
Asim Aslam
89d71417f5 Merge pull request #825 from milosgajdos83/net-chan-nodes
Recover net channel Accept() errors. Init tunnel nodes before tunnel.Connect()
2019-10-07 20:29:45 +01:00
Milos Gajdos
9d9683b6f9 Recover net channel Accept errors. Init tunnel nodes before Connect 2019-10-07 19:09:04 +01:00
Asim Aslam
0edcd5c8dc Merge pull request #824 from micro/tunnel
wait for response on accept message
2019-10-07 18:33:00 +01:00
Asim Aslam
2e1432d5dc wait for response on accept message 2019-10-07 18:29:49 +01:00
Asim Aslam
e4f8b5de70 Merge pull request #823 from micro/list-services
Support listing full service info in etcd
2019-10-07 16:15:30 +01:00
Asim Aslam
e9dcff49e0 Support listing full service info in etcd 2019-10-07 16:11:52 +01:00
Asim Aslam
fa6590f999 Merge pull request #822 from micro/service
Add Name to Service
2019-10-07 08:43:07 +01:00
Asim Aslam
fd8a0fb2f5 Update internal service definition 2019-10-07 08:34:15 +01:00
Asim Aslam
b594547408 Add service Name 2019-10-07 08:32:28 +01:00
Asim Aslam
2c00e726b6 Decode and hash the existing node 2019-10-06 13:43:41 +01:00
Asim Aslam
68a3fc7996 Merge pull request #820 from micro/etcd-reg
Fix etcd registry lease processing and suppression
2019-10-06 10:03:38 +01:00
Asim Aslam
2fb2d7145e Fix etcd registry lease processing and suppression 2019-10-06 09:54:26 +01:00
Asim Aslam
6fe9f2a958 Merge pull request #815 from micro/broker
Add broker service implementation
2019-10-04 17:23:29 +01:00
Asim Aslam
86984a8a8a Extend the stream timeout 2019-10-04 16:44:21 +01:00
Asim Aslam
cfb846ee7e Fix race in cache 2019-10-04 16:40:21 +01:00
Asim Aslam
e36960612a go fmt 2019-10-04 16:40:16 +01:00
Asim Aslam
04320d69ff Fix and comment broker service 2019-10-04 16:30:03 +01:00
Asim Aslam
c4b6d0f3a8 fix major deadlock in registry cache 2019-10-04 16:29:56 +01:00
Asim Aslam
3c6b6553fb Use peerAddress as the thing to listen on 2019-10-03 18:35:54 +01:00
Asim Aslam
d5658ab0b0 Merge pull request #816 from micro/net-advertise
Advertise your peer address as advertised address
2019-10-03 17:42:11 +01:00
Asim Aslam
2244eb8597 Advertise your peer address as advertised address 2019-10-03 17:37:29 +01:00
Asim Aslam
05eacd74c8 Add logging for broker handler 2019-10-03 17:30:37 +01:00
Asim Aslam
b80654bf7e Add broker service to config/cmd 2019-10-03 16:22:26 +01:00
Asim Aslam
0941a0f031 Merge pull request #814 from milosgajdos83/etcd-port
Append a port to address if it does not exist
2019-10-03 16:20:43 +01:00
Asim Aslam
4de346920f Add broker service implementation 2019-10-03 16:19:02 +01:00
Milos Gajdos
b8815dff14 Append a port to address if it does not exist 2019-10-03 16:16:25 +01:00
Asim Aslam
b1163b9dee Fix breaking import 2019-10-03 11:26:24 +01:00
Asim Aslam
af5d7a3420 Move the remaining consul cruft to go-plugins 2019-10-03 11:22:35 +01:00
Asim Aslam
b5f33b2aaa Rename Dump to Sync 2019-10-03 09:56:25 +01:00
Asim Aslam
a9c85eda68 Merge pull request #813 from micro/store
Move out consul sync/lock and store. Move data/store to store
2019-10-03 09:51:21 +01:00
Asim Aslam
b5ca40a91a Move out consul sync/lock and store. Move data/store to store 2019-10-03 09:46:20 +01:00
Asim Aslam
b81bb07afc Merge pull request #812 from micro/gossip
Remove gossip registry
2019-10-03 09:36:14 +01:00
Asim Aslam
8d2b12258f Remove gossip registry 2019-10-03 09:29:48 +01:00
Asim Aslam
31026da2a1 Update etcd.go
Use /micro/registry as the etcd key prefix
2019-10-02 20:33:59 +01:00
Asim Aslam
1129803bcb Merge pull request #810 from milosgajdos83/etcd
First commit to add etcd registry support
2019-10-02 20:27:38 +01:00
Milos Gajdos
25148af44c First commit to add etcd registry support 2019-10-02 18:56:53 +01:00
Asim Aslam
36675aff1e Merge pull request #809 from micro/log-prefix
Add ability to set log prefix
2019-10-02 17:47:27 +01:00
Asim Aslam
b6db0d2663 Add ability to set log prefix 2019-10-02 17:42:34 +01:00
Asim Aslam
2370fb1209 Set gateway to node address rather than id 2019-10-02 15:52:31 +01:00
Asim Aslam
519e8a7213 Merge pull request #808 from micro/net-address
Hash the network address
2019-10-02 15:27:07 +01:00
Asim Aslam
308424488b Hash the network address 2019-10-02 15:22:44 +01:00
Asim Aslam
5d77ce9e9b Rename rcache file to cache 2019-10-02 12:35:20 +01:00
Asim Aslam
9eb6262168 Merge pull request #807 from unistack-org/fixup
some spelling fixes in memory and gossip registry
2019-10-02 10:47:59 +01:00
b722798caa some spelling fixes in memory and gossip registry
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-10-02 03:54:04 +03:00
Asim Aslam
0cf7b70423 Merge pull request #806 from milosgajdos83/go-mod-shrink
Update go.mod
2019-10-01 20:13:26 +01:00
Milos Gajdos
03b8ceab5c Update go.mod 2019-10-01 19:33:23 +01:00
Asim Aslam
e8a53610f1 Update go mod to use 1.13 2019-10-01 19:09:29 +01:00
Asim Aslam
e48155118f Update go mod 2019-10-01 18:55:03 +01:00
Asim Aslam
6477c3afff Bump travis 2019-10-01 18:33:26 +01:00
Asim Aslam
57647772c8 Merge pull request #790 from milosgajdos83/memreg-ttl
[WIP] Memory registry TTL expiry
2019-09-30 15:35:57 +01:00
Milos Gajdos
4b73ac9dc5 Simplified code. Small bug fix the used to lead to multi-registry loop. 2019-09-30 12:48:59 +01:00
Milos Gajdos
3f3f1272b3 Turn regular logs to Debug logs; annotate with Registry "tag" 2019-09-30 12:48:59 +01:00
Milos Gajdos
859ecb1872 Dont set default TTL. Stop tracking nodes with no TTL 2019-09-30 12:48:59 +01:00
Milos Gajdos
204c7d1fcf Fixed options bug and proto indenting 2019-09-30 12:48:59 +01:00
Milos Gajdos
8417361bce Set registry TTL to seconds, not the nanoseconds 2019-09-30 12:48:59 +01:00
Milos Gajdos
d85ca7abd2 Set registry TTL properly via protobuf Options 2019-09-30 12:48:59 +01:00
Milos Gajdos
e973bfaa25 Add TTL options to memory registry. 2019-09-30 12:48:59 +01:00
Milos Gajdos
27bd9581bf Refresh TTL; prune expired nodes. 2019-09-30 12:48:59 +01:00
Milos Gajdos
16c7b3a390 Added Registry TTL to memory registry. Tracking node lifetimes. 2019-09-30 12:48:59 +01:00
Asim Aslam
219d759f1d Merge pull request #802 from micro/services
Add Network.Services handler
2019-09-30 07:55:03 +01:00
Asim Aslam
b90871c241 Add Network.Services handler 2019-09-30 07:51:13 +01:00
Asim Aslam
1322fb0d9d Merge pull request #800 from kordenlu/master
fix rcache node overwrited issue
2019-09-30 07:17:46 +01:00
lubaoquan
0eb69e4f9a Undo go.mod go.sum change,fixes https://github.com/micro/go-micro/issues/793 2019-09-30 10:57:35 +08:00
lubaoquan
1ed73d0f91 fixes https://github.com/micro/go-micro/issues/793 2019-09-30 10:38:53 +08:00
lubaoquan
866631df1d fixes https://github.com/micro/go-micro/issues/793 2019-09-30 10:36:43 +08:00
lubaoquan
d5e962c4a8 fixes https://github.com/micro/go-micro/issues/793 2019-09-29 10:27:38 +08:00
Asim Aslam
9ec27392de Merge pull request #799 from milosgajdos83/go1.13-fix-tests
Fix tests to make go-micro work on Go 1.13
2019-09-27 18:14:54 +01:00
Milos Gajdos
de1d9122ea Remove 1.13 support because go-quic reasons nobody understands 2019-09-27 17:19:38 +01:00
Milos Gajdos
87a5e85062 Add 1.13 support. Fix tests to enable 1.13 support 2019-09-27 17:14:24 +01:00
Asim Aslam
da572041ca Merge pull request #797 from RichardLindhout/patch-1
Do not log error when EOS is being written on an EOF socket
2019-09-27 15:18:35 +01:00
Richard Lindhout
a725998c0a Update rpc_server.go 2019-09-27 16:01:16 +02:00
Richard Lindhout
f3b723ca44 Do nog log error when EOS is being written on an EOF socket 2019-09-27 15:02:21 +02:00
Asim Aslam
e1bb4d7379 Merge pull request #795 from milosgajdos83/advert-events
Rather than append to list of events just keep the last event
2019-09-26 18:13:28 +01:00
Milos Gajdos
2d7975a7ce Rather than append to list of events just keep the last event for a route hash 2019-09-26 17:54:55 +01:00
Asim Aslam
8b77d62ed4 Merge pull request #794 from micro/hash
Don't shutdown old nodes in mdns registry
2019-09-26 16:50:17 +01:00
Asim Aslam
ef7bb46884 Don't shutdown old nodes in mdns registry 2019-09-26 16:46:09 +01:00
Asim Aslam
06975f64b7 Merge pull request #792 from milosgajdos83/router-fixes
Simplified table code. Fixed event dedup
2019-09-26 12:53:40 +01:00
Milos Gajdos
a4c04d8f50 Only emit Update event if a route was updated/added 2019-09-26 12:45:10 +01:00
Milos Gajdos
b2577e6022 Update log statement 2019-09-26 12:19:00 +01:00
Milos Gajdos
77f3e7ef48 Simplified table code. Fixed event dedup. 2019-09-26 11:56:30 +01:00
Asim Aslam
6f2a8298ef Fix router log messages 2019-09-25 20:29:25 +01:00
Asim Aslam
9e33637213 Do not log send/recv body 2019-09-25 20:24:56 +01:00
Asim Aslam
99dbed0b67 Merge pull request #789 from micro/cache
Preserve cache in the face of failure
2019-09-25 19:49:21 +01:00
Asim Aslam
2b8210a106 Preserve cache in the face of failure 2019-09-25 19:44:46 +01:00
Asim Aslam
dfcedbab1e In case of non 200 response return error 2019-09-25 16:53:06 +01:00
Asim Aslam
140e3d576c Merge pull request #788 from micro/stream
Revert to creating new connections for stream
2019-09-25 15:26:09 +01:00
Asim Aslam
afa1f50435 Revert to creating new connections for stream 2019-09-25 15:21:21 +01:00
Asim Aslam
cb22136a35 Merge pull request #787 from micro/solicit
only solicit the first time seeing a peer
2019-09-25 14:35:09 +01:00
Asim Aslam
ae40553bad only solicit the first time seeing a peer 2019-09-25 14:30:35 +01:00
Asim Aslam
855cd5ecf4 Merge pull request #786 from micro/net
Do not embed proxy/router/tunnel
2019-09-25 13:00:36 +01:00
Asim Aslam
f23c6d91ba Do not embed proxy/router/tunnel 2019-09-25 12:56:52 +01:00
Asim Aslam
c3b430af53 Merge pull request #785 from micro/link
Keep track of errors and delete beyond error count > 3
2019-09-25 12:36:23 +01:00
Asim Aslam
3d2bf7d4f6 Add log message 2019-09-25 12:36:07 +01:00
Asim Aslam
6c2b9d7636 Keep track of errors and delete beyond error count > 3 2019-09-25 12:14:09 +01:00
Asim Aslam
be5799b09f Merge pull request #783 from micro/service
Add flag for registry service client
2019-09-25 11:14:17 +01:00
Asim Aslam
7fe64192a7 Add flag for registry service client 2019-09-25 11:09:19 +01:00
Asim Aslam
624d37cf13 Merge pull request #781 from milosgajdos83/hash-reg-service
Emit memory registry event only when it actually happens
2019-09-25 07:44:55 +01:00
Milos Gajdos
1f23c8a85a Emit memory registry event only when it actually happens 2019-09-25 01:58:28 +01:00
Asim Aslam
96e79c4498 Add runtime output 2019-09-24 19:00:11 +01:00
Asim Aslam
1b08036a0b add create options 2019-09-24 18:32:35 +01:00
Asim Aslam
c52651c4d0 Move runtime.Start to be non blocking 2019-09-24 18:05:51 +01:00
Asim Aslam
9f880a1215 Merge pull request #780 from milosgajdos83/registry-event
[WIP] Registry event
2019-09-24 14:48:38 +01:00
Milos Gajdos
39755721d0 Fix proto formatting 2019-09-24 14:39:51 +01:00
Milos Gajdos
ccda1d3559 Remove rpc Sync method from registry handler 2019-09-24 10:41:48 +01:00
Milos Gajdos
61ee436cc4 Added Sync RPC call; it's identical to ListServices for now 2019-09-23 21:08:31 +01:00
Milos Gajdos
04a5d884da Move global vars to the top of the src file: conventions 2019-09-23 20:48:25 +01:00
Milos Gajdos
0ec1b840fd Add registry event to registry package 2019-09-23 20:48:25 +01:00
Asim Aslam
71ab35e055 Merge pull request #778 from micro/register-interval
Set register ttl and interval by default
2019-09-23 18:04:52 +01:00
Asim Aslam
fa0d020556 Set register ttl and interval by default 2019-09-23 17:59:34 +01:00
Asim Aslam
d8dc713e2d Fix the http resolver to use micro.mu url 2019-09-23 17:50:53 +01:00
Asim Aslam
d38c8b23f2 Merge pull request #777 from micro/net-nodes
Set rpc methods as Network.Graph/Nodes/Routes
2019-09-23 15:47:49 +01:00
Asim Aslam
4260913b45 Set rpc methods as Network.Graph/Nodes/Routes 2019-09-23 15:41:05 +01:00
Asim Aslam
ac5eb5da47 Remove fmt 2019-09-22 15:31:07 +01:00
Asim Aslam
2434c7b2a7 replace version format 2019-09-22 15:21:22 +01:00
Asim Aslam
9fbc88a60f Provide a way to get status now 2019-09-20 17:55:02 +01:00
Asim Aslam
444cc59250 Ensure transport matches for monitoring service 2019-09-20 17:20:15 +01:00
Asim Aslam
95e4ed8ee9 Merge pull request #775 from micro/proxy-watcher
Fix the proxy watcher
2019-09-20 16:36:00 +01:00
Asim Aslam
4eb1aaae85 Fix the proxy watcher 2019-09-20 16:25:29 +01:00
Asim Aslam
46450ba507 Merge pull request #773 from micro/net-hash
Only hash address if its a local route
2019-09-20 10:50:35 +01:00
Asim Aslam
f13887f604 Only hash address if its a local route 2019-09-20 09:40:55 +01:00
Asim Aslam
66769e671f Merge pull request #772 from micro/peers
Replace Nodes with Peers
2019-09-19 16:53:17 +01:00
Asim Aslam
7e05d2c440 Replace Nodes with Peers 2019-09-19 16:32:15 +01:00
Asim Aslam
0abeb3f660 Merge pull request #771 from milosgajdos83/peers-race
Remove data race; Unlock once done pruning/deleting
2019-09-19 11:59:03 +01:00
Milos Gajdos
a38482ffcb Remove data race; Unlock once done pruning/deleting 2019-09-19 11:53:13 +01:00
Asim Aslam
ee74e26582 Merge pull request #769 from micro/advertise
allow setting advertise address
2019-09-18 19:06:24 +01:00
Asim Aslam
6222bc2a1e only set tunnel address if advertise is blank 2019-09-18 19:04:22 +01:00
Asim Aslam
05e62a2b95 allow setting advertise address 2019-09-18 18:56:02 +01:00
Asim Aslam
cdbab3df66 Merge pull request #766 from milosgajdos83/hash-service
Hash the service before advertising it to the network.
2019-09-17 18:39:17 +01:00
Milos Gajdos
38d6ffdf9a Hash the service address before advertising it to the network. 2019-09-17 18:34:06 +01:00
Asim Aslam
e586763301 Merge pull request #761 from milosgajdos83/delete-peer-gw
Delete dead peer [gateway] routes
2019-09-17 16:54:35 +01:00
Milos Gajdos
3201b4cb36 Gateway is now set to node Id, hence we prune peer.id Gateway 2019-09-17 16:31:33 +01:00
Asim Aslam
837cb4fc11 Merge pull request #763 from milosgajdos83/gateway-addressing
Fix gateway addressing
2019-09-17 16:27:35 +01:00
Milos Gajdos
21dc7bcccf Fix gateway addressing
- Set Gateway as node.ID when advertising
- Set server.Address as node.ID to listen on
- Set server.Advertise as node.Address
2019-09-17 16:11:02 +01:00
Asim Aslam
a811b4be3d Merge pull request #762 from micro/net-address
Set node address to tunnel address
2019-09-17 15:44:13 +01:00
Asim Aslam
9147d378bc Set node address to tunnel address 2019-09-17 15:40:00 +01:00
Asim Aslam
b7b968ad74 remove the funding thing 2019-09-17 12:43:20 +01:00
Asim Aslam
8e8a4c1a9d Update FUNDING.yml 2019-09-17 12:42:50 +01:00
Asim Aslam
bc29164f77 Update FUNDING.yml 2019-09-17 12:39:41 +01:00
Asim Aslam
e161b2fa84 Create FUNDING.yml 2019-09-17 12:36:36 +01:00
Milos Gajdos
a72a2f717d Prune stale nodes in the whole topology. 2019-09-16 19:22:55 +01:00
Milos Gajdos
2599ee8591 Prune routes routable via dead node. 2019-09-15 12:17:12 +01:00
Asim Aslam
364c5a4861 Immediately start services 2019-09-14 08:07:36 -07:00
Asim Aslam
c8a675249d Merge pull request #759 from micro/runtime
update runtime to function
2019-09-13 22:01:52 -07:00
Asim Aslam
0cdfc7b9ea add create/delete/start/stop to runtime 2019-09-13 21:58:03 -07:00
Asim Aslam
0fc4c180ee update runtime to function 2019-09-13 21:33:14 -07:00
Asim Aslam
e5f6480f8a Merge pull request #757 from milosgajdos83/empty-advert
Skip processing Advert which carries no events
2019-09-13 17:29:36 -07:00
Milos Gajdos
ccb6778f7f Skip processing Advert which carries no events 2019-09-13 20:46:14 +01:00
Asim Aslam
ef86c9625b Merge pull request #750 from milosgajdos83/node-peers
[WIP] Neighbour is now Peer. Peer is a node in network topology.
2019-09-13 12:00:16 -07:00
Asim Aslam
b23ee58865 Update default.go 2019-09-13 11:55:53 -07:00
Milos Gajdos
323a72be34 Small refactoring; Split horizon loop break. 2019-09-13 18:46:24 +01:00
Milos Gajdos
d72e91fb38 Unlock on return from network.Connect 2019-09-13 03:31:58 +01:00
Milos Gajdos
b91c3147e7 Node API allows us to drop all network locks
Network locks are now needed only when accessing client map. node map
access is serialied with the node mutex.
2019-09-13 03:03:56 +01:00
Milos Gajdos
ef91d836eb Implement Solicit method for handler.Router 2019-09-13 03:03:56 +01:00
Milos Gajdos
77c6c9781b getProtoTopology has been replaced by PeersToProto
This helps us remove redundant code across node and handler
2019-09-13 03:03:56 +01:00
Milos Gajdos
fa4ff8921e Removed redundant lock. Simplified proto topology 2019-09-13 03:03:56 +01:00
Milos Gajdos
d6be91e8af Changed RPC methods. Changed Network interface.
* Nodes/Topology removed from public methods from Network interface
* Peers() returns max depth 3 topology
* handler.Topology rpc endpoint removed
* handler.Peers rpc endpoint accept "depth" param to return max depth peers
2019-09-13 03:03:56 +01:00
Milos Gajdos
588484c3bf Fixed some races. Added more tests. 2019-09-13 03:03:56 +01:00
Milos Gajdos
d58eb51976 Code change to make Solicit router.proto message 2019-09-13 03:03:55 +01:00
Milos Gajdos
35cf2a5739 Make topology test more generic 2019-09-13 03:03:55 +01:00
Milos Gajdos
2dfbe93d65 Added more node tests. Small refactoring of Netowkr and handler. 2019-09-13 03:03:55 +01:00
Milos Gajdos
16fcf1fbda Nodes, Peers and Topology methods for node
Topology accepts an argument to define the depth of the topology
requested from the network. proto definitions have been modified
accordingly, too.
2019-09-13 03:03:55 +01:00
Milos Gajdos
cbce5490d7 Lock the Nodes method properly when collecting them. 2019-09-13 03:03:55 +01:00
Milos Gajdos
4c709f7ac1 Write Lock() advert update: we are writing into peers map here 2019-09-13 03:03:55 +01:00
Milos Gajdos
baf4c05663 Send solicit message to ControlChannel 2019-09-13 03:03:55 +01:00
Milos Gajdos
195c6a8c90 Neighbour is now peer. Neighbourhood is Peers. Small refactor. 2019-09-13 03:03:54 +01:00
Milos Gajdos
f91d0408ab Moved node implementation into dedicated source file 2019-09-13 03:03:54 +01:00
Milos Gajdos
eec780aaa7 Update neighbours when neighbour message is received 2019-09-13 03:03:54 +01:00
Milos Gajdos
f0a1031e97 Adding new peers up to given depth. Outline of node gaph Update 2019-09-13 03:03:54 +01:00
Asim Aslam
a6668ae057 Move delete link log message 2019-09-12 17:40:47 -07:00
Asim Aslam
af5421c2cf Merge pull request #756 from micro/tunnel
Missing fixes for the tunnel
2019-09-12 17:17:33 -07:00
Asim Aslam
2406ef9999 Missing fixes for the tunnel 2019-09-12 17:12:49 -07:00
Asim Aslam
af585d3a57 Merge pull request #755 from micro/tunnel
Add tunnel fixes for quic and keepalive
2019-09-12 16:35:53 -07:00
Asim Aslam
97cf478f71 Add tunnel fixes for quic and keepalive 2019-09-12 16:22:43 -07:00
Asim Aslam
ec6a30be37 Links above Dial/Listen in interface 2019-09-11 12:49:27 -07:00
Asim Aslam
634c55e2d7 Merge pull request #753 from micro/link
Option to set Link
2019-09-11 12:16:07 -07:00
Asim Aslam
cb0de43dba add link status 2019-09-11 12:12:11 -07:00
Asim Aslam
63d535aea9 Add link field to session 2019-09-11 12:07:43 -07:00
Asim Aslam
6819386e05 Remove dead link code 2019-09-11 12:00:55 -07:00
Asim Aslam
988603f87e Merge pull request #752 from printfcoder/master
recover gPRC handler if panic
2019-09-11 09:10:30 -07:00
Asim Aslam
9ca7d90f11 link crufT 2019-09-11 07:11:40 -07:00
Asim Aslam
6ec32805d0 Don't allow socket close while writing h2 headers 2019-09-10 18:26:12 -07:00
Shu Xian
c1c173dfe5 recover handler if panic 2019-09-11 00:40:40 +08:00
Shu Xian
ce18de2647 Merge branch 'master' of github.com:micro/go-micro 2019-09-11 00:39:19 +08:00
Asim Aslam
3e3bbe3fd0 Merge pull request #751 from micro/link
Move link to tunnel/
2019-09-10 08:16:35 -07:00
Asim Aslam
b5eea02f7a Move link to tunnel/ 2019-09-10 08:12:28 -07:00
Asim Aslam
08c6f60b0f Merge pull request #746 from micro/plugin
Support plugin loading
2019-09-10 05:38:10 -07:00
Asim Aslam
065c7d5616 fix plugin init 2019-09-10 05:32:49 -07:00
Asim Aslam
a5ce3e32da Support plugin loading on service.Init 2019-09-09 20:17:36 -07:00
Asim Aslam
3bfbcd5e6a Add default plugin loader 2019-09-09 19:43:13 -07:00
Asim Aslam
b6c6b13277 Support plugin loading 2019-09-09 19:09:28 -07:00
Asim Aslam
04b31d374c Merge pull request #745 from micro/registry-service
Add service registry
2019-09-09 13:05:46 -07:00
Asim Aslam
e828a099c5 Merge pull request #744 from micro/mdns-domain
Use .micro domain for mdns
2019-09-09 11:59:37 -07:00
Asim Aslam
2c16c7e62f Fix build breaks 2019-09-09 09:25:47 -07:00
Asim Aslam
1f44d7a4a1 Add registry handler 2019-09-09 09:20:17 -07:00
Asim Aslam
b076ef906a Add service registry 2019-09-09 08:57:57 -07:00
Asim Aslam
c669a2b155 Use .micro domain for mdns 2019-09-09 05:11:25 -07:00
Asim Aslam
48a3e51aca Merge pull request #742 from micro/unlock
unlock before sending the message to avoid deadlock
2019-09-06 17:06:39 +01:00
Asim Aslam
e8aaca27d3 unlock before sending the message to avoid deadlock 2019-09-06 16:57:17 +01:00
Asim Aslam
5596407144 Merge pull request #741 from milosgajdos83/list-nodes
List nodes now works properly. Send solicit message on ControlChannel
2019-09-06 15:18:25 +01:00
Milos Gajdos
7971b1b7f9 Remove debug logs 2019-09-06 15:12:23 +01:00
Milos Gajdos
dafbacbdcb Properly handle the list of the nodes. Send solicit on ControlChannel 2019-09-06 15:09:49 +01:00
Asim Aslam
df5657dcd1 Merge pull request #737 from milosgajdos83/buffered-advertchan
Lets make advert channel buffered so we don't lose adverts
2019-09-05 19:19:03 +01:00
Milos Gajdos
bb595c85b2 Lets make advert channel buffered so we don't lose adverts 2019-09-05 19:05:47 +01:00
Asim Aslam
bc6187ea89 Merge pull request #734 from micro/tunnel
Update tunnel to send discovery on connect and multicast messages. An…
2019-09-05 18:19:37 +01:00
Asim Aslam
ed1faa7a5c Add a discover ticker, announce on connect and refactor 2019-09-05 18:13:02 +01:00
Asim Aslam
1d9298ae2b Merge pull request #736 from milosgajdos83/solicit-routes
Solicit routes when new node is discovered
2019-09-05 18:08:49 +01:00
Milos Gajdos
dddfb6f878 Fixed typos and simplified map iteration 2019-09-05 17:59:14 +01:00
Milos Gajdos
ec354934e3 Move Errors to separate init block 2019-09-05 17:44:47 +01:00
Milos Gajdos
b01c8e06e0 Update error name to ErrClientNotFound 2019-09-05 17:43:59 +01:00
Asim Aslam
97b1071f7e Merge pull request #735 from huangzhhui/patch-1
Fixed the link of Chinese documentation
2019-09-05 17:43:03 +01:00
Asim Aslam
1527a84297 Shorten multicast discovery 2019-09-05 17:40:41 +01:00
Milos Gajdos
5ddfd911ba Replace send message code by one network method 2019-09-05 17:18:16 +01:00
黄朝晖
2310ee424c Update README.zh-cn.md 2019-09-05 23:52:54 +08:00
Milos Gajdos
2522d8cb96 Send solicit message when new neighbour is discovered 2019-09-05 16:04:44 +01:00
Asim Aslam
d198765c6c Put back close of listener 2019-09-05 15:23:19 +01:00
Asim Aslam
1840b5bd74 Update tunnel to send discovery on connect and multicast messages. Announce as broadcast 2019-09-05 15:16:11 +01:00
Milos Gajdos
9161b20d6b Add Solicit method to router interface
When calling Solicit, router lists all the routes and advertise them
straight away
2019-09-05 13:23:33 +01:00
Asim Aslam
a1ba1482c5 Only set link if not multicast 2019-09-05 07:41:19 +01:00
Asim Aslam
d0761e0a1b Merge pull request #733 from milosgajdos83/freeze-graph
Freeze network graph when building full network topology
2019-09-05 07:21:53 +01:00
Milos Gajdos
4b1a7abb42 Freeze network graph when building full network topology
Also added some comments and debug logs
2019-09-05 00:16:22 +01:00
Asim Aslam
e33bd17894 Merge pull request #732 from micro/massive-cruft
Fix massive cruft in tunnel dial to set the link on discovered
2019-09-04 20:27:07 +01:00
Asim Aslam
cc5d811a83 add comment to tunnel link selection 2019-09-04 20:19:53 +01:00
Asim Aslam
e15389febb Fix massive cruft in tunnel dial to set the link on discovered 2019-09-04 20:18:26 +01:00
Asim Aslam
6d63c3777f Merge pull request #731 from micro/tunnel
Add some fixes
2019-09-04 18:53:48 +01:00
Asim Aslam
d8a1b47954 Remove lock from link 2019-09-04 18:48:43 +01:00
Asim Aslam
b9a2f719a0 Add some fixes 2019-09-04 18:46:20 +01:00
Asim Aslam
46a9767648 Merge pull request #730 from milosgajdos83/advert-lastseen
Update node.lastSeen properly. Set node.lastSeen when processing advert
2019-09-04 18:13:43 +01:00
Milos Gajdos
dd9f42e3b9 Update lastSeen timestamp properly. Set lastSeen when processing advert 2019-09-04 18:02:13 +01:00
Asim Aslam
f2c8492c77 Merge pull request #729 from micro/tunnel
Tunnel session management and unicast/multicast
2019-09-04 16:25:38 +01:00
Asim Aslam
407381912b Don't try discover on multicast, don't block existing sessions on listen 2019-09-04 15:55:37 +01:00
Asim Aslam
d559ce9da2 Provide Links() method in Tunnel 2019-09-04 15:41:57 +01:00
Asim Aslam
7ab3934eb7 add message comment 2019-09-04 12:18:37 +01:00
Asim Aslam
0075477df0 make tunnel broker use multicast 2019-09-04 12:18:31 +01:00
Asim Aslam
d5be2136ad cleanup new message creation 2019-09-04 12:16:31 +01:00
Asim Aslam
c718b8bf93 Move vars and comment 2019-09-04 12:00:11 +01:00
Asim Aslam
a24818ee54 Fix typo 2019-09-04 11:58:25 +01:00
Asim Aslam
66db0ac52c Move announce into session 2019-09-04 11:58:03 +01:00
Asim Aslam
b9c437fbfe Tunnel discover/announce/open/session/close 2019-09-04 09:48:05 +01:00
Asim Aslam
147899283c Merge pull request #728 from wuyumin/master
Update config source README file
2019-09-04 09:16:04 +01:00
Yumin Wu
5b991cd2c2 Update config source README file 2019-09-04 15:49:58 +08:00
Yumin Wu
bb64f94313 .gitignore file for develop tools 2019-09-04 15:47:46 +08:00
Milos Gajdos
4f4b3d3bae Send connect message to NetworkChannel once we are not at caller mercy 2019-09-03 19:51:52 +01:00
Asim Aslam
eb4a709195 Merge branch 'master' of ssh://github.com/micro/go-micro into tunnel 2019-09-03 17:20:39 +01:00
Asim Aslam
6c21b31226 Merge pull request #727 from milosgajdos83/bug-overhaul
Major bug overhaul in how we handle network.Nodes and related handler
2019-09-03 17:20:15 +01:00
Milos Gajdos
6eb6d050ed Major bug overhaul in how we handle network.Nodes and related handler 2019-09-03 16:39:27 +01:00
Asim Aslam
6c7582a6be Move message to session 2019-09-03 15:56:37 +01:00
Milos Gajdos
3ea4490d6c Don't preallocate the slice if you don't index later on. 2019-09-03 15:02:30 +01:00
Asim Aslam
b50c44a758 Merge pull request #726 from milosgajdos83/prune-nodes
Prune nodes that have not announced themselves for certain time period.
2019-09-03 10:28:31 +01:00
Milos Gajdos
ec6318befc Prune nodes that have not announced themselves for certain time period. 2019-09-03 10:00:14 +01:00
Asim Aslam
5440325a18 Merge pull request #724 from milosgajdos83/efficient-bfs-queue
Make Nodes() BFS implementation efficient
2019-09-03 07:43:37 +01:00
Milos Gajdos
fb13877904 Make Nodes() BFS implementation efficient 2019-09-03 02:58:17 +01:00
Asim Aslam
2f5e3c66b9 Merge pull request #723 from milosgajdos83/sort-nodes-search
Sort the returned slice of nodes before searching
2019-09-02 20:13:20 +01:00
Milos Gajdos
a8d4299df9 Sort the returned slice of nodes before searching
See docs:
https://golang.org/pkg/sort/#Search
2019-09-02 20:00:52 +01:00
Asim Aslam
90745c14f2 Merge pull request #722 from milosgajdos83/net-handler
[WIP] Network handler
2019-09-02 17:15:38 +01:00
Milos Gajdos
86665454e7 Implementation of Nodes method. First take on full handler 2019-09-02 17:06:21 +01:00
Milos Gajdos
4f5a849211 Added Nodes method to Network interface 2019-09-02 12:40:05 +01:00
Milos Gajdos
bf53c16e4b Rough outline of Network introspection interface 2019-09-02 12:40:05 +01:00
Asim Aslam
6c3631728b Merge pull request #721 from micro/tunnel
Separate lookup nodes and setup nodes
2019-09-02 12:10:24 +01:00
Asim Aslam
2cdfed359f Separate lookup nodes and setup nodes 2019-09-02 12:05:47 +01:00
Asim Aslam
956be5c59d Merge pull request #717 from micro/client-stream
use with stream for client connection
2019-09-02 07:36:41 +01:00
Asim Aslam
52d9d75dfa use with stream for client connection 2019-08-31 18:26:48 +01:00
Asim Aslam
0d94784e72 Add some tunnel comments 2019-08-31 17:32:20 +01:00
Asim Aslam
65c2de5a79 Merge pull request #716 from micro/tunnel
Rename Tunnel ID to Channel
2019-08-31 16:32:41 +01:00
Asim Aslam
6fa9d7270f Rename Tunnel ID to Channel 2019-08-30 20:05:00 +01:00
Asim Aslam
140c830af1 Merge pull request #715 from milosgajdos83/net-debug
Add proto definitions for network introspection.
2019-08-30 12:38:24 +01:00
Milos Gajdos
b37837ad92 Add proto definitions for network introspection. 2019-08-30 12:29:26 +01:00
Asim Aslam
10b64af0b3 Merge pull request #713 from milosgajdos83/route-loop-break
Avoid setting routes that route back to the node without its being direct GW to dest
2019-08-30 11:33:17 +01:00
Asim Aslam
5d01284574 Merge pull request #714 from wuyumin/master
Load consul source
2019-08-30 10:15:37 +01:00
Yumin Wu
ff81e4b246 Load consul source 2019-08-30 16:20:58 +08:00
Milos Gajdos
e955e3f798 Avoid routes that route back to node without its being direct GW to dest 2019-08-30 00:04:46 +01:00
Asim Aslam
a17a8b3372 Merge branch 'master' of ssh://github.com/micro/go-micro 2019-08-29 17:21:49 +01:00
Asim Aslam
e1d56fbf58 switch warn to error logging 2019-08-29 17:21:43 +01:00
Milos Gajdos
e7d8cdda44 Avoid duplicate debug logs. 2019-08-29 16:58:07 +01:00
Asim Aslam
690640eeeb Merge pull request #712 from milosgajdos83/route-update
Only emit table event if table.Update actually happens
2019-08-29 16:30:27 +01:00
Milos Gajdos
4f788c6fc7 Only emit the events when actually deleting the route 2019-08-29 16:25:21 +01:00
Milos Gajdos
f50bd400f8 Only emit event if Update actually happens 2019-08-29 16:21:30 +01:00
Asim Aslam
b457ec1990 Merge pull request #711 from milosgajdos83/node-neighbours
Don't override the neighbours.
2019-08-29 15:48:13 +01:00
Milos Gajdos
ffa6b551f4 Don't override the neighbours. 2019-08-29 15:42:07 +01:00
Asim Aslam
3d03fe4076 Fix panic for nil slice 2019-08-29 15:09:01 +01:00
Asim Aslam
6eecb199e9 Merge pull request #710 from micro/nodes
add the ability to provide seed nodes to the network
2019-08-29 15:00:51 +01:00
Asim Aslam
7479515099 add the ability to provide seed nodes to the network 2019-08-29 14:53:30 +01:00
Asim Aslam
6e3d53e1ee Merge pull request #709 from micro/tunnel-arp
Tunnel Direction Fix
2019-08-29 13:13:25 +01:00
Asim Aslam
721c5e6857 fix broken build 2019-08-29 13:11:20 +01:00
Asim Aslam
7d033818cf if the service name is blank, barf 2019-08-29 13:10:06 +01:00
Asim Aslam
00ab58f61b Fix loopback cruft 2019-08-29 12:42:27 +01:00
Asim Aslam
b3aef71fdb Merge pull request #708 from milosgajdos83/route-metric
Set the route.Metric before updating routing table
2019-08-29 12:28:43 +01:00
Milos Gajdos
8606f1e143 Set the route.Metric before updating routing table 2019-08-29 11:45:47 +01:00
Asim Aslam
927fac2cec Merge pull request #706 from milosgajdos83/neighbour-map
Broadcast neighbourhood
2019-08-28 23:16:42 +01:00
Asim Aslam
6ab86c9e57 Don't process unless connected, and only fire loopback messages back up the loopback 2019-08-28 23:12:22 +01:00
Milos Gajdos
db8e2620cb Make tunnel channel clients key-able. Neighbour map simplified.
tunClient is a map of tunnel clients keyed on tunnel channel name.
Neighbour map is now a cimple map of nodes which contains its nodes.
2019-08-28 23:11:26 +01:00
Milos Gajdos
d09b7dbbef Broadcast neighbourhood; fix critical bugs in channel connections
This commit introduces neighbourhood announcements which allows to
maintaing neighbour map if each next-hop node.

It also fixes a critical bug when accepting connections for a particular
tunnel channel.
2019-08-28 20:11:19 +01:00
Asim Aslam
a4f5772555 add network field to the routes 2019-08-28 08:41:19 +01:00
Asim Aslam
731f6f74dd Merge pull request #703 from milosgajdos83/net-id
Adds network id. Skips processing routes when router is the origin.
2019-08-28 08:05:19 +01:00
Milos Gajdos
5e7208119e Adds network id. Skips processing routes when router is the origin. 2019-08-27 23:08:35 +01:00
Asim Aslam
470304ef87 Merge pull request #701 from h-hy/master
Check last for the address binded in lo interface (LVS , DR mode)
2019-08-27 18:08:52 +01:00
huanghaoyan
a6ab4d7b4b check last for the address bind in lo interface. 2019-08-27 23:35:27 +08:00
Asim Aslam
87b56d46ac Use tunnel transport and set server address 2019-08-27 13:21:36 +01:00
Milos Gajdos
371b23d055 Introduce DefaultLink; dont hardcode name of the link 2019-08-27 11:36:46 +01:00
Asim Aslam
f97565ef0a Merge pull request #685 from milosgajdos83/default-network
Default network implementation
2019-08-27 11:02:55 +01:00
Asim Aslam
0888d2fbbc Add grpc content-type 2019-08-27 08:13:58 +01:00
Shu xian
75e20b5bf7 Merge pull request #1 from micro/master
merge
2019-08-27 09:38:18 +08:00
Asim Aslam
443fc0ebde Merge pull request #700 from micro/h2-grpc
H2 grpc
2019-08-26 15:55:31 +01:00
huanghaoyan
35e7b9551f ignore Loopback Address (LVS,DR mode) 2019-08-26 21:48:40 +08:00
Asim Aslam
6daf4fda72 Full support for grpc server side 2019-08-26 12:33:59 +01:00
Asim Aslam
36623bfe50 Improve stream processing 2019-08-25 19:30:22 +01:00
Asim Aslam
6128d18ee0 checkpoint fixing data race to process h2 and grpc requests 2019-08-24 20:12:04 +01:00
Asim Aslam
abadb2211e Merge pull request #698 from micro/tunnel-broker
Add a tunnel broker
2019-08-24 14:37:20 +01:00
Asim Aslam
ca267f73de add a tunnel broker 2019-08-24 09:46:55 +01:00
Asim Aslam
d8608b2343 Merge pull request #697 from micro/static-resolver
Add a static network node resolver
2019-08-23 22:05:02 +01:00
Milos Gajdos
ed8d28c9ab Set Route.Link to "network" not Route.Network. Oops! 2019-08-23 21:08:18 +01:00
Milos Gajdos
88e47b9b06 Dont bail when unable to resolve network nodes. 2019-08-23 17:48:14 +01:00
Asim Aslam
1b0295de0d Add a static network node resolver 2019-08-23 17:24:21 +01:00
Milos Gajdos
9448d7c164 Set Route.Network to "network" and Router.Gateway to network.Address 2019-08-23 16:01:57 +01:00
Milos Gajdos
8c3eec9f2a Set the default resolver to registry 2019-08-23 15:14:16 +01:00
Milos Gajdos
e53484302c Added ControlChannel tunnel.Listener to process incoming messages 2019-08-23 15:14:16 +01:00
Milos Gajdos
db89fc4efe Set server name to the correct value. 2019-08-23 15:14:16 +01:00
Milos Gajdos
e1599b0f17 Set server name. Set default network name. 2019-08-23 15:14:16 +01:00
Milos Gajdos
a09d5d2e9a Add Address method. Start and Stop router/server. 2019-08-23 15:14:16 +01:00
Milos Gajdos
6c1f1d66f7 Switch received messages on the right header 2019-08-23 15:14:16 +01:00
Milos Gajdos
a6e1287b27 Replaced incorrect proto import path 2019-08-23 15:14:15 +01:00
Milos Gajdos
fcec6e8eae First attempt to implement default network interface 2019-08-23 15:14:15 +01:00
Milos Gajdos
30dd3f54f0 Make router.Table docs consistent 2019-08-23 15:14:15 +01:00
Milos Gajdos
6beae23afd First commit. Outline of the default network. 2019-08-23 15:14:15 +01:00
Asim Aslam
718780367e Merge pull request #696 from milosgajdos83/server-idempotent
Make server Start() and Stop() idempotent
2019-08-23 15:12:33 +01:00
Milos Gajdos
ba99f037fb Lock started flag when changing it. 2019-08-23 15:07:08 +01:00
Milos Gajdos
80dc0b97a9 Make server starts and stops idempotent 2019-08-23 15:00:57 +01:00
Asim Aslam
1a32e3a11d Merge pull request #695 from micro/proxy-link
Support multiple clients in the proxy as Links
2019-08-23 14:48:49 +01:00
Asim Aslam
955dc2a23d change where we order the routes 2019-08-23 14:11:53 +01:00
Asim Aslam
934b8eb86d Error as link not found 2019-08-23 14:09:57 +01:00
Asim Aslam
b7f510ff64 support links in the proxy 2019-08-23 14:05:11 +01:00
Asim Aslam
353eade6c3 Update client proto 2019-08-23 12:06:11 +01:00
Asim Aslam
a133e61c2d Merge pull request #694 from milosgajdos83/tunnel-loopback-sleep
Lock when setting loopback flag and receiving keepalives
2019-08-22 17:35:03 +01:00
Milos Gajdos
99d39e743b Lock when setting loopback flag and receiving keepalives 2019-08-22 16:31:37 +01:00
Asim Aslam
0cdac2aa36 Merge pull request #689 from milosgajdos83/router-stop
Make router.Stop idempotent
2019-08-21 21:25:48 +01:00
Milos Gajdos
75871287a1 Make stop idempotent. Small refactoring. Router name is memory. 2019-08-21 21:10:42 +01:00
Asim Aslam
fb750a0bb1 Don't start the router if its already running 2019-08-21 18:58:56 +01:00
Asim Aslam
c6e15ef2d1 rename server, set version to timestamp 2019-08-21 15:43:46 +01:00
Asim Aslam
f787cc0ee0 Merge pull request #687 from micro/tunnel
Add tunnel address
2019-08-21 13:01:28 +01:00
Asim Aslam
c2d85a6e1f Add tunnel address 2019-08-21 12:55:10 +01:00
Milos Gajdos
86f0c06fac Removed filewatch counter test. 2019-08-21 11:26:41 +01:00
Asim Aslam
0aea8e3163 Merge pull request #686 from milosgajdos83/config-watcher
Introduce ErrStoppedWatcher for source.Source Watchers and fixed test
2019-08-21 11:06:14 +01:00
Milos Gajdos
4ea27517b5 Source watcher ErrStoppedWatcher and fixed test 2019-08-20 22:32:47 +01:00
Asim Aslam
f8e68ae101 Add string method to tunnel 2019-08-20 17:21:35 +01:00
Asim Aslam
f848041c49 Add a message type to the tunnel 2019-08-20 17:20:21 +01:00
Asim Aslam
dfbd730b8c Fix mucp service option passing 2019-08-20 16:48:09 +01:00
Asim Aslam
ac2a5a04a2 Merge pull request #681 from unistack-org/fix_wg
fix panic: negative WaitGroup counter
2019-08-19 12:06:45 +01:00
f1d08f251f fix panic: negative WaitGroup counter
avoid double wait group Done()

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-08-19 13:58:57 +03:00
Asim Aslam
718ae42808 Merge pull request #680 from printfcoder/master
fix file watcher event bug on Linux
2019-08-19 08:34:21 +01:00
Shu Xian
2413cbcd80 fix file watcher event bug on Linux
the watcher can not normally get events of file changes on linux. it just can get the first two changes.
2019-08-19 15:28:24 +08:00
Asim Aslam
9c820445a4 Merge pull request #679 from micro/grpc-codec
Force grpc client/server to use grpc codec for broker
2019-08-18 11:37:38 +01:00
Asim Aslam
c44fd63301 Force grpc client/server to use grpc codec for broker 2019-08-18 11:28:21 +01:00
Asim Aslam
d9a699ae6f Merge pull request #673 from micro/multiplex
Stream Multiplexing
2019-08-16 17:41:45 +01:00
Asim Aslam
4495ca3839 Use client.Call for non streaming requests 2019-08-16 17:24:17 +01:00
Asim Aslam
0b0eee41d0 functioning proxy code 2019-08-16 16:46:29 +01:00
Asim Aslam
e18f8defde Merge pull request #672 from milosgajdos83/tunnel-ping
Monitor outbound links and delete them when disconnected
2019-08-16 16:09:14 +01:00
Milos Gajdos
7abdc68049 Fixed the race. Made wait channel boolean. 2019-08-16 15:40:35 +01:00
Milos Gajdos
c90e1ccb99 Fixed reconnect code; refactor tunnel tests. 2019-08-16 15:18:34 +01:00
Asim Aslam
991142cd57 No need to set request in the buffer 2019-08-16 14:42:45 +01:00
Asim Aslam
5a5b1b8f6e only continue to stream when its a stream 2019-08-15 20:54:28 +01:00
Asim Aslam
58bc4c103f go fmt 2019-08-15 20:54:09 +01:00
Asim Aslam
88817dc53f Strip some dead code 2019-08-15 20:54:00 +01:00
Asim Aslam
ef04331b86 multiplexing cruft 2019-08-15 20:08:49 +01:00
Milos Gajdos
67215ae5da Changed nodeLink to setupLink 2019-08-15 19:24:24 +01:00
Milos Gajdos
f120452d28 Monitor outbound links periodically and reconnect the failed links. 2019-08-15 18:18:58 +01:00
Milos Gajdos
740cfab8d0 Monitor outbound links and delete them when disconnected 2019-08-15 16:52:16 +01:00
Asim Aslam
f6b8045dd5 send client error if it exists 2019-08-15 15:22:53 +01:00
Asim Aslam
b776fbb766 add a pseudo socket implementation 2019-08-15 15:09:56 +01:00
Asim Aslam
a42de29f67 Do same for host port on deregister 2019-08-15 08:59:50 +01:00
Asim Aslam
0f6d09af33 go fmt 2019-08-15 08:47:32 +01:00
Asim Aslam
2dd5109eee Merge pull request #669 from printfcoder/master
fix registry addr error for mq-rpc
2019-08-15 07:36:06 +01:00
Shu xian
e609095ba4 Merge pull request #2 from micro/master
merge
2019-08-15 08:38:08 +08:00
Asim Aslam
4843f09afa Merge pull request #670 from milosgajdos83/loopback-msg-fix
Fixing the tunnel loopback messaging
2019-08-14 17:28:15 +01:00
Milos Gajdos
f9eddf1e6f Fixing the tunnel loopback messaging 2019-08-14 17:14:39 +01:00
Shu xian
f19308f1e6 Merge pull request #1 from micro/master
merge
2019-08-14 22:02:41 +08:00
Asim Aslam
8f0c2e0412 add a better tunnel test 2019-08-14 14:38:17 +01:00
Shu Xian
bf0e46dc0d fix registry addr error for mq-rpc 2019-08-14 21:32:28 +08:00
Asim Aslam
15975d2903 Merge pull request #668 from milosgajdos83/tun-token-loopback
[WIP] Tunnel loopback connections
2019-08-14 14:32:18 +01:00
Milos Gajdos
9f2f0e3cea Moved Close method to the bottom 2019-08-14 13:26:23 +01:00
Milos Gajdos
151bcf0ea1 Send and receive on loopback tunnel interface 2019-08-14 13:00:10 +01:00
Asim Aslam
dc0fbfc3c0 Merge pull request #666 from unistack-org/log
export log levels and reverse log level order
2019-08-14 07:41:01 +01:00
Milos Gajdos
e607485c6b Check for token in every received message. 2019-08-14 01:23:03 +01:00
70d0029658 add warn log level
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-08-14 02:26:51 +03:00
a606813fdf export log levels and reverse log level order
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-08-14 02:18:22 +03:00
Milos Gajdos
750267b308 first commit to draft up a way for Sending messages to loopback 2019-08-13 20:11:23 +01:00
Asim Aslam
7ce0305db4 only operate on clients that are the same as the server 2019-08-13 16:08:56 +01:00
Asim Aslam
c39591af0e add a mux package for the proxy 2019-08-13 15:21:51 +01:00
Asim Aslam
fedc6be3e6 Merge pull request #663 from milosgajdos83/router-start
Add Start method to router
2019-08-13 08:12:56 +01:00
Milos Gajdos
cb1679fd8d Add Start method to router
Added Start to router packages.
Fixed potential deadlocks.
2019-08-12 22:23:46 +01:00
Asim Aslam
c0a676bfa9 Only check the router status if the lookup fails 2019-08-12 17:06:08 +01:00
Asim Aslam
cb4e376c64 Update go mod 2019-08-12 12:35:09 +01:00
Asim Aslam
c2c8182a5b delete tunnel headers and add some TODOs 2019-08-11 21:53:40 +01:00
Asim Aslam
01cb146e0d send message once after creating socket 2019-08-11 18:24:16 +01:00
Asim Aslam
d0d729a789 fix the tunnel execution 2019-08-11 18:11:33 +01:00
Asim Aslam
113d87d855 Merge branch 'master' of ssh://github.com/micro/go-micro 2019-08-11 16:15:30 +01:00
Asim Aslam
56df10f68b use keepalive in quic by default 2019-08-11 16:12:31 +01:00
Asim Aslam
3a5428fb36 Merge pull request #660 from xpunch/serviceNotFoundIssue
Service not found issue
2019-08-11 12:44:27 +01:00
johnson
178a3b3d8e Merge remote-tracking branch 'origin/master' into serviceNotFoundIssue
# Conflicts:
#	client/grpc/grpc.go
2019-08-11 19:34:00 +08:00
johnson
de34f259ba update service not found error tooltip
fixing test failed issue

change back error type
change registry.ErrNotFound back to selector.ErrNotFound

change back error type
change registry.ErrNotFound back to selector.ErrNotFound

remove the single node tunnel test

Fix read yaml config from memory

package main

import (
	"fmt"

	"github.com/micro/go-micro/config"
	"github.com/micro/go-micro/config/source/memory"
)

var configData = []byte(`
---
a: 1234
`)

func main() {
	memorySource := memory.NewSource(
		memory.WithYAML(configData),
	)
	// Create new config
	conf := config.NewConfig()

	// Load file source
	conf.Load(memorySource)

	fmt.Println(string(conf.Bytes()))
}
2019-08-11 19:31:22 +08:00
potato
81b68a1d7f Merge pull request #4 from micro/master
update fork
2019-08-11 19:05:49 +08:00
Asim Aslam
1a600810a7 Merge pull request #661 from XiaoLer/patch-2
Fix read yaml config from memory
2019-08-11 12:03:18 +01:00
potato
94127ae1aa Merge pull request #3 from micro/master
update fork
2019-08-11 18:24:57 +08:00
刘小乐
cd2ac648ff Fix read yaml config from memory
package main

import (
	"fmt"

	"github.com/micro/go-micro/config"
	"github.com/micro/go-micro/config/source/memory"
)

var configData = []byte(`
---
a: 1234
`)

func main() {
	memorySource := memory.NewSource(
		memory.WithYAML(configData),
	)
	// Create new config
	conf := config.NewConfig()

	// Load file source
	conf.Load(memorySource)

	fmt.Println(string(conf.Bytes()))
}
2019-08-11 18:05:35 +08:00
Asim Aslam
e613b0c205 remove the single node tunnel test 2019-08-11 09:54:02 +01:00
potato
57dacf1831 Merge pull request #2 from micro/master
update fork
2019-08-11 10:22:33 +08:00
Asim Aslam
8986b3135f Strip logging 2019-08-10 18:46:54 +01:00
Asim Aslam
6dd3ea1853 Remove listen check 2019-08-10 18:44:50 +01:00
Asim Aslam
2c66e94045 fix some tunnel bugs like races and duplicate messages... 2019-08-10 16:37:49 +01:00
Asim Aslam
c1ff3ceee4 Add more verbose not found error 2019-08-09 12:31:29 +01:00
potato
b13604fb4b Merge pull request #1 from micro/master
Update forks to latest
2019-08-09 18:46:28 +08:00
Asim Aslam
057adb2b2e Merge pull request #658 from XiaoLer/patch-1
no more `WithData` method, instead of  `WithJSON`
2019-08-09 11:37:35 +01:00
刘小乐
7bd6d1b549 no more WithData method, instead of WithJSON 2019-08-09 12:45:59 +08:00
Asim Aslam
37988b596d Merge pull request #656 from milosgajdos83/tun-listener
Close the tunnel listener when the tunnel is cloed.
2019-08-08 15:25:19 +01:00
Milos Gajdos
9eb45dac82 Close the tunnel listener when the tunnel is cloed. 2019-08-08 15:20:53 +01:00
Asim Aslam
59b13aef22 tunnel skip zero length nodes 2019-08-08 13:15:30 +01:00
Asim Aslam
1e496938b7 more tunnel logging 2019-08-08 13:07:13 +01:00
Asim Aslam
fbc1d523d7 add ability to set log level via env var 2019-08-08 13:07:04 +01:00
Asim Aslam
11795071fb Fix panic 2019-08-08 12:45:37 +01:00
Asim Aslam
c7e8a2aeb9 Merge pull request #651 from magodo/master
wait nats drain since it's asynchronous
2019-08-08 00:30:01 +01:00
Asim Aslam
cb1c1afc84 add quic to defaults 2019-08-08 00:19:30 +01:00
Asim Aslam
3fc7d9ea50 Quic requires an initial message to start the session so we need connect 2019-08-08 00:19:16 +01:00
Asim Aslam
abc2ace409 Merge pull request #653 from micro/tunnel
Add back the old tunnel interface
2019-08-07 22:36:15 +01:00
Asim Aslam
243d43df92 Strip master from travis 2019-08-07 22:32:16 +01:00
Asim Aslam
9c2b882008 Bump travis go 2019-08-07 22:27:03 +01:00
Asim Aslam
4370f03e04 update go modules 2019-08-07 22:11:52 +01:00
Asim Aslam
a3b962f37b Fix travis test? 2019-08-07 22:02:58 +01:00
Asim Aslam
a894b4f354 add tunnel/transport package 2019-08-07 21:58:25 +01:00
Asim Aslam
fc379f2d2c Remove other accept 2019-08-07 19:03:45 +01:00
Asim Aslam
dcf4fed6a3 Add a second test for two tunnels 2019-08-07 18:56:21 +01:00
Asim Aslam
117376a922 Add back the old tunnel interface 2019-08-07 18:44:33 +01:00
Asim Aslam
380d9790e6 add io.ReadWriteCloser ontop of transport.Socket 2019-08-07 15:02:00 +01:00
magodo
0baea58938 wait nats drain since it's asynchronous
1. nats subscription draining is removed, since it is asynchronous,
   and there is no reliable way to detect when it is finished.
   Remove this option to avoid confusion.
2. nats connection draining is kept, and use 2 callbacks to detect
   draining timeout (timeout is set via `nats.Options`) or finish.
3. Also honour options passed in `broker.Init()` (previously only
   `broker.New()` is honoured).
2019-08-07 17:58:45 +08:00
Asim Aslam
edb0fe4b16 fix the consul setup code 2019-08-06 19:43:46 +01:00
Asim Aslam
eae32176c4 Monitor all services in the monitor 2019-08-06 19:02:57 +01:00
Asim Aslam
bc751c55fb Merge pull request #650 from micro/monitor
Add monitor/debug/service packages
2019-08-06 18:09:57 +01:00
Asim Aslam
91f2af91de Fix bugs in monitor 2019-08-06 18:05:05 +01:00
Asim Aslam
3adce58eb2 Add monitor/debug packages 2019-08-06 17:53:14 +01:00
Asim Aslam
bb01b3ed78 Don't extract repeated value 2019-08-06 14:52:15 +01:00
Asim Aslam
c3ea25225c Don't check value name on extraction 2019-08-06 14:49:42 +01:00
Asim Aslam
beffa625f8 fix broker log line 2019-08-06 12:25:51 +01:00
Asim Aslam
0d85e35da0 Merge pull request #649 from milosgajdos83/tun-types
Rough outline of tunnel types
2019-08-06 11:54:54 +01:00
Milos Gajdos
4074cce397 Rough outline of tunnel types 2019-08-06 11:46:47 +01:00
Asim Aslam
000431f489 Nats addr fix https://github.com/micro/go-micro/pull/648 2019-08-06 09:15:54 +01:00
Asim Aslam
e16420fdbd Consul config fix https://github.com/micro/go-micro/pull/641 2019-08-06 09:15:38 +01:00
Milos Gajdos
52d8d26018 Transport() will return tunnel (pseudo) Transport 2019-08-05 21:09:46 +01:00
Asim Aslam
6649012af3 Merge pull request #645 from milosgajdos83/transport-out
Tunnel no longer embeds transport
2019-08-05 19:57:22 +01:00
Milos Gajdos
6b5dcbf814 Tunnel no longer embeds transport 2019-08-05 19:41:48 +01:00
Asim Aslam
34381213e7 Package comment 2019-08-05 18:04:47 +01:00
Asim Aslam
767292906a Merge pull request #644 from milosgajdos83/tunnel
Adds outline of go-micro Tunnel interface
2019-08-05 17:54:40 +01:00
Milos Gajdos
e1ecd728c5 Adds outline of go-micro Tunnel interface 2019-08-05 17:52:57 +01:00
Asim Aslam
f1b6709722 Fix breaking api changes 2019-08-05 17:47:50 +01:00
Asim Aslam
4030ccc27b Move proxy/router 2019-08-05 17:44:33 +01:00
Asim Aslam
2e67e23a23 Update .travis.yml 2019-08-03 15:16:30 +01:00
Asim Aslam
be229438bc Merge pull request #640 from unistack-org/deadline
transport memory: add Send/Recv Timeout
2019-08-03 13:51:01 +01:00
e1709026e4 transport memory: add Send/Recv Timeout
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-08-03 15:39:44 +03:00
Asim Aslam
d250ac736f In the event watchRegistry or watchTable exit then close the go routines 2019-08-02 15:17:48 +01:00
Asim Aslam
6719f8d655 Remove the table watcher when stopped 2019-08-02 14:59:08 +01:00
Asim Aslam
d7929ef8f3 Stop the ticker when exiting 2019-08-02 14:44:11 +01:00
Asim Aslam
04404441a4 Merge pull request #634 from micro/rcache-stop
Stop a goroutine leak in registy
2019-08-01 23:17:39 +01:00
Asim Aslam
b806e7bdf5 Stop a goroutine leak in registy 2019-08-01 23:03:11 +01:00
Milos Gajdos
2720c6f28e Removed trailing white characters 2019-08-01 13:32:55 +01:00
Asim Aslam
cdf0f14d58 remove this code 2019-07-31 17:19:49 +01:00
Asim Aslam
679c5f0ccd Fix some connection bugs 2019-07-31 16:49:48 +01:00
Asim Aslam
873bfcc73c Process/Stop router 2019-07-31 16:46:55 +01:00
Asim Aslam
7884e889f4 Don't publish the process rpc call and embed the router handler in the network 2019-07-31 16:36:53 +01:00
Asim Aslam
b1c49a0ddc Add router handler 2019-07-31 16:10:04 +01:00
Asim Aslam
318367cd71 move NewNetwork 2019-07-31 15:37:12 +01:00
Asim Aslam
2d09e74b0e add address/advertise 2019-07-31 15:35:51 +01:00
Asim Aslam
3e90d32f29 Add proxy/router 2019-07-31 15:30:51 +01:00
Asim Aslam
fca89e06ef Some network inspiration 2019-07-31 15:22:57 +01:00
Asim Aslam
89fc142e47 Update to use mdns 0.2.0 2019-07-31 13:16:57 +01:00
Asim Aslam
0b2c8ee523 Add top level data package comment 2019-07-30 16:47:18 +01:00
218 changed files with 18417 additions and 5124 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,7 @@
# Develop tools
/.vscode/
/.idea/
# Binaries for programs and plugins
*.exe
*.exe~

View File

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

View File

@@ -32,5 +32,5 @@ Go Micro把分布式系统的各种细节抽象出来。下面是它的主要特
## 快速上手
更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/go-micro_cn.html)。
更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/cn/)。

24
api/server/acme/acme.go Normal file
View File

@@ -0,0 +1,24 @@
// Package acme abstracts away various ACME libraries
package acme
import (
"errors"
"net"
)
var (
// ErrProviderNotImplemented can be returned when attempting to
// instantiate an unimplemented provider
ErrProviderNotImplemented = errors.New("Provider not implemented")
)
// Provider is a ACME provider interface
type Provider interface {
NewListener(...string) (net.Listener, error)
}
// The Let's Encrypt ACME endpoints
const (
LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory"
LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory"
)

View File

@@ -0,0 +1,23 @@
// Package autocert is the ACME provider from golang.org/x/crypto/acme/autocert
// This provider does not take any config.
package autocert
import (
"net"
"github.com/micro/go-micro/api/server/acme"
"golang.org/x/crypto/acme/autocert"
)
// autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert
type autocertProvider struct{}
// NewListener implements acme.Provider
func (a *autocertProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
return autocert.NewListener(ACMEHosts...), nil
}
// New returns an autocert acme.Provider
func New() acme.Provider {
return &autocertProvider{}
}

View File

@@ -0,0 +1,16 @@
package autocert
import (
"testing"
)
func TestAutocert(t *testing.T) {
l := New()
if _, ok := l.(*autocertProvider); !ok {
t.Error("New() didn't return an autocertProvider")
}
// TODO: Travis CI doesn't let us bind :443
// if _, err := l.NewListener(); err != nil {
// t.Error(err.Error())
// }
}

View File

@@ -0,0 +1,62 @@
// Package certmagic is the ACME provider from github.com/mholt/certmagic
package certmagic
import (
"log"
"math/rand"
"net"
"time"
"github.com/mholt/certmagic"
"github.com/micro/go-micro/api/server/acme"
)
type certmagicProvider struct {
opts *acme.Options
}
func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
certmagic.Default.CA = c.opts.CA
if c.opts.ChallengeProvider != nil {
// Enabling DNS Challenge disables the other challenges
certmagic.Default.DNSProvider = c.opts.ChallengeProvider
}
if c.opts.OnDemand {
certmagic.Default.OnDemand = new(certmagic.OnDemandConfig)
}
if c.opts.Cache != nil {
// already validated by new()
certmagic.Default.Storage = c.opts.Cache.(certmagic.Storage)
}
// If multiple instances of the provider are running, inject some
// randomness so they don't collide
rand.Seed(time.Now().UnixNano())
randomDuration := (7 * 24 * time.Hour) + (time.Duration(rand.Intn(504)) * time.Hour)
certmagic.Default.RenewDurationBefore = randomDuration
return certmagic.Listen(ACMEHosts)
}
// New returns a certmagic provider
func New(options ...acme.Option) acme.Provider {
o := &acme.Options{}
if len(options) == 0 {
for _, op := range acme.Default() {
op(o)
}
} else {
for _, op := range options {
op(o)
}
}
if o.Cache != nil {
if _, ok := o.Cache.(certmagic.Storage); !ok {
log.Fatal("ACME: cache provided doesn't implement certmagic's Storage interface")
}
}
return &certmagicProvider{
opts: o,
}
}

View File

@@ -0,0 +1,231 @@
package certmagic
import (
"net/http"
"os"
"reflect"
"sort"
"testing"
"time"
"github.com/go-acme/lego/v3/providers/dns/cloudflare"
"github.com/mholt/certmagic"
"github.com/micro/go-micro/api/server/acme"
"github.com/micro/go-micro/config/options"
cloudflarestorage "github.com/micro/go-micro/store/cloudflare"
"github.com/micro/go-micro/sync/lock/memory"
)
func TestCertMagic(t *testing.T) {
if len(os.Getenv("IN_TRAVIS_CI")) != 0 {
t.Skip("Travis doesn't let us bind :443")
}
l, err := New().NewListener()
if err != nil {
t.Error(err.Error())
}
l.Close()
c := cloudflare.NewDefaultConfig()
c.AuthEmail = ""
c.AuthKey = ""
c.AuthToken = "test"
c.ZoneToken = "test"
p, err := cloudflare.NewDNSProviderConfig(c)
if err != nil {
t.Error(err.Error())
}
l, err = New(acme.AcceptToS(true),
acme.CA(acme.LetsEncryptStagingCA),
acme.ChallengeProvider(p),
).NewListener()
if err != nil {
t.Error(err.Error())
}
l.Close()
}
func TestStorageImplementation(t *testing.T) {
apiToken, accountID := os.Getenv("CF_API_TOKEN"), os.Getenv("CF_ACCOUNT_ID")
kvID := os.Getenv("KV_NAMESPACE_ID")
if len(apiToken) == 0 || len(accountID) == 0 || len(kvID) == 0 {
t.Skip("No Cloudflare API keys available, skipping test")
}
var s certmagic.Storage
st, err := cloudflarestorage.New(
options.WithValue("CF_API_TOKEN", apiToken),
options.WithValue("CF_ACCOUNT_ID", accountID),
options.WithValue("KV_NAMESPACE_ID", kvID),
)
if err != nil {
t.Fatalf("Couldn't initialise cloudflare storage: %s\n", err.Error())
}
s = &storage{
lock: memory.NewLock(),
store: st,
}
// Test Lock
if err := s.Lock("test"); err != nil {
t.Error(err)
}
// Test Unlock
if err := s.Unlock("test"); err != nil {
t.Error(err)
}
// Test data
testdata := []struct {
key string
value []byte
}{
{key: "/foo/a", value: []byte("lorem")},
{key: "/foo/b", value: []byte("ipsum")},
{key: "/foo/c", value: []byte("dolor")},
{key: "/foo/d", value: []byte("sit")},
{key: "/bar/a", value: []byte("amet")},
{key: "/bar/b", value: []byte("consectetur")},
{key: "/bar/c", value: []byte("adipiscing")},
{key: "/bar/d", value: []byte("elit")},
{key: "/foo/bar/a", value: []byte("sed")},
{key: "/foo/bar/b", value: []byte("do")},
{key: "/foo/bar/c", value: []byte("eiusmod")},
{key: "/foo/bar/d", value: []byte("tempor")},
{key: "/foo/bar/baz/a", value: []byte("incididunt")},
{key: "/foo/bar/baz/b", value: []byte("ut")},
{key: "/foo/bar/baz/c", value: []byte("labore")},
{key: "/foo/bar/baz/d", value: []byte("et")},
// a duplicate just in case there's any edge cases
{key: "/foo/a", value: []byte("lorem")},
}
// Test Store
for _, d := range testdata {
if err := s.Store(d.key, d.value); err != nil {
t.Error(err.Error())
}
}
// Test Load
for _, d := range testdata {
if value, err := s.Load(d.key); err != nil {
t.Error(err.Error())
} else {
if !reflect.DeepEqual(value, d.value) {
t.Errorf("Load %s: expected %v, got %v", d.key, d.value, value)
}
}
}
// Test Exists
for _, d := range testdata {
if !s.Exists(d.key) {
t.Errorf("%s should exist, but doesn't\n", d.key)
}
}
// Test List
if list, err := s.List("/", true); err != nil {
t.Error(err.Error())
} else {
var expected []string
for i, d := range testdata {
if i != len(testdata)-1 {
// Don't store the intentionally duplicated key
expected = append(expected, d.key)
}
}
sort.Strings(expected)
sort.Strings(list)
if !reflect.DeepEqual(expected, list) {
t.Errorf("List: Expected %v, got %v\n", expected, list)
}
}
if list, err := s.List("/foo", false); err != nil {
t.Error(err.Error())
} else {
sort.Strings(list)
expected := []string{"/foo/a", "/foo/b", "/foo/bar", "/foo/c", "/foo/d"}
if !reflect.DeepEqual(expected, list) {
t.Errorf("List: expected %s, got %s\n", expected, list)
}
}
// Test Stat
for _, d := range testdata {
info, err := s.Stat(d.key)
if err != nil {
t.Error(err.Error())
} else {
if info.Key != d.key {
t.Errorf("Stat().Key: expected %s, got %s\n", d.key, info.Key)
}
if info.Size != int64(len(d.value)) {
t.Errorf("Stat().Size: expected %d, got %d\n", len(d.value), info.Size)
}
if time.Since(info.Modified) > time.Minute {
t.Errorf("Stat().Modified: expected time since last modified to be < 1 minute, got %v\n", time.Since(info.Modified))
}
}
}
// Test Delete
for _, d := range testdata {
if err := s.Delete(d.key); err != nil {
t.Error(err.Error())
}
}
// New interface doesn't return an error, so call it in case any log.Fatal
// happens
New(acme.Cache(s))
}
// Full test with a real zone, with against LE staging
func TestE2e(t *testing.T) {
apiToken, accountID := os.Getenv("CF_API_TOKEN"), os.Getenv("CF_ACCOUNT_ID")
kvID := os.Getenv("KV_NAMESPACE_ID")
if len(apiToken) == 0 || len(accountID) == 0 || len(kvID) == 0 {
t.Skip("No Cloudflare API keys available, skipping test")
}
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),
)
if err != nil {
t.Fatal(err.Error())
}
testStorage := NewStorage(testLock, testStore)
conf := cloudflare.NewDefaultConfig()
conf.AuthToken = apiToken
conf.ZoneToken = apiToken
testChallengeProvider, err := cloudflare.NewDNSProviderConfig(conf)
if err != nil {
t.Fatal(err.Error())
}
testProvider := New(
acme.AcceptToS(true),
acme.Cache(testStorage),
acme.CA(acme.LetsEncryptStagingCA),
acme.ChallengeProvider(testChallengeProvider),
acme.OnDemand(false),
)
listener, err := testProvider.NewListener("*.micro.mu", "micro.mu")
if err != nil {
t.Fatal(err.Error())
}
go http.Serve(listener, http.NotFoundHandler())
time.Sleep(10 * time.Minute)
}

View File

@@ -0,0 +1,146 @@
package certmagic
import (
"bytes"
"encoding/gob"
"errors"
"fmt"
"path"
"strings"
"time"
"github.com/mholt/certmagic"
"github.com/micro/go-micro/store"
"github.com/micro/go-micro/sync/lock"
)
// File represents a "File" that will be stored in store.Store - the contents and last modified time
type File struct {
// last modified time
LastModified time.Time
// Contents
Contents []byte
}
// storage is an implementation of certmagic.Storage using micro's sync.Map and store.Store interfaces.
// As certmagic storage expects a filesystem (with stat() abilities) we have to implement
// the bare minimum of metadata.
type storage struct {
lock lock.Lock
store store.Store
}
func (s *storage) Lock(key string) error {
return s.lock.Acquire(key, lock.TTL(10*time.Minute))
}
func (s *storage) Unlock(key string) error {
return s.lock.Release(key)
}
func (s *storage) Store(key string, value []byte) error {
f := File{
LastModified: time.Now(),
Contents: value,
}
buf := &bytes.Buffer{}
e := gob.NewEncoder(buf)
if err := e.Encode(f); err != nil {
return err
}
r := &store.Record{
Key: key,
Value: buf.Bytes(),
}
return s.store.Write(r)
}
func (s *storage) Load(key string) ([]byte, error) {
if !s.Exists(key) {
return nil, certmagic.ErrNotExist(errors.New(key + " doesn't exist"))
}
records, err := s.store.Read(key)
if err != nil {
return nil, err
}
if len(records) != 1 {
return nil, fmt.Errorf("ACME Storage: multiple records matched key %s", key)
}
b := bytes.NewBuffer(records[0].Value)
d := gob.NewDecoder(b)
var f File
err = d.Decode(&f)
if err != nil {
return nil, err
}
return f.Contents, nil
}
func (s *storage) Delete(key string) error {
return s.store.Delete(key)
}
func (s *storage) Exists(key string) bool {
_, err := s.store.Read(key)
if err != nil {
return false
}
return true
}
func (s *storage) List(prefix string, recursive bool) ([]string, error) {
records, err := s.store.Sync()
if err != nil {
return nil, err
}
var results []string
for _, r := range records {
if strings.HasPrefix(r.Key, prefix) {
results = append(results, r.Key)
}
}
if recursive {
return results, nil
}
keysMap := make(map[string]bool)
for _, key := range results {
dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
keysMap[dir[0]] = true
}
results = make([]string, 0)
for k := range keysMap {
results = append(results, path.Join(prefix, k))
}
return results, nil
}
func (s *storage) Stat(key string) (certmagic.KeyInfo, error) {
records, err := s.store.Read(key)
if err != nil {
return certmagic.KeyInfo{}, err
}
if len(records) != 1 {
return certmagic.KeyInfo{}, fmt.Errorf("ACME Storage: multiple records matched key %s", key)
}
b := bytes.NewBuffer(records[0].Value)
d := gob.NewDecoder(b)
var f File
err = d.Decode(&f)
if err != nil {
return certmagic.KeyInfo{}, err
}
return certmagic.KeyInfo{
Key: key,
Modified: f.LastModified,
Size: int64(len(f.Contents)),
IsTerminal: false,
}, nil
}
// NewStorage returns a certmagic.Storage backed by a go-micro/lock and go-micro/store
func NewStorage(lock lock.Lock, store store.Store) certmagic.Storage {
return &storage{
lock: lock,
store: store,
}
}

View File

@@ -0,0 +1,73 @@
package acme
import "github.com/go-acme/lego/v3/challenge"
// Option (or Options) are passed to New() to configure providers
type Option func(o *Options)
// Options represents various options you can present to ACME providers
type Options struct {
// AcceptTLS must be set to true to indicate that you have read your
// provider's terms of service.
AcceptToS bool
// CA is the CA to use
CA string
// ChallengeProvider is a go-acme/lego challenge provider. Set this if you
// want to use DNS Challenges. Otherwise, tls-alpn-01 will be used
ChallengeProvider challenge.Provider
// Issue certificates for domains on demand. Otherwise, certs will be
// retrieved / issued on start-up.
OnDemand bool
// Cache is a storage interface. Most ACME libraries have an cache, but
// there's no defined interface, so if you consume this option
// sanity check it before using.
Cache interface{}
}
// AcceptToS indicates whether you accept your CA's terms of service
func AcceptToS(b bool) Option {
return func(o *Options) {
o.AcceptToS = b
}
}
// CA sets the CA of an acme.Options
func CA(CA string) Option {
return func(o *Options) {
o.CA = CA
}
}
// ChallengeProvider sets the Challenge provider of an acme.Options
// if set, it enables the DNS challenge, otherwise tls-alpn-01 will be used.
func ChallengeProvider(p challenge.Provider) Option {
return func(o *Options) {
o.ChallengeProvider = p
}
}
// OnDemand enables on-demand certificate issuance. Not recommended for use
// with the DNS challenge, as the first connection may be very slow.
func OnDemand(b bool) Option {
return func(o *Options) {
o.OnDemand = b
}
}
// Cache provides a cache / storage interface to the underlying ACME library
// as there is no standard, this needs to be validated by the underlying
// implentation.
func Cache(c interface{}) Option {
return func(o *Options) {
o.Cache = c
}
}
// Default uses the Let's Encrypt Production CA, with DNS Challenge disabled.
func Default() []Option {
return []Option{
AcceptToS(true),
CA(LetsEncryptProductionCA),
OnDemand(true),
}
}

View File

@@ -11,7 +11,6 @@ import (
"github.com/gorilla/handlers"
"github.com/micro/go-micro/api/server"
"github.com/micro/go-micro/util/log"
"golang.org/x/crypto/acme/autocert"
)
type httpServer struct {
@@ -53,9 +52,9 @@ func (s *httpServer) Start() error {
var l net.Listener
var err error
if s.opts.EnableACME {
if s.opts.EnableACME && s.opts.ACMEProvider != nil {
// should we check the address to make sure its using :443?
l = autocert.NewListener(s.opts.ACMEHosts...)
l, err = s.opts.ACMEProvider.NewListener(s.opts.ACMEHosts...)
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig)
} else {

View File

@@ -2,15 +2,24 @@ package server
import (
"crypto/tls"
"github.com/micro/go-micro/api/server/acme"
)
type Option func(o *Options)
type Options struct {
EnableACME bool
EnableTLS bool
ACMEHosts []string
TLSConfig *tls.Config
EnableACME bool
ACMEProvider acme.Provider
EnableTLS bool
ACMEHosts []string
TLSConfig *tls.Config
}
func EnableACME(b bool) Option {
return func(o *Options) {
o.EnableACME = b
}
}
func ACMEHosts(hosts ...string) Option {
@@ -19,9 +28,9 @@ func ACMEHosts(hosts ...string) Option {
}
}
func EnableACME(b bool) Option {
func ACMEProvider(p acme.Provider) Option {
return func(o *Options) {
o.EnableACME = b
o.ACMEProvider = p
}
}

View File

@@ -324,15 +324,21 @@ func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
p := &httpEvent{m: m, t: topic}
id := req.Form.Get("id")
var subs []Handler
h.RLock()
for _, subscriber := range h.subscribers[topic] {
if id == subscriber.id {
// sub is sync; crufty rate limiting
// so we don't hose the cpu
subscriber.fn(p)
if id != subscriber.id {
continue
}
subs = append(subs, subscriber.fn)
}
h.RUnlock()
// execute the handler
for _, fn := range subs {
fn(p)
}
}
func (h *httpBroker) Address() string {
@@ -420,7 +426,6 @@ func (h *httpBroker) Connect() error {
}
func (h *httpBroker) Disconnect() error {
h.RLock()
if !h.running {
h.RUnlock()
@@ -522,7 +527,7 @@ func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption)
// now attempt to get the service
h.RLock()
s, err := h.r.GetService("topic:" + topic)
s, err := h.r.GetService(topic)
if err != nil {
h.RUnlock()
// ignore error
@@ -560,13 +565,29 @@ func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption)
continue
}
var nodes []*registry.Node
for _, node := range service.Nodes {
// only use nodes tagged with broker http
if node.Metadata["broker"] != "http" {
continue
}
// look for nodes for the topic
if node.Metadata["topic"] != topic {
continue
}
nodes = append(nodes, node)
}
switch service.Version {
// broadcast version means broadcast to all nodes
case broadcastVersion:
var success bool
// publish to all nodes
for _, node := range service.Nodes {
for _, node := range nodes {
// publish async
if err := pub(node, topic, b); err == nil {
success = true
@@ -579,7 +600,7 @@ func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption)
}
default:
// select node to publish to
node := service.Nodes[rand.Int()%len(service.Nodes)]
node := nodes[rand.Int()%len(nodes)]
// publish async to one node
if err := pub(node, topic, b); err != nil {
@@ -642,6 +663,8 @@ func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeO
Address: mnet.HostPort(addr, port),
Metadata: map[string]string{
"secure": fmt.Sprintf("%t", secure),
"broker": "http",
"topic": topic,
},
}
@@ -652,7 +675,7 @@ func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeO
}
service := &registry.Service{
Name: "topic:" + topic,
Name: topic,
Version: version,
Nodes: []*registry.Node{node},
}

View File

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

View File

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

View File

@@ -0,0 +1,66 @@
package handler
import (
"context"
"github.com/micro/go-micro/broker"
pb "github.com/micro/go-micro/broker/service/proto"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/util/log"
)
type Broker struct {
Broker broker.Broker
}
func (b *Broker) Publish(ctx context.Context, req *pb.PublishRequest, rsp *pb.Empty) error {
log.Debugf("Publishing message to %s topic", req.Topic)
err := b.Broker.Publish(req.Topic, &broker.Message{
Header: req.Message.Header,
Body: req.Message.Body,
})
log.Debugf("Published message to %s topic", req.Topic)
if err != nil {
return errors.InternalServerError("go.micro.broker", err.Error())
}
return nil
}
func (b *Broker) Subscribe(ctx context.Context, req *pb.SubscribeRequest, stream pb.Broker_SubscribeStream) error {
errChan := make(chan error, 1)
// message handler to stream back messages from broker
handler := func(p broker.Event) error {
if err := stream.Send(&pb.Message{
Header: p.Message().Header,
Body: p.Message().Body,
}); err != nil {
select {
case errChan <- err:
return err
default:
return err
}
}
return nil
}
log.Debugf("Subscribing to %s topic", req.Topic)
sub, err := b.Broker.Subscribe(req.Topic, handler, broker.Queue(req.Queue))
if err != nil {
return errors.InternalServerError("go.micro.broker", err.Error())
}
defer func() {
log.Debugf("Unsubscribing from topic %s", req.Topic)
sub.Unsubscribe()
}()
select {
case <-ctx.Done():
log.Debugf("Context done for subscription to topic %s", req.Topic)
return nil
case err := <-errChan:
log.Debugf("Subscription error for topic %s: %v", req.Topic, err)
return err
}
}

View File

@@ -0,0 +1,173 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/go-micro/broker/proto/broker.proto
package go_micro_broker
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
client "github.com/micro/go-micro/client"
server "github.com/micro/go-micro/server"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ client.Option
var _ server.Option
// Client API for Broker service
type BrokerService interface {
Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error)
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error)
}
type brokerService struct {
c client.Client
name string
}
func NewBrokerService(name string, c client.Client) BrokerService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.broker"
}
return &brokerService{
c: c,
name: name,
}
}
func (c *brokerService) Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error) {
req := c.c.NewRequest(c.name, "Broker.Publish", in)
out := new(Empty)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *brokerService) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error) {
req := c.c.NewRequest(c.name, "Broker.Subscribe", &SubscribeRequest{})
stream, err := c.c.Stream(ctx, req, opts...)
if err != nil {
return nil, err
}
if err := stream.Send(in); err != nil {
return nil, err
}
return &brokerServiceSubscribe{stream}, nil
}
type Broker_SubscribeService interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Recv() (*Message, error)
}
type brokerServiceSubscribe struct {
stream client.Stream
}
func (x *brokerServiceSubscribe) Close() error {
return x.stream.Close()
}
func (x *brokerServiceSubscribe) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *brokerServiceSubscribe) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *brokerServiceSubscribe) Recv() (*Message, error) {
m := new(Message)
err := x.stream.Recv(m)
if err != nil {
return nil, err
}
return m, nil
}
// Server API for Broker service
type BrokerHandler interface {
Publish(context.Context, *PublishRequest, *Empty) error
Subscribe(context.Context, *SubscribeRequest, Broker_SubscribeStream) error
}
func RegisterBrokerHandler(s server.Server, hdlr BrokerHandler, opts ...server.HandlerOption) error {
type broker interface {
Publish(ctx context.Context, in *PublishRequest, out *Empty) error
Subscribe(ctx context.Context, stream server.Stream) error
}
type Broker struct {
broker
}
h := &brokerHandler{hdlr}
return s.Handle(s.NewHandler(&Broker{h}, opts...))
}
type brokerHandler struct {
BrokerHandler
}
func (h *brokerHandler) Publish(ctx context.Context, in *PublishRequest, out *Empty) error {
return h.BrokerHandler.Publish(ctx, in, out)
}
func (h *brokerHandler) Subscribe(ctx context.Context, stream server.Stream) error {
m := new(SubscribeRequest)
if err := stream.Recv(m); err != nil {
return err
}
return h.BrokerHandler.Subscribe(ctx, m, &brokerSubscribeStream{stream})
}
type Broker_SubscribeStream interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*Message) error
}
type brokerSubscribeStream struct {
stream server.Stream
}
func (x *brokerSubscribeStream) Close() error {
return x.stream.Close()
}
func (x *brokerSubscribeStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *brokerSubscribeStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *brokerSubscribeStream) Send(m *Message) error {
return x.stream.Send(m)
}

View File

@@ -0,0 +1,364 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: github.com/micro/go-micro/broker/proto/broker.proto
package go_micro_broker
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Empty struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) {
return fileDescriptor_5edf81766900dd99, []int{0}
}
func (m *Empty) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Empty.Unmarshal(m, b)
}
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
}
func (m *Empty) XXX_Merge(src proto.Message) {
xxx_messageInfo_Empty.Merge(m, src)
}
func (m *Empty) XXX_Size() int {
return xxx_messageInfo_Empty.Size(m)
}
func (m *Empty) XXX_DiscardUnknown() {
xxx_messageInfo_Empty.DiscardUnknown(m)
}
var xxx_messageInfo_Empty proto.InternalMessageInfo
type PublishRequest struct {
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
Message *Message `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PublishRequest) Reset() { *m = PublishRequest{} }
func (m *PublishRequest) String() string { return proto.CompactTextString(m) }
func (*PublishRequest) ProtoMessage() {}
func (*PublishRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_5edf81766900dd99, []int{1}
}
func (m *PublishRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PublishRequest.Unmarshal(m, b)
}
func (m *PublishRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PublishRequest.Marshal(b, m, deterministic)
}
func (m *PublishRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PublishRequest.Merge(m, src)
}
func (m *PublishRequest) XXX_Size() int {
return xxx_messageInfo_PublishRequest.Size(m)
}
func (m *PublishRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PublishRequest.DiscardUnknown(m)
}
var xxx_messageInfo_PublishRequest proto.InternalMessageInfo
func (m *PublishRequest) GetTopic() string {
if m != nil {
return m.Topic
}
return ""
}
func (m *PublishRequest) GetMessage() *Message {
if m != nil {
return m.Message
}
return nil
}
type SubscribeRequest struct {
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
Queue string `protobuf:"bytes,2,opt,name=queue,proto3" json:"queue,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} }
func (m *SubscribeRequest) String() string { return proto.CompactTextString(m) }
func (*SubscribeRequest) ProtoMessage() {}
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_5edf81766900dd99, []int{2}
}
func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SubscribeRequest.Unmarshal(m, b)
}
func (m *SubscribeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SubscribeRequest.Marshal(b, m, deterministic)
}
func (m *SubscribeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SubscribeRequest.Merge(m, src)
}
func (m *SubscribeRequest) XXX_Size() int {
return xxx_messageInfo_SubscribeRequest.Size(m)
}
func (m *SubscribeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SubscribeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SubscribeRequest proto.InternalMessageInfo
func (m *SubscribeRequest) GetTopic() string {
if m != nil {
return m.Topic
}
return ""
}
func (m *SubscribeRequest) GetQueue() string {
if m != nil {
return m.Queue
}
return ""
}
type Message struct {
Header map[string]string `protobuf:"bytes,1,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) {
return fileDescriptor_5edf81766900dd99, []int{3}
}
func (m *Message) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Message.Unmarshal(m, b)
}
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
}
func (m *Message) XXX_Merge(src proto.Message) {
xxx_messageInfo_Message.Merge(m, src)
}
func (m *Message) XXX_Size() int {
return xxx_messageInfo_Message.Size(m)
}
func (m *Message) XXX_DiscardUnknown() {
xxx_messageInfo_Message.DiscardUnknown(m)
}
var xxx_messageInfo_Message proto.InternalMessageInfo
func (m *Message) GetHeader() map[string]string {
if m != nil {
return m.Header
}
return nil
}
func (m *Message) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
func init() {
proto.RegisterType((*Empty)(nil), "go.micro.broker.Empty")
proto.RegisterType((*PublishRequest)(nil), "go.micro.broker.PublishRequest")
proto.RegisterType((*SubscribeRequest)(nil), "go.micro.broker.SubscribeRequest")
proto.RegisterType((*Message)(nil), "go.micro.broker.Message")
proto.RegisterMapType((map[string]string)(nil), "go.micro.broker.Message.HeaderEntry")
}
func init() {
proto.RegisterFile("github.com/micro/go-micro/broker/proto/broker.proto", fileDescriptor_5edf81766900dd99)
}
var fileDescriptor_5edf81766900dd99 = []byte{
// 309 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xcf, 0x4a, 0xf3, 0x40,
0x14, 0xc5, 0x3b, 0xed, 0xd7, 0x86, 0xde, 0x7e, 0x68, 0x19, 0x8a, 0x84, 0x6e, 0x8c, 0xc1, 0x45,
0x36, 0x4e, 0x24, 0xdd, 0xa8, 0x88, 0x0b, 0xb1, 0xe0, 0x42, 0x41, 0xc6, 0x9d, 0xbb, 0x4c, 0x3a,
0x24, 0xa1, 0x8d, 0x93, 0x4e, 0x66, 0x84, 0xbc, 0x88, 0x2b, 0x1f, 0x56, 0x3a, 0x93, 0xfa, 0xa7,
0xa1, 0xee, 0xee, 0x49, 0x7e, 0x73, 0xee, 0xe1, 0x5c, 0x98, 0xa5, 0xb9, 0xca, 0x34, 0x23, 0x89,
0x28, 0xc2, 0x22, 0x4f, 0xa4, 0x08, 0x53, 0x71, 0x66, 0x07, 0x26, 0xc5, 0x92, 0xcb, 0xb0, 0x94,
0x42, 0x6d, 0x05, 0x31, 0x02, 0x1f, 0xa6, 0x82, 0x18, 0x86, 0xd8, 0xcf, 0xbe, 0x03, 0xfd, 0x79,
0x51, 0xaa, 0xda, 0x7f, 0x81, 0x83, 0x27, 0xcd, 0x56, 0x79, 0x95, 0x51, 0xbe, 0xd6, 0xbc, 0x52,
0x78, 0x02, 0x7d, 0x25, 0xca, 0x3c, 0x71, 0x91, 0x87, 0x82, 0x21, 0xb5, 0x02, 0x47, 0xe0, 0x14,
0xbc, 0xaa, 0xe2, 0x94, 0xbb, 0x5d, 0x0f, 0x05, 0xa3, 0xc8, 0x25, 0x3b, 0x9e, 0xe4, 0xd1, 0xfe,
0xa7, 0x5b, 0xd0, 0xbf, 0x81, 0xf1, 0xb3, 0x66, 0x55, 0x22, 0x73, 0xc6, 0xff, 0x76, 0x9f, 0x40,
0x7f, 0xad, 0xb9, 0xb6, 0xde, 0x43, 0x6a, 0x85, 0xff, 0x8e, 0xc0, 0x69, 0x4c, 0xf1, 0x35, 0x0c,
0x32, 0x1e, 0x2f, 0xb8, 0x74, 0x91, 0xd7, 0x0b, 0x46, 0xd1, 0xe9, 0xbe, 0xf5, 0xe4, 0xde, 0x60,
0xf3, 0x57, 0x25, 0x6b, 0xda, 0xbc, 0xc1, 0x18, 0xfe, 0x31, 0xb1, 0xa8, 0x8d, 0xfd, 0x7f, 0x6a,
0xe6, 0xe9, 0x25, 0x8c, 0x7e, 0xa0, 0x78, 0x0c, 0xbd, 0x25, 0xaf, 0x9b, 0x58, 0x9b, 0x71, 0x13,
0xea, 0x2d, 0x5e, 0x7d, 0x87, 0x32, 0xe2, 0xaa, 0x7b, 0x81, 0xa2, 0x0f, 0x04, 0x83, 0x5b, 0xb3,
0x15, 0xdf, 0x81, 0xd3, 0xf4, 0x87, 0x8f, 0x5b, 0x91, 0x7e, 0x37, 0x3b, 0x3d, 0x6a, 0x01, 0xf6,
0x06, 0x1d, 0xfc, 0x00, 0xc3, 0xaf, 0xa6, 0xf0, 0x49, 0x0b, 0xdb, 0x6d, 0x71, 0xba, 0xb7, 0x7c,
0xbf, 0x73, 0x8e, 0xd8, 0xc0, 0x1c, 0x7d, 0xf6, 0x19, 0x00, 0x00, 0xff, 0xff, 0x25, 0x38, 0xfa,
0x02, 0x2b, 0x02, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// BrokerClient is the client API for Broker service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type BrokerClient interface {
Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*Empty, error)
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Broker_SubscribeClient, error)
}
type brokerClient struct {
cc *grpc.ClientConn
}
func NewBrokerClient(cc *grpc.ClientConn) BrokerClient {
return &brokerClient{cc}
}
func (c *brokerClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, "/go.micro.broker.Broker/Publish", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *brokerClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Broker_SubscribeClient, error) {
stream, err := c.cc.NewStream(ctx, &_Broker_serviceDesc.Streams[0], "/go.micro.broker.Broker/Subscribe", opts...)
if err != nil {
return nil, err
}
x := &brokerSubscribeClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Broker_SubscribeClient interface {
Recv() (*Message, error)
grpc.ClientStream
}
type brokerSubscribeClient struct {
grpc.ClientStream
}
func (x *brokerSubscribeClient) Recv() (*Message, error) {
m := new(Message)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// BrokerServer is the server API for Broker service.
type BrokerServer interface {
Publish(context.Context, *PublishRequest) (*Empty, error)
Subscribe(*SubscribeRequest, Broker_SubscribeServer) error
}
func RegisterBrokerServer(s *grpc.Server, srv BrokerServer) {
s.RegisterService(&_Broker_serviceDesc, srv)
}
func _Broker_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PublishRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BrokerServer).Publish(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.broker.Broker/Publish",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BrokerServer).Publish(ctx, req.(*PublishRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Broker_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(SubscribeRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(BrokerServer).Subscribe(m, &brokerSubscribeServer{stream})
}
type Broker_SubscribeServer interface {
Send(*Message) error
grpc.ServerStream
}
type brokerSubscribeServer struct {
grpc.ServerStream
}
func (x *brokerSubscribeServer) Send(m *Message) error {
return x.ServerStream.SendMsg(m)
}
var _Broker_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.broker.Broker",
HandlerType: (*BrokerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Publish",
Handler: _Broker_Publish_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Subscribe",
Handler: _Broker_Subscribe_Handler,
ServerStreams: true,
},
},
Metadata: "github.com/micro/go-micro/broker/proto/broker.proto",
}

View File

@@ -0,0 +1,25 @@
syntax = "proto3";
package go.micro.broker;
service Broker {
rpc Publish(PublishRequest) returns (Empty) {};
rpc Subscribe(SubscribeRequest) returns (stream Message) {};
}
message Empty {}
message PublishRequest {
string topic = 1;
Message message = 2;
}
message SubscribeRequest {
string topic = 1;
string queue = 2;
}
message Message {
map<string,string> header = 1;
bytes body = 2;
}

132
broker/service/service.go Normal file
View File

@@ -0,0 +1,132 @@
// Package service provides the broker service client
package service
import (
"context"
"time"
"github.com/micro/go-micro/broker"
pb "github.com/micro/go-micro/broker/service/proto"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/util/log"
)
type serviceBroker struct {
Addrs []string
Client pb.BrokerService
options broker.Options
}
var (
DefaultName = "go.micro.broker"
)
func (b *serviceBroker) Address() string {
return b.Addrs[0]
}
func (b *serviceBroker) Connect() error {
return nil
}
func (b *serviceBroker) Disconnect() error {
return nil
}
func (b *serviceBroker) Init(opts ...broker.Option) error {
for _, o := range opts {
o(&b.options)
}
return nil
}
func (b *serviceBroker) Options() broker.Options {
return b.options
}
func (b *serviceBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
log.Debugf("Publishing to topic %s broker %v", topic, b.Addrs)
_, err := b.Client.Publish(context.TODO(), &pb.PublishRequest{
Topic: topic,
Message: &pb.Message{
Header: msg.Header,
Body: msg.Body,
},
}, client.WithAddress(b.Addrs...))
return err
}
func (b *serviceBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
var options broker.SubscribeOptions
for _, o := range opts {
o(&options)
}
log.Debugf("Subscribing to topic %s queue %s broker %v", topic, options.Queue, b.Addrs)
stream, err := b.Client.Subscribe(context.TODO(), &pb.SubscribeRequest{
Topic: topic,
Queue: options.Queue,
}, client.WithAddress(b.Addrs...), client.WithRequestTimeout(time.Hour))
if err != nil {
return nil, err
}
sub := &serviceSub{
topic: topic,
queue: options.Queue,
handler: handler,
stream: stream,
closed: make(chan bool),
options: options,
}
go func() {
for {
select {
case <-sub.closed:
log.Debugf("Unsubscribed from topic %s", topic)
return
default:
// run the subscriber
log.Debugf("Streaming from broker %v to topic [%s] queue [%s]", b.Addrs, topic, options.Queue)
if err := sub.run(); err != nil {
log.Debugf("Resubscribing to topic %s broker %v", topic, b.Addrs)
stream, err := b.Client.Subscribe(context.TODO(), &pb.SubscribeRequest{
Topic: topic,
Queue: options.Queue,
}, client.WithAddress(b.Addrs...), client.WithRequestTimeout(time.Hour))
if err != nil {
log.Debugf("Failed to resubscribe to topic %s: %v", topic, err)
time.Sleep(time.Second)
continue
}
// new stream
sub.stream = stream
}
}
}
}()
return sub, nil
}
func (b *serviceBroker) String() string {
return "service"
}
func NewBroker(opts ...broker.Option) broker.Broker {
var options broker.Options
for _, o := range opts {
o(&options)
}
addrs := options.Addrs
if len(addrs) == 0 {
addrs = []string{"127.0.0.1:8001"}
}
return &serviceBroker{
Addrs: addrs,
Client: pb.NewBrokerService(DefaultName, client.DefaultClient),
options: options,
}
}

View File

@@ -0,0 +1,101 @@
package service
import (
"github.com/micro/go-micro/broker"
pb "github.com/micro/go-micro/broker/service/proto"
"github.com/micro/go-micro/util/log"
)
type serviceSub struct {
topic string
queue string
handler broker.Handler
stream pb.Broker_SubscribeService
closed chan bool
options broker.SubscribeOptions
}
type serviceEvent struct {
topic string
message *broker.Message
}
func (s *serviceEvent) Topic() string {
return s.topic
}
func (s *serviceEvent) Message() *broker.Message {
return s.message
}
func (s *serviceEvent) Ack() error {
return nil
}
func (s *serviceSub) isClosed() bool {
select {
case <-s.closed:
return true
default:
return false
}
}
func (s *serviceSub) run() error {
exit := make(chan bool)
go func() {
select {
case <-exit:
case <-s.closed:
}
// close the stream
s.stream.Close()
}()
for {
// TODO: do not fail silently
msg, err := s.stream.Recv()
if err != nil {
log.Debugf("Streaming error for subcription to topic %s: %v", s.Topic(), err)
// close the exit channel
close(exit)
// don't return an error if we unsubscribed
if s.isClosed() {
return nil
}
// return stream error
return err
}
// TODO: handle error
s.handler(&serviceEvent{
topic: s.topic,
message: &broker.Message{
Header: msg.Header,
Body: msg.Body,
},
})
}
}
func (s *serviceSub) Options() broker.SubscribeOptions {
return s.options
}
func (s *serviceSub) Topic() string {
return s.topic
}
func (s *serviceSub) Unsubscribe() error {
select {
case <-s.closed:
return nil
default:
close(s.closed)
}
return nil
}

View File

@@ -30,6 +30,7 @@ var (
"application/proto": protoCodec{},
"application/protobuf": protoCodec{},
"application/octet-stream": protoCodec{},
"application/grpc": protoCodec{},
"application/grpc+json": jsonCodec{},
"application/grpc+proto": protoCodec{},
"application/grpc+bytes": bytesCodec{},

View File

@@ -18,7 +18,6 @@ import (
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/buf"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/encoding"
@@ -73,10 +72,11 @@ func (g *grpcClient) next(request client.Request, opts client.CallOptions) (sele
// get next nodes from the selector
next, err := g.opts.Selector.Select(service, opts.SelectOptions...)
if err != nil && err == selector.ErrNotFound {
return nil, errors.NotFound("go.micro.client", err.Error())
} else if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
if err != nil {
if err == selector.ErrNotFound {
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
}
return next, nil
@@ -110,12 +110,21 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
var grr error
cc, err := g.pool.getConn(address, grpc.WithDefaultCallOptions(grpc.ForceCodec(cf)),
grpc.WithTimeout(opts.DialTimeout), g.secure(),
grpcDialOptions := []grpc.DialOption{
grpc.WithDefaultCallOptions(grpc.ForceCodec(cf)),
grpc.WithTimeout(opts.DialTimeout),
g.secure(),
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(maxRecvMsgSize),
grpc.MaxCallSendMsgSize(maxSendMsgSize),
))
),
}
if opts := g.getGrpcDialOptions(); opts != nil {
grpcDialOptions = append(grpcDialOptions, opts...)
}
cc, err := g.pool.getConn(address, grpcDialOptions...)
if err != nil {
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
}
@@ -127,7 +136,11 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
ch := make(chan error, 1)
go func() {
err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpc.CallContentSubtype(cf.Name()))
grpcCallOptions := []grpc.CallOption{grpc.CallContentSubtype(cf.Name())}
if opts := g.getGrpcCallOptions(); opts != nil {
grpcCallOptions = append(grpcCallOptions, opts...)
}
err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpcCallOptions...)
ch <- microError(err)
}()
@@ -175,7 +188,16 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
wc := wrapCodec{cf}
cc, err := grpc.DialContext(dialCtx, address, grpc.WithDefaultCallOptions(grpc.ForceCodec(wc)), g.secure())
grpcDialOptions := []grpc.DialOption{
grpc.WithDefaultCallOptions(grpc.ForceCodec(wc)),
g.secure(),
}
if opts := g.getGrpcDialOptions(); opts != nil {
grpcDialOptions = append(grpcDialOptions, opts...)
}
cc, err := grpc.DialContext(dialCtx, address, grpcDialOptions...)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
}
@@ -186,7 +208,11 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
ServerStreams: true,
}
st, err := cc.NewStream(ctx, desc, methodToGRPC(req.Service(), req.Endpoint()))
grpcCallOptions := []grpc.CallOption{}
if opts := g.getGrpcCallOptions(); opts != nil {
grpcCallOptions = append(grpcCallOptions, opts...)
}
st, err := cc.NewStream(ctx, desc, methodToGRPC(req.Service(), req.Endpoint()), grpcCallOptions...)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err))
}
@@ -350,15 +376,17 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
// select next node
node, err := next()
if err != nil && err == selector.ErrNotFound {
return errors.NotFound("go.micro.client", err.Error())
} else if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
service := req.Service()
if err != nil {
if err == selector.ErrNotFound {
return errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
}
// make the call
err = gcall(ctx, node, req, rsp, callOpts)
g.opts.Selector.Mark(req.Service(), node, err)
g.opts.Selector.Mark(service, node, err)
return err
}
@@ -429,14 +457,16 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
}
node, err := next()
if err != nil && err == selector.ErrNotFound {
return nil, errors.NotFound("go.micro.client", err.Error())
} else if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
service := req.Service()
if err != nil {
if err == selector.ErrNotFound {
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
}
stream, err := g.stream(ctx, node, req, callOpts)
g.opts.Selector.Mark(req.Service(), node, err)
g.opts.Selector.Mark(service, node, err)
return stream, err
}
@@ -486,14 +516,13 @@ func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...clie
}
md["Content-Type"] = p.ContentType()
cf, err := g.newCodec(p.ContentType())
cf, err := g.newGRPCCodec(p.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
b := buf.New(nil)
if err := cf(b).Write(&codec.Message{Type: codec.Event}, p.Payload()); err != nil {
b, err := cf.Marshal(p.Payload())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
@@ -503,7 +532,7 @@ func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...clie
return g.opts.Broker.Publish(p.Topic(), &broker.Message{
Header: md,
Body: b.Bytes(),
Body: b,
})
}
@@ -511,6 +540,46 @@ func (g *grpcClient) String() string {
return "grpc"
}
func (g *grpcClient) getGrpcDialOptions() []grpc.DialOption {
if g.opts.CallOptions.Context == nil {
return nil
}
v := g.opts.CallOptions.Context.Value(grpcDialOptions{})
if v == nil {
return nil
}
opts, ok := v.([]grpc.DialOption)
if !ok {
return nil
}
return opts
}
func (g *grpcClient) getGrpcCallOptions() []grpc.CallOption {
if g.opts.CallOptions.Context == nil {
return nil
}
v := g.opts.CallOptions.Context.Value(grpcCallOptions{})
if v == nil {
return nil
}
opts, ok := v.([]grpc.CallOption)
if !ok {
return nil
}
return opts
}
func newClient(opts ...client.Option) client.Client {
options := client.Options{
Codecs: make(map[string]codec.NewCodec),

View File

@@ -6,6 +6,7 @@ import (
"crypto/tls"
"github.com/micro/go-micro/client"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
)
@@ -23,6 +24,8 @@ type codecsKey struct{}
type tlsAuth struct{}
type maxRecvMsgSizeKey struct{}
type maxSendMsgSizeKey struct{}
type grpcDialOptions struct{}
type grpcCallOptions struct{}
// gRPC Codec to be used to encode/decode requests for a given content type
func Codec(contentType string, c encoding.Codec) client.Option {
@@ -72,3 +75,27 @@ func MaxSendMsgSize(s int) client.Option {
o.Context = context.WithValue(o.Context, maxSendMsgSizeKey{}, s)
}
}
//
// DialOptions to be used to configure gRPC dial options
//
func DialOptions(opts ...grpc.DialOption) client.CallOption {
return func(o *client.CallOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, grpcDialOptions{}, opts)
}
}
//
// CallOptions to be used to configure gRPC call options
//
func CallOptions(opts ...grpc.CallOption) client.CallOption {
return func(o *client.CallOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, grpcCallOptions{}, opts)
}
}

View File

@@ -31,37 +31,37 @@ var _ context.Context
var _ client.Option
var _ server.Option
// Client API for Micro service
// Client API for Client service
type MicroService interface {
type ClientService interface {
// Call allows a single request to be made
Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
// Stream is a bidirectional stream
Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error)
Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error)
// Publish publishes a message and returns an empty Message
Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error)
}
type microService struct {
type clientService struct {
c client.Client
name string
}
func NewMicroService(name string, c client.Client) MicroService {
func NewClientService(name string, c client.Client) ClientService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.client"
}
return &microService{
return &clientService{
c: c,
name: name,
}
}
func (c *microService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.name, "Micro.Call", in)
func (c *clientService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.name, "Client.Call", in)
out := new(Response)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
@@ -70,16 +70,16 @@ func (c *microService) Call(ctx context.Context, in *Request, opts ...client.Cal
return out, nil
}
func (c *microService) Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error) {
req := c.c.NewRequest(c.name, "Micro.Stream", &Request{})
func (c *clientService) Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error) {
req := c.c.NewRequest(c.name, "Client.Stream", &Request{})
stream, err := c.c.Stream(ctx, req, opts...)
if err != nil {
return nil, err
}
return &microServiceStream{stream}, nil
return &clientServiceStream{stream}, nil
}
type Micro_StreamService interface {
type Client_StreamService interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
@@ -87,27 +87,27 @@ type Micro_StreamService interface {
Recv() (*Response, error)
}
type microServiceStream struct {
type clientServiceStream struct {
stream client.Stream
}
func (x *microServiceStream) Close() error {
func (x *clientServiceStream) Close() error {
return x.stream.Close()
}
func (x *microServiceStream) SendMsg(m interface{}) error {
func (x *clientServiceStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *microServiceStream) RecvMsg(m interface{}) error {
func (x *clientServiceStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *microServiceStream) Send(m *Request) error {
func (x *clientServiceStream) Send(m *Request) error {
return x.stream.Send(m)
}
func (x *microServiceStream) Recv() (*Response, error) {
func (x *clientServiceStream) Recv() (*Response, error) {
m := new(Response)
err := x.stream.Recv(m)
if err != nil {
@@ -116,8 +116,8 @@ func (x *microServiceStream) Recv() (*Response, error) {
return m, nil
}
func (c *microService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) {
req := c.c.NewRequest(c.name, "Micro.Publish", in)
func (c *clientService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) {
req := c.c.NewRequest(c.name, "Client.Publish", in)
out := new(Message)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
@@ -126,43 +126,43 @@ func (c *microService) Publish(ctx context.Context, in *Message, opts ...client.
return out, nil
}
// Server API for Micro service
// Server API for Client service
type MicroHandler interface {
type ClientHandler interface {
// Call allows a single request to be made
Call(context.Context, *Request, *Response) error
// Stream is a bidirectional stream
Stream(context.Context, Micro_StreamStream) error
Stream(context.Context, Client_StreamStream) error
// Publish publishes a message and returns an empty Message
Publish(context.Context, *Message, *Message) error
}
func RegisterMicroHandler(s server.Server, hdlr MicroHandler, opts ...server.HandlerOption) error {
type micro interface {
func RegisterClientHandler(s server.Server, hdlr ClientHandler, opts ...server.HandlerOption) error {
type client interface {
Call(ctx context.Context, in *Request, out *Response) error
Stream(ctx context.Context, stream server.Stream) error
Publish(ctx context.Context, in *Message, out *Message) error
}
type Micro struct {
micro
type Client struct {
client
}
h := &microHandler{hdlr}
return s.Handle(s.NewHandler(&Micro{h}, opts...))
h := &clientHandler{hdlr}
return s.Handle(s.NewHandler(&Client{h}, opts...))
}
type microHandler struct {
MicroHandler
type clientHandler struct {
ClientHandler
}
func (h *microHandler) Call(ctx context.Context, in *Request, out *Response) error {
return h.MicroHandler.Call(ctx, in, out)
func (h *clientHandler) Call(ctx context.Context, in *Request, out *Response) error {
return h.ClientHandler.Call(ctx, in, out)
}
func (h *microHandler) Stream(ctx context.Context, stream server.Stream) error {
return h.MicroHandler.Stream(ctx, &microStreamStream{stream})
func (h *clientHandler) Stream(ctx context.Context, stream server.Stream) error {
return h.ClientHandler.Stream(ctx, &clientStreamStream{stream})
}
type Micro_StreamStream interface {
type Client_StreamStream interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
@@ -170,27 +170,27 @@ type Micro_StreamStream interface {
Recv() (*Request, error)
}
type microStreamStream struct {
type clientStreamStream struct {
stream server.Stream
}
func (x *microStreamStream) Close() error {
func (x *clientStreamStream) Close() error {
return x.stream.Close()
}
func (x *microStreamStream) SendMsg(m interface{}) error {
func (x *clientStreamStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *microStreamStream) RecvMsg(m interface{}) error {
func (x *clientStreamStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *microStreamStream) Send(m *Response) error {
func (x *clientStreamStream) Send(m *Response) error {
return x.stream.Send(m)
}
func (x *microStreamStream) Recv() (*Request, error) {
func (x *clientStreamStream) Recv() (*Request, error) {
m := new(Request)
if err := x.stream.Recv(m); err != nil {
return nil, err
@@ -198,6 +198,6 @@ func (x *microStreamStream) Recv() (*Request, error) {
return m, nil
}
func (h *microHandler) Publish(ctx context.Context, in *Message, out *Message) error {
return h.MicroHandler.Publish(ctx, in, out)
func (h *clientHandler) Publish(ctx context.Context, in *Message, out *Message) error {
return h.ClientHandler.Publish(ctx, in, out)
}

View File

@@ -191,23 +191,23 @@ func init() {
var fileDescriptor_7d733ae29171347b = []byte{
// 270 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x3f, 0x4f, 0xc3, 0x30,
0x10, 0xc5, 0xeb, 0xfe, 0x4b, 0x39, 0x2a, 0x21, 0x9d, 0x18, 0x4c, 0x06, 0x54, 0x32, 0x65, 0xc1,
0x45, 0x30, 0x23, 0x86, 0xce, 0x95, 0x50, 0x40, 0xac, 0x28, 0x71, 0x4f, 0xc1, 0x52, 0x6a, 0x9b,
0xd8, 0xad, 0x94, 0xef, 0xc8, 0x87, 0x42, 0x38, 0x29, 0x45, 0xd0, 0x2e, 0x6c, 0xf7, 0xee, 0x67,
0xbd, 0x3b, 0xbf, 0x83, 0x74, 0xad, 0x64, 0x6d, 0xe6, 0xa5, 0xb9, 0x6e, 0x0b, 0x59, 0x29, 0xd2,
0x7e, 0x6e, 0x6b, 0xe3, 0x77, 0x42, 0x04, 0x81, 0x67, 0xa5, 0x11, 0xe1, 0x8d, 0x68, 0xdb, 0xc9,
0x16, 0xa2, 0x8c, 0xde, 0x37, 0xe4, 0x3c, 0x72, 0x88, 0x1c, 0xd5, 0x5b, 0x25, 0x89, 0xb3, 0x19,
0x4b, 0x4f, 0xb2, 0x9d, 0xc4, 0x18, 0x26, 0xa4, 0x57, 0xd6, 0x28, 0xed, 0x79, 0x3f, 0xa0, 0x6f,
0x8d, 0x57, 0x30, 0x95, 0x46, 0x7b, 0xd2, 0xfe, 0xd5, 0x37, 0x96, 0xf8, 0x20, 0xf0, 0xd3, 0xae,
0xf7, 0xdc, 0x58, 0x42, 0x84, 0x61, 0x61, 0x56, 0x0d, 0x1f, 0xce, 0x58, 0x3a, 0xcd, 0x42, 0x9d,
0x5c, 0xc2, 0x24, 0x23, 0x67, 0x8d, 0x76, 0x7b, 0xce, 0x7e, 0xf0, 0x17, 0x88, 0x96, 0xe4, 0x5c,
0x5e, 0x12, 0x9e, 0xc3, 0xc8, 0x1b, 0xab, 0x64, 0xb7, 0x55, 0x2b, 0xfe, 0xcc, 0xed, 0x1f, 0x9f,
0x3b, 0xd8, 0xfb, 0xde, 0x7e, 0x30, 0x18, 0x2d, 0xbf, 0x02, 0xc0, 0x7b, 0x18, 0x2e, 0xf2, 0xaa,
0x42, 0x2e, 0x7e, 0x65, 0x22, 0xba, 0x40, 0xe2, 0x8b, 0x03, 0xa4, 0x5d, 0x39, 0xe9, 0xe1, 0x02,
0xc6, 0x4f, 0xbe, 0xa6, 0x7c, 0xfd, 0x4f, 0x83, 0x94, 0xdd, 0x30, 0x7c, 0x80, 0xe8, 0x71, 0x53,
0x54, 0xca, 0xbd, 0x1d, 0x70, 0xe9, 0xfe, 0x1f, 0x1f, 0x25, 0x49, 0xaf, 0x18, 0x87, 0xb3, 0xde,
0x7d, 0x06, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x63, 0x94, 0x1a, 0x02, 0x02, 0x00, 0x00,
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.
@@ -218,59 +218,59 @@ var _ grpc.ClientConn
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// MicroClient is the client API for Micro service.
// 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 MicroClient interface {
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) (Micro_StreamClient, error)
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 microClient struct {
type clientClient struct {
cc *grpc.ClientConn
}
func NewMicroClient(cc *grpc.ClientConn) MicroClient {
return &microClient{cc}
func NewClientClient(cc *grpc.ClientConn) ClientClient {
return &clientClient{cc}
}
func (c *microClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
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.Micro/Call", in, out, opts...)
err := c.cc.Invoke(ctx, "/go.micro.client.Client/Call", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *microClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Micro_StreamClient, error) {
stream, err := c.cc.NewStream(ctx, &_Micro_serviceDesc.Streams[0], "/go.micro.client.Micro/Stream", opts...)
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 := &microStreamClient{stream}
x := &clientStreamClient{stream}
return x, nil
}
type Micro_StreamClient interface {
type Client_StreamClient interface {
Send(*Request) error
Recv() (*Response, error)
grpc.ClientStream
}
type microStreamClient struct {
type clientStreamClient struct {
grpc.ClientStream
}
func (x *microStreamClient) Send(m *Request) error {
func (x *clientStreamClient) Send(m *Request) error {
return x.ClientStream.SendMsg(m)
}
func (x *microStreamClient) Recv() (*Response, error) {
func (x *clientStreamClient) Recv() (*Response, error) {
m := new(Response)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
@@ -278,66 +278,66 @@ func (x *microStreamClient) Recv() (*Response, error) {
return m, nil
}
func (c *microClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
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.Micro/Publish", in, out, opts...)
err := c.cc.Invoke(ctx, "/go.micro.client.Client/Publish", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MicroServer is the server API for Micro service.
type MicroServer interface {
// 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(Micro_StreamServer) error
Stream(Client_StreamServer) error
// Publish publishes a message and returns an empty Message
Publish(context.Context, *Message) (*Message, error)
}
func RegisterMicroServer(s *grpc.Server, srv MicroServer) {
s.RegisterService(&_Micro_serviceDesc, srv)
func RegisterClientServer(s *grpc.Server, srv ClientServer) {
s.RegisterService(&_Client_serviceDesc, srv)
}
func _Micro_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
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.(MicroServer).Call(ctx, in)
return srv.(ClientServer).Call(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.client.Micro/Call",
FullMethod: "/go.micro.client.Client/Call",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MicroServer).Call(ctx, req.(*Request))
return srv.(ClientServer).Call(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
func _Micro_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(MicroServer).Stream(&microStreamServer{stream})
func _Client_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(ClientServer).Stream(&clientStreamServer{stream})
}
type Micro_StreamServer interface {
type Client_StreamServer interface {
Send(*Response) error
Recv() (*Request, error)
grpc.ServerStream
}
type microStreamServer struct {
type clientStreamServer struct {
grpc.ServerStream
}
func (x *microStreamServer) Send(m *Response) error {
func (x *clientStreamServer) Send(m *Response) error {
return x.ServerStream.SendMsg(m)
}
func (x *microStreamServer) Recv() (*Request, error) {
func (x *clientStreamServer) Recv() (*Request, error) {
m := new(Request)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
@@ -345,41 +345,41 @@ func (x *microStreamServer) Recv() (*Request, error) {
return m, nil
}
func _Micro_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
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.(MicroServer).Publish(ctx, in)
return srv.(ClientServer).Publish(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.client.Micro/Publish",
FullMethod: "/go.micro.client.Client/Publish",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MicroServer).Publish(ctx, req.(*Message))
return srv.(ClientServer).Publish(ctx, req.(*Message))
}
return interceptor(ctx, in, info, handler)
}
var _Micro_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.client.Micro",
HandlerType: (*MicroServer)(nil),
var _Client_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.client.Client",
HandlerType: (*ClientServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Call",
Handler: _Micro_Call_Handler,
Handler: _Client_Call_Handler,
},
{
MethodName: "Publish",
Handler: _Micro_Publish_Handler,
Handler: _Client_Publish_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Stream",
Handler: _Micro_Stream_Handler,
Handler: _Client_Stream_Handler,
ServerStreams: true,
ClientStreams: true,
},

View File

@@ -2,8 +2,8 @@ syntax = "proto3";
package go.micro.client;
// Micro is the micro client interface
service Micro {
// Client is the micro client interface
service Client {
// Call allows a single request to be made
rpc Call(Request) returns (Response) {};
// Stream is a bidirectional stream

View File

@@ -96,19 +96,22 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
}
}
var grr error
c, err := r.pool.Get(address, transport.WithTimeout(opts.DialTimeout))
dOpts := []transport.DialOption{
transport.WithStream(),
}
if opts.DialTimeout >= 0 {
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
}
c, err := r.pool.Get(address, dOpts...)
if err != nil {
return errors.InternalServerError("go.micro.client", "connection error: %v", err)
}
defer func() {
// defer execution of release
r.pool.Release(c, grr)
}()
seq := atomic.LoadUint64(&r.seq)
atomic.AddUint64(&r.seq, 1)
codec := newRpcCodec(msg, c, cf)
codec := newRpcCodec(msg, c, cf, "")
rsp := &rpcResponse{
socket: c,
@@ -116,15 +119,19 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
}
stream := &rpcStream{
id: fmt.Sprintf("%v", seq),
context: ctx,
request: req,
response: rsp,
codec: codec,
closed: make(chan bool),
id: fmt.Sprintf("%v", seq),
release: func(err error) { r.pool.Release(c, err) },
sendEOS: false,
}
// close the stream on exiting this function
defer stream.Close()
// wait for error response
ch := make(chan error, 1)
go func() {
@@ -150,14 +157,26 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
ch <- nil
}()
var grr error
select {
case err := <-ch:
grr = err
return err
case <-ctx.Done():
grr = ctx.Err()
return errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
}
// set the stream error
if grr != nil {
stream.Lock()
stream.err = grr
stream.Unlock()
return grr
}
return nil
}
func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request, opts CallOptions) (Stream, error) {
@@ -206,7 +225,13 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
return nil, errors.InternalServerError("go.micro.client", "connection error: %v", err)
}
codec := newRpcCodec(msg, c, cf)
// increment the sequence number
seq := atomic.LoadUint64(&r.seq)
atomic.AddUint64(&r.seq, 1)
id := fmt.Sprintf("%v", seq)
// create codec with stream id
codec := newRpcCodec(msg, c, cf, id)
rsp := &rpcResponse{
socket: c,
@@ -219,16 +244,24 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
}
stream := &rpcStream{
id: id,
context: ctx,
request: req,
response: rsp,
closed: make(chan bool),
codec: codec,
// used to close the stream
closed: make(chan bool),
// signal the end of stream,
sendEOS: true,
// release func
release: func(err error) { c.Close() },
}
// wait for error response
ch := make(chan error, 1)
go func() {
// send the first message
ch <- stream.Send(req.Body())
}()
@@ -242,6 +275,12 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
}
if grr != nil {
// set the error
stream.Lock()
stream.err = grr
stream.Unlock()
// close the stream
stream.Close()
return nil, grr
}
@@ -312,10 +351,11 @@ func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, erro
// get next nodes from the selector
next, err := r.opts.Selector.Select(service, opts.SelectOptions...)
if err != nil && err == selector.ErrNotFound {
return nil, errors.NotFound("go.micro.client", "service %s: %v", service, err.Error())
} else if err != nil {
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %v", service, err.Error())
if err != nil {
if err == selector.ErrNotFound {
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
}
return next, nil
@@ -375,15 +415,17 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
// select next node
node, err := next()
if err != nil && err == selector.ErrNotFound {
return errors.NotFound("go.micro.client", "service %s: %v", request.Service(), err.Error())
} else if err != nil {
return errors.InternalServerError("go.micro.client", "error getting next %s node: %v", request.Service(), err.Error())
service := request.Service()
if err != nil {
if err == selector.ErrNotFound {
return errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return errors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
}
// make the call
err = rcall(ctx, node, request, response, callOpts)
r.opts.Selector.Mark(request.Service(), node, err)
r.opts.Selector.Mark(service, node, err)
return err
}
@@ -452,14 +494,16 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
}
node, err := next()
if err != nil && err == selector.ErrNotFound {
return nil, errors.NotFound("go.micro.client", "service %s: %v", request.Service(), err.Error())
} else if err != nil {
return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %v", request.Service(), err.Error())
service := request.Service()
if err != nil {
if err == selector.ErrNotFound {
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
}
stream, err := r.stream(ctx, node, request, callOpts)
r.opts.Selector.Mark(request.Service(), node, err)
r.opts.Selector.Mark(service, node, err)
return stream, err
}

View File

@@ -39,6 +39,9 @@ type rpcCodec struct {
req *transport.Message
buf *readWriteCloser
// signify if its a stream
stream string
}
type readWriteCloser struct {
@@ -113,7 +116,7 @@ func getHeaders(m *codec.Message) {
}
}
func setHeaders(m *codec.Message) {
func setHeaders(m *codec.Message, stream string) {
set := func(hdr, v string) {
if len(v) == 0 {
return
@@ -126,6 +129,11 @@ func setHeaders(m *codec.Message) {
set("Micro-Service", m.Target)
set("Micro-Method", m.Method)
set("Micro-Endpoint", m.Endpoint)
set("Micro-Error", m.Error)
if len(stream) > 0 {
set("Micro-Stream", stream)
}
}
// setupProtocol sets up the old protocol
@@ -149,7 +157,7 @@ func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec {
return defaultCodecs[msg.Header["Content-Type"]]
}
func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCodec) codec.Codec {
func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCodec, stream string) codec.Codec {
rwc := &readWriteCloser{
wbuf: bytes.NewBuffer(nil),
rbuf: bytes.NewBuffer(nil),
@@ -159,6 +167,7 @@ func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCod
client: client,
codec: c(rwc),
req: req,
stream: stream,
}
return r
}
@@ -177,7 +186,7 @@ func (c *rpcCodec) Write(m *codec.Message, body interface{}) error {
}
// set the mucp headers
setHeaders(m)
setHeaders(m, c.stream)
// if body is bytes Frame don't encode
if body != nil {
@@ -240,6 +249,12 @@ func (c *rpcCodec) ReadHeader(m *codec.Message, r codec.MessageType) error {
func (c *rpcCodec) ReadBody(b interface{}) error {
// read body
// read raw data
if v, ok := b.(*raw.Frame); ok {
v.Data = c.buf.rbuf.Bytes()
return nil
}
if err := c.codec.ReadBody(b); err != nil {
return errors.InternalServerError("go.micro.client.codec", err.Error())
}

View File

@@ -18,6 +18,12 @@ type rpcStream struct {
response Response
codec codec.Codec
context context.Context
// signal whether we should send EOS
sendEOS bool
// release releases the connection back to the pool
release func(err error)
}
func (r *rpcStream) isClosed() bool {
@@ -120,6 +126,26 @@ func (r *rpcStream) Close() error {
return nil
default:
close(r.closed)
return r.codec.Close()
// send the end of stream message
if r.sendEOS {
// no need to check for error
r.codec.Write(&codec.Message{
Id: r.id,
Target: r.request.Service(),
Method: r.request.Method(),
Endpoint: r.request.Endpoint(),
Type: codec.Error,
Error: lastStreamResponseError,
}, nil)
}
err := r.codec.Close()
// release the connection
r.release(r.Error())
// return the codec error
return err
}
}

View File

@@ -51,6 +51,9 @@ func (c *registrySelector) Select(service string, opts ...SelectOption) (Next, e
// if that fails go directly to the registry
services, err := c.rc.GetService(service)
if err != nil {
if err == registry.ErrNotFound {
return nil, ErrNotFound
}
return nil, err
}

View File

@@ -9,9 +9,9 @@ import (
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/network/router"
pb "github.com/micro/go-micro/network/router/proto"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/router"
pb "github.com/micro/go-micro/router/proto"
)
type routerSelector struct {
@@ -43,9 +43,9 @@ type routerKey struct{}
func (r *routerSelector) getRoutes(service string) ([]router.Route, error) {
if !r.remote {
// lookup router for routes for the service
return r.r.Lookup(router.NewQuery(
return r.r.Lookup(
router.QueryService(service),
))
)
}
// lookup the remote router

View File

@@ -89,9 +89,22 @@ func (c *Codec) Write(m *codec.Message, b interface{}) error {
m.Header[":authority"] = m.Target
m.Header["content-type"] = c.ContentType
case codec.Response:
m.Header["Trailer"] = "grpc-status, grpc-message"
m.Header["Trailer"] = "grpc-status" //, grpc-message"
m.Header["content-type"] = c.ContentType
m.Header[":status"] = "200"
m.Header["grpc-status"] = "0"
m.Header["grpc-message"] = ""
// m.Header["grpc-message"] = ""
case codec.Error:
m.Header["Trailer"] = "grpc-status, grpc-message"
// micro end of stream
if m.Error == "EOS" {
m.Header["grpc-status"] = "0"
} else {
m.Header["grpc-message"] = m.Error
m.Header["grpc-status"] = "13"
}
return nil
}
// marshal content

View File

@@ -23,13 +23,15 @@ import (
"github.com/micro/go-micro/broker/http"
"github.com/micro/go-micro/broker/memory"
"github.com/micro/go-micro/broker/nats"
brokerSrv "github.com/micro/go-micro/broker/service"
// registries
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/registry/consul"
"github.com/micro/go-micro/registry/gossip"
"github.com/micro/go-micro/registry/etcd"
"github.com/micro/go-micro/registry/mdns"
rmem "github.com/micro/go-micro/registry/memory"
regSrv "github.com/micro/go-micro/registry/service"
// selectors
"github.com/micro/go-micro/client/selector"
@@ -42,6 +44,7 @@ import (
tgrpc "github.com/micro/go-micro/transport/grpc"
thttp "github.com/micro/go-micro/transport/http"
tmem "github.com/micro/go-micro/transport/memory"
"github.com/micro/go-micro/transport/quic"
)
type Cmd interface {
@@ -94,11 +97,13 @@ var (
cli.IntFlag{
Name: "register_ttl",
EnvVar: "MICRO_REGISTER_TTL",
Value: 60,
Usage: "Register TTL in seconds",
},
cli.IntFlag{
Name: "register_interval",
EnvVar: "MICRO_REGISTER_INTERVAL",
Value: 30,
Usage: "Register interval in seconds",
},
cli.StringFlag{
@@ -150,7 +155,7 @@ var (
cli.StringFlag{
Name: "registry",
EnvVar: "MICRO_REGISTRY",
Usage: "Registry for discovery. consul, mdns",
Usage: "Registry for discovery. consul, etcd, mdns",
},
cli.StringFlag{
Name: "registry_address",
@@ -175,9 +180,11 @@ var (
}
DefaultBrokers = map[string]func(...broker.Option) broker.Broker{
"http": http.NewBroker,
"memory": memory.NewBroker,
"nats": nats.NewBroker,
"go.micro.broker": brokerSrv.NewBroker,
"service": brokerSrv.NewBroker,
"http": http.NewBroker,
"memory": memory.NewBroker,
"nats": nats.NewBroker,
}
DefaultClients = map[string]func(...client.Option) client.Client{
@@ -187,10 +194,12 @@ var (
}
DefaultRegistries = map[string]func(...registry.Option) registry.Registry{
"consul": consul.NewRegistry,
"gossip": gossip.NewRegistry,
"mdns": mdns.NewRegistry,
"memory": rmem.NewRegistry,
"go.micro.registry": regSrv.NewRegistry,
"service": regSrv.NewRegistry,
"consul": consul.NewRegistry,
"etcd": etcd.NewRegistry,
"mdns": mdns.NewRegistry,
"memory": rmem.NewRegistry,
}
DefaultSelectors = map[string]func(...selector.Option) selector.Selector{
@@ -211,6 +220,7 @@ var (
"memory": tmem.NewTransport,
"http": thttp.NewTransport,
"grpc": tgrpc.NewTransport,
"quic": quic.NewTransport,
}
// used for default selection as the fall back
@@ -415,11 +425,11 @@ func (c *cmd) Before(ctx *cli.Context) error {
serverOpts = append(serverOpts, server.Advertise(ctx.String("server_advertise")))
}
if ttl := time.Duration(ctx.GlobalInt("register_ttl")); ttl > 0 {
if ttl := time.Duration(ctx.GlobalInt("register_ttl")); ttl >= 0 {
serverOpts = append(serverOpts, server.RegisterTTL(ttl*time.Second))
}
if val := time.Duration(ctx.GlobalInt("register_interval")); val > 0 {
if val := time.Duration(ctx.GlobalInt("register_interval")); val >= 0 {
serverOpts = append(serverOpts, server.RegisterInterval(val*time.Second))
}

View File

@@ -12,6 +12,10 @@ import (
"github.com/micro/go-micro/config/source/file"
)
var (
sep = string(os.PathSeparator)
)
func createFileForIssue18(t *testing.T, content string) *os.File {
data := []byte(content)
path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano()))

View File

@@ -1,49 +0,0 @@
# Consul Source
The consul source reads config from consul key/values
## Consul Format
The consul source expects keys under the default prefix `/micro/config`
Values are expected to be json
```
// set database
consul kv put micro/config/database '{"address": "10.0.0.1", "port": 3306}'
// set cache
consul kv put micro/config/cache '{"address": "10.0.0.2", "port": 6379}'
```
Keys are split on `/` so access becomes
```
conf.Get("micro", "config", "database")
```
## New Source
Specify source with data
```go
consulSource := consul.NewSource(
// optionally specify consul address; default to localhost:8500
consul.WithAddress("10.0.0.10:8500"),
// optionally specify prefix; defaults to /micro/config
consul.WithPrefix("/my/prefix"),
// optionally strip the provided prefix from the keys, defaults to false
consul.StripPrefix(true),
)
```
## Load Source
Load the source into config
```go
// Create new config
conf := config.NewConfig()
// Load file source
conf.Load(consulSource)
```

View File

@@ -1,126 +0,0 @@
package consul
import (
"fmt"
"net"
"time"
"github.com/hashicorp/consul/api"
"github.com/micro/go-micro/config/source"
)
// Currently a single consul reader
type consul struct {
prefix string
stripPrefix string
addr string
opts source.Options
client *api.Client
}
var (
// DefaultPrefix is the prefix that consul keys will be assumed to have if you
// haven't specified one
DefaultPrefix = "/micro/config/"
)
func (c *consul) Read() (*source.ChangeSet, error) {
kv, _, err := c.client.KV().List(c.prefix, nil)
if err != nil {
return nil, err
}
if kv == nil || len(kv) == 0 {
return nil, fmt.Errorf("source not found: %s", c.prefix)
}
data, err := makeMap(c.opts.Encoder, kv, c.stripPrefix)
if err != nil {
return nil, fmt.Errorf("error reading data: %v", err)
}
b, err := c.opts.Encoder.Encode(data)
if err != nil {
return nil, fmt.Errorf("error reading source: %v", err)
}
cs := &source.ChangeSet{
Timestamp: time.Now(),
Format: c.opts.Encoder.String(),
Source: c.String(),
Data: b,
}
cs.Checksum = cs.Sum()
return cs, nil
}
func (c *consul) String() string {
return "consul"
}
func (c *consul) Watch() (source.Watcher, error) {
w, err := newWatcher(c.prefix, c.addr, c.String(), c.stripPrefix, c.opts.Encoder)
if err != nil {
return nil, err
}
return w, nil
}
// NewSource creates a new consul source
func NewSource(opts ...source.Option) source.Source {
options := source.NewOptions(opts...)
// use default config
config := api.DefaultConfig()
// use the consul config passed in the options if any
if co, ok := options.Context.Value(configKey{}).(*api.Config); ok {
config = co
}
// check if there are any addrs
a, ok := options.Context.Value(addressKey{}).(string)
if ok {
addr, port, err := net.SplitHostPort(a)
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
port = "8500"
addr = a
config.Address = fmt.Sprintf("%s:%s", addr, port)
} else if err == nil {
config.Address = fmt.Sprintf("%s:%s", addr, port)
}
}
dc, ok := options.Context.Value(dcKey{}).(string)
if ok {
config.Datacenter = dc
}
token, ok := options.Context.Value(tokenKey{}).(string)
if ok {
config.Token = token
}
// create the client
client, _ := api.NewClient(config)
prefix := DefaultPrefix
sp := ""
f, ok := options.Context.Value(prefixKey{}).(string)
if ok {
prefix = f
}
if b, ok := options.Context.Value(stripPrefixKey{}).(bool); ok && b {
sp = prefix
}
return &consul{
prefix: prefix,
stripPrefix: sp,
addr: config.Address,
opts: options,
client: client,
}
}

View File

@@ -1,89 +0,0 @@
package consul
import (
"fmt"
"strings"
"github.com/hashicorp/consul/api"
"github.com/micro/go-micro/config/encoder"
)
type configValue interface {
Value() interface{}
Decode(encoder.Encoder, []byte) error
}
type configArrayValue struct {
v []interface{}
}
func (a *configArrayValue) Value() interface{} { return a.v }
func (a *configArrayValue) Decode(e encoder.Encoder, b []byte) error {
return e.Decode(b, &a.v)
}
type configMapValue struct {
v map[string]interface{}
}
func (m *configMapValue) Value() interface{} { return m.v }
func (m *configMapValue) Decode(e encoder.Encoder, b []byte) error {
return e.Decode(b, &m.v)
}
func makeMap(e encoder.Encoder, kv api.KVPairs, stripPrefix string) (map[string]interface{}, error) {
data := make(map[string]interface{})
// consul guarantees lexicographic order, so no need to sort
for _, v := range kv {
pathString := strings.TrimPrefix(strings.TrimPrefix(v.Key, strings.TrimPrefix(stripPrefix, "/")), "/")
if pathString == "" {
continue
}
var val configValue
var err error
// ensure a valid value is stored at this location
if len(v.Value) > 0 {
// try to decode into map value or array value
arrayV := &configArrayValue{v: []interface{}{}}
mapV := &configMapValue{v: map[string]interface{}{}}
switch {
case arrayV.Decode(e, v.Value) == nil:
val = arrayV
case mapV.Decode(e, v.Value) == nil:
val = mapV
default:
return nil, fmt.Errorf("faild decode value. path: %s, error: %s", pathString, err)
}
}
// set target at the root
target := data
path := strings.Split(pathString, "/")
// find (or create) the leaf node we want to put this value at
for _, dir := range path[:len(path)-1] {
if _, ok := target[dir]; !ok {
target[dir] = make(map[string]interface{})
}
target = target[dir].(map[string]interface{})
}
leafDir := path[len(path)-1]
// copy over the keys from the value
switch val.(type) {
case *configArrayValue:
target[leafDir] = val.Value()
case *configMapValue:
target[leafDir] = make(map[string]interface{})
target = target[leafDir].(map[string]interface{})
mapv := val.Value().(map[string]interface{})
for k := range mapv {
target[k] = mapv[k]
}
}
}
return data, nil
}

View File

@@ -1,96 +0,0 @@
package consul
import (
"errors"
"time"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/api/watch"
"github.com/micro/go-micro/config/encoder"
"github.com/micro/go-micro/config/source"
)
type watcher struct {
e encoder.Encoder
name string
stripPrefix string
wp *watch.Plan
ch chan *source.ChangeSet
exit chan bool
}
func newWatcher(key, addr, name, stripPrefix string, e encoder.Encoder) (source.Watcher, error) {
w := &watcher{
e: e,
name: name,
stripPrefix: stripPrefix,
ch: make(chan *source.ChangeSet),
exit: make(chan bool),
}
wp, err := watch.Parse(map[string]interface{}{"type": "keyprefix", "prefix": key})
if err != nil {
return nil, err
}
wp.Handler = w.handle
// wp.Run is a blocking call and will prevent newWatcher from returning
go wp.Run(addr)
w.wp = wp
return w, nil
}
func (w *watcher) handle(idx uint64, data interface{}) {
if data == nil {
return
}
kvs, ok := data.(api.KVPairs)
if !ok {
return
}
d, err := makeMap(w.e, kvs, w.stripPrefix)
if err != nil {
return
}
b, err := w.e.Encode(d)
if err != nil {
return
}
cs := &source.ChangeSet{
Timestamp: time.Now(),
Format: w.e.String(),
Source: w.name,
Data: b,
}
cs.Checksum = cs.Sum()
w.ch <- cs
}
func (w *watcher) Next() (*source.ChangeSet, error) {
select {
case cs := <-w.ch:
return cs, nil
case <-w.exit:
return nil, errors.New("watcher stopped")
}
}
func (w *watcher) Stop() error {
select {
case <-w.exit:
return nil
default:
w.wp.Stop()
close(w.exit)
}
return nil
}

View File

@@ -91,6 +91,6 @@ Load the source into config
// Create new config
conf := config.NewConfig()
// Load file source
// Load env source
conf.Load(src)
```

View File

@@ -86,8 +86,8 @@ func TestEnvvar_Prefixes(t *testing.T) {
}
func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) {
source := NewSource(WithStrippedPrefix("GOMICRO_"))
w, err := source.Watch()
src := NewSource(WithStrippedPrefix("GOMICRO_"))
w, err := src.Watch()
if err != nil {
t.Error(err)
}
@@ -97,7 +97,7 @@ func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) {
w.Stop()
}()
if _, err := w.Next(); err.Error() != "watcher stopped" {
if _, err := w.Next(); err != source.ErrWatcherStopped {
t.Errorf("expected watcher stopped error, got %v", err)
}
}

View File

@@ -1,8 +1,6 @@
package env
import (
"errors"
"github.com/micro/go-micro/config/source"
)
@@ -13,7 +11,7 @@ type watcher struct {
func (w *watcher) Next() (*source.ChangeSet, error) {
<-w.exit
return nil, errors.New("watcher stopped")
return nil, source.ErrWatcherStopped
}
func (w *watcher) Stop() error {

View File

@@ -0,0 +1,51 @@
# Etcd Source
The etcd source reads config from etcd key/values
This source supports etcd version 3 and beyond.
## Etcd Format
The etcd source expects keys under the default prefix `/micro/config` (prefix can be changed)
Values are expected to be JSON
```
// set database
etcdctl put /micro/config/database '{"address": "10.0.0.1", "port": 3306}'
// set cache
etcdctl put /micro/config/cache '{"address": "10.0.0.2", "port": 6379}'
```
Keys are split on `/` so access becomes
```
conf.Get("micro", "config", "database")
```
## New Source
Specify source with data
```go
etcdSource := etcd.NewSource(
// optionally specify etcd address; default to localhost:8500
etcd.WithAddress("10.0.0.10:8500"),
// optionally specify prefix; defaults to /micro/config
etcd.WithPrefix("/my/prefix"),
// optionally strip the provided prefix from the keys, defaults to false
etcd.StripPrefix(true),
)
```
## Load Source
Load the source into config
```go
// Create new config
conf := config.NewConfig()
// Load file source
conf.Load(etcdSource)
```

141
config/source/etcd/etcd.go Normal file
View File

@@ -0,0 +1,141 @@
package etcd
import (
"context"
"fmt"
"net"
"time"
cetcd "github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/mvcc/mvccpb"
"github.com/micro/go-micro/config/source"
)
// Currently a single etcd reader
type etcd struct {
prefix string
stripPrefix string
opts source.Options
client *cetcd.Client
cerr error
}
var (
DefaultPrefix = "/micro/config/"
)
func (c *etcd) Read() (*source.ChangeSet, error) {
if c.cerr != nil {
return nil, c.cerr
}
rsp, err := c.client.Get(context.Background(), c.prefix, cetcd.WithPrefix())
if err != nil {
return nil, err
}
if rsp == nil || len(rsp.Kvs) == 0 {
return nil, fmt.Errorf("source not found: %s", c.prefix)
}
var kvs []*mvccpb.KeyValue
for _, v := range rsp.Kvs {
kvs = append(kvs, (*mvccpb.KeyValue)(v))
}
data := makeMap(c.opts.Encoder, kvs, c.stripPrefix)
b, err := c.opts.Encoder.Encode(data)
if err != nil {
return nil, fmt.Errorf("error reading source: %v", err)
}
cs := &source.ChangeSet{
Timestamp: time.Now(),
Source: c.String(),
Data: b,
Format: c.opts.Encoder.String(),
}
cs.Checksum = cs.Sum()
return cs, nil
}
func (c *etcd) String() string {
return "etcd"
}
func (c *etcd) Watch() (source.Watcher, error) {
if c.cerr != nil {
return nil, c.cerr
}
cs, err := c.Read()
if err != nil {
return nil, err
}
return newWatcher(c.prefix, c.stripPrefix, c.client.Watcher, cs, c.opts)
}
func NewSource(opts ...source.Option) source.Source {
options := source.NewOptions(opts...)
var endpoints []string
// check if there are any addrs
addrs, ok := options.Context.Value(addressKey{}).([]string)
if ok {
for _, a := range addrs {
addr, port, err := net.SplitHostPort(a)
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
port = "2379"
addr = a
endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port))
} else if err == nil {
endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port))
}
}
}
if len(endpoints) == 0 {
endpoints = []string{"localhost:2379"}
}
// check dial timeout option
dialTimeout, ok := options.Context.Value(dialTimeoutKey{}).(time.Duration)
if !ok {
dialTimeout = 3 * time.Second // default dial timeout
}
config := cetcd.Config{
Endpoints: endpoints,
DialTimeout: dialTimeout,
}
u, ok := options.Context.Value(authKey{}).(*authCreds)
if ok {
config.Username = u.Username
config.Password = u.Password
}
// use default config
client, err := cetcd.New(config)
prefix := DefaultPrefix
sp := ""
f, ok := options.Context.Value(prefixKey{}).(string)
if ok {
prefix = f
}
if b, ok := options.Context.Value(stripPrefixKey{}).(bool); ok && b {
sp = prefix
}
return &etcd{
prefix: prefix,
stripPrefix: sp,
opts: options,
client: client,
cerr: err,
}
}

View File

@@ -1,21 +1,25 @@
package consul
package etcd
import (
"context"
"time"
"github.com/hashicorp/consul/api"
"github.com/micro/go-micro/config/source"
)
type addressKey struct{}
type prefixKey struct{}
type stripPrefixKey struct{}
type dcKey struct{}
type tokenKey struct{}
type configKey struct{}
type authKey struct{}
type dialTimeoutKey struct{}
// WithAddress sets the consul address
func WithAddress(a string) source.Option {
type authCreds struct {
Username string
Password string
}
// WithAddress sets the etcd address
func WithAddress(a ...string) source.Option {
return func(o *source.Options) {
if o.Context == nil {
o.Context = context.Background()
@@ -45,31 +49,22 @@ func StripPrefix(strip bool) source.Option {
}
}
func WithDatacenter(p string) source.Option {
// Auth allows you to specify username/password
func Auth(username, password string) source.Option {
return func(o *source.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, dcKey{}, p)
o.Context = context.WithValue(o.Context, authKey{}, &authCreds{Username: username, Password: password})
}
}
// WithToken sets the key token to use
func WithToken(p string) source.Option {
// WithDialTimeout set the time out for dialing to etcd
func WithDialTimeout(timeout time.Duration) source.Option {
return func(o *source.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, tokenKey{}, p)
}
}
// WithConfig set consul-specific options
func WithConfig(c *api.Config) source.Option {
return func(o *source.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, configKey{}, c)
o.Context = context.WithValue(o.Context, dialTimeoutKey{}, timeout)
}
}

View File

@@ -0,0 +1,89 @@
package etcd
import (
"strings"
"github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/mvcc/mvccpb"
"github.com/micro/go-micro/config/encoder"
)
func makeEvMap(e encoder.Encoder, data map[string]interface{}, kv []*clientv3.Event, stripPrefix string) map[string]interface{} {
if data == nil {
data = make(map[string]interface{})
}
for _, v := range kv {
switch mvccpb.Event_EventType(v.Type) {
case mvccpb.DELETE:
data = update(e, data, (*mvccpb.KeyValue)(v.Kv), "delete", stripPrefix)
default:
data = update(e, data, (*mvccpb.KeyValue)(v.Kv), "insert", stripPrefix)
}
}
return data
}
func makeMap(e encoder.Encoder, kv []*mvccpb.KeyValue, stripPrefix string) map[string]interface{} {
data := make(map[string]interface{})
for _, v := range kv {
data = update(e, data, v, "put", stripPrefix)
}
return data
}
func update(e encoder.Encoder, data map[string]interface{}, v *mvccpb.KeyValue, action, stripPrefix string) map[string]interface{} {
// remove prefix if non empty, and ensure leading / is removed as well
vkey := strings.TrimPrefix(strings.TrimPrefix(string(v.Key), stripPrefix), "/")
// split on prefix
haveSplit := strings.Contains(vkey, "/")
keys := strings.Split(vkey, "/")
var vals interface{}
e.Decode(v.Value, &vals)
if !haveSplit && len(keys) == 1 {
switch action {
case "delete":
data = make(map[string]interface{})
default:
v, ok := vals.(map[string]interface{})
if ok {
data = v
}
}
return data
}
// set data for first iteration
kvals := data
// iterate the keys and make maps
for i, k := range keys {
kval, ok := kvals[k].(map[string]interface{})
if !ok {
// create next map
kval = make(map[string]interface{})
// set it
kvals[k] = kval
}
// last key: write vals
if l := len(keys) - 1; i == l {
switch action {
case "delete":
delete(kvals, k)
default:
kvals[k] = vals
}
break
}
// set kvals for next iterator
kvals = kval
}
return data
}

View File

@@ -0,0 +1,113 @@
package etcd
import (
"context"
"errors"
"sync"
"time"
cetcd "github.com/coreos/etcd/clientv3"
"github.com/micro/go-micro/config/source"
)
type watcher struct {
opts source.Options
name string
stripPrefix string
sync.RWMutex
cs *source.ChangeSet
ch chan *source.ChangeSet
exit chan bool
}
func newWatcher(key, strip string, wc cetcd.Watcher, cs *source.ChangeSet, opts source.Options) (source.Watcher, error) {
w := &watcher{
opts: opts,
name: "etcd",
stripPrefix: strip,
cs: cs,
ch: make(chan *source.ChangeSet),
exit: make(chan bool),
}
ch := wc.Watch(context.Background(), key, cetcd.WithPrefix())
go w.run(wc, ch)
return w, nil
}
func (w *watcher) handle(evs []*cetcd.Event) {
w.RLock()
data := w.cs.Data
w.RUnlock()
var vals map[string]interface{}
// unpackage existing changeset
if err := w.opts.Encoder.Decode(data, &vals); err != nil {
return
}
// update base changeset
d := makeEvMap(w.opts.Encoder, vals, evs, w.stripPrefix)
// pack the changeset
b, err := w.opts.Encoder.Encode(d)
if err != nil {
return
}
// create new changeset
cs := &source.ChangeSet{
Timestamp: time.Now(),
Source: w.name,
Data: b,
Format: w.opts.Encoder.String(),
}
cs.Checksum = cs.Sum()
// set base change set
w.Lock()
w.cs = cs
w.Unlock()
// send update
w.ch <- cs
}
func (w *watcher) run(wc cetcd.Watcher, ch cetcd.WatchChan) {
for {
select {
case rsp, ok := <-ch:
if !ok {
return
}
w.handle(rsp.Events)
case <-w.exit:
wc.Close()
return
}
}
}
func (w *watcher) Next() (*source.ChangeSet, error) {
select {
case cs := <-w.ch:
return cs, nil
case <-w.exit:
return nil, errors.New("watcher stopped")
}
}
func (w *watcher) Stop() error {
select {
case <-w.exit:
return nil
default:
close(w.exit)
}
return nil
}

View File

@@ -1,7 +1,8 @@
//+build !linux
package file
import (
"errors"
"os"
"github.com/fsnotify/fsnotify"
@@ -34,7 +35,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
// is it closed?
select {
case <-w.exit:
return nil, errors.New("watcher stopped")
return nil, source.ErrWatcherStopped
default:
}
@@ -57,7 +58,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
case err := <-w.fw.Errors:
return nil, err
case <-w.exit:
return nil, errors.New("watcher stopped")
return nil, source.ErrWatcherStopped
}
}

View File

@@ -0,0 +1,71 @@
//+build linux
package file
import (
"os"
"github.com/fsnotify/fsnotify"
"github.com/micro/go-micro/config/source"
)
type watcher struct {
f *file
fw *fsnotify.Watcher
exit chan bool
}
func newWatcher(f *file) (source.Watcher, error) {
fw, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
fw.Add(f.path)
return &watcher{
f: f,
fw: fw,
exit: make(chan bool),
}, nil
}
func (w *watcher) Next() (*source.ChangeSet, error) {
// is it closed?
select {
case <-w.exit:
return nil, source.ErrWatcherStopped
default:
}
// try get the event
select {
case event, _ := <-w.fw.Events:
if event.Op == fsnotify.Rename {
// check existence of file, and add watch again
_, err := os.Stat(event.Name)
if err == nil || os.IsExist(err) {
w.fw.Add(event.Name)
}
}
c, err := w.f.Read()
if err != nil {
return nil, err
}
// add path again for the event bug of fsnotify
w.fw.Add(w.f.path)
return c, nil
case err := <-w.fw.Errors:
return nil, err
case <-w.exit:
return nil, source.ErrWatcherStopped
}
}
func (w *watcher) Stop() error {
return w.fw.Close()
}

View File

@@ -42,6 +42,6 @@ Load the source into config
// Create new config
conf := config.NewConfig()
// Load file source
// Load flag source
conf.Load(flagSource)
```

View File

@@ -12,13 +12,14 @@ var (
dbpw = flag.String("database-password", "", "db pw")
)
func init() {
func initTestFlags() {
flag.Set("database-host", "localhost")
flag.Set("database-password", "some-password")
flag.Parse()
}
func TestFlagsrc_Read(t *testing.T) {
initTestFlags()
source := NewSource()
c, err := source.Read()
if err != nil {
@@ -46,6 +47,7 @@ func TestFlagsrc_Read(t *testing.T) {
}
func TestFlagsrc_ReadAll(t *testing.T) {
initTestFlags()
source := NewSource(IncludeUnset(true))
c, err := source.Read()
if err != nil {

View File

@@ -27,7 +27,7 @@ Specify source with data
```go
memorySource := memory.NewSource(
memory.WithData(data),
memory.WithJSON(data),
)
```
@@ -39,6 +39,6 @@ Load the source into config
// Create new config
conf := config.NewConfig()
// Load file source
// Load memory source
conf.Load(memorySource)
```

View File

@@ -18,6 +18,7 @@ type memory struct {
func (s *memory) Read() (*source.ChangeSet, error) {
s.RLock()
cs := &source.ChangeSet{
Format: s.ChangeSet.Format,
Timestamp: s.ChangeSet.Timestamp,
Data: s.ChangeSet.Data,
Checksum: s.ChangeSet.Checksum,

View File

@@ -2,9 +2,15 @@
package source
import (
"errors"
"time"
)
var (
// ErrWatcherStopped is returned when source watcher has been stopped
ErrWatcherStopped = errors.New("watcher stopped")
)
// Source is the source from which config is loaded
type Source interface {
Read() (*ChangeSet, error)

View File

@@ -1,96 +0,0 @@
// Package consul is a consul implementation of kv
package consul
import (
"fmt"
"net"
"github.com/hashicorp/consul/api"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/data/store"
)
type ckv struct {
options.Options
client *api.Client
}
func (c *ckv) Read(key string) (*store.Record, error) {
keyval, _, err := c.client.KV().Get(key, nil)
if err != nil {
return nil, err
}
if keyval == nil {
return nil, store.ErrNotFound
}
return &store.Record{
Key: keyval.Key,
Value: keyval.Value,
}, nil
}
func (c *ckv) Delete(key string) error {
_, err := c.client.KV().Delete(key, nil)
return err
}
func (c *ckv) Write(record *store.Record) error {
_, err := c.client.KV().Put(&api.KVPair{
Key: record.Key,
Value: record.Value,
}, nil)
return err
}
func (c *ckv) Dump() ([]*store.Record, error) {
keyval, _, err := c.client.KV().List("/", nil)
if err != nil {
return nil, err
}
if keyval == nil {
return nil, store.ErrNotFound
}
var vals []*store.Record
for _, keyv := range keyval {
vals = append(vals, &store.Record{
Key: keyv.Key,
Value: keyv.Value,
})
}
return vals, nil
}
func (c *ckv) String() string {
return "consul"
}
func NewStore(opts ...options.Option) store.Store {
options := options.NewOptions(opts...)
config := api.DefaultConfig()
var nodes []string
if n, ok := options.Values().Get("store.nodes"); ok {
nodes = n.([]string)
}
// set host
if len(nodes) > 0 {
addr, port, err := net.SplitHostPort(nodes[0])
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
port = "8500"
config.Address = fmt.Sprintf("%s:%s", nodes[0], port)
} else if err == nil {
config.Address = fmt.Sprintf("%s:%s", addr, port)
}
}
client, _ := api.NewClient(config)
return &ckv{
Options: options,
client: client,
}
}

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

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

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

@@ -0,0 +1,108 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/debug/proto/debug.proto
package debug
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
client "github.com/micro/go-micro/client"
server "github.com/micro/go-micro/server"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ client.Option
var _ server.Option
// Client API for Debug service
type DebugService interface {
Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error)
Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error)
}
type debugService struct {
c client.Client
name string
}
func NewDebugService(name string, c client.Client) DebugService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "debug"
}
return &debugService{
c: c,
name: name,
}
}
func (c *debugService) Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error) {
req := c.c.NewRequest(c.name, "Debug.Health", in)
out := new(HealthResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *debugService) Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error) {
req := c.c.NewRequest(c.name, "Debug.Stats", in)
out := new(StatsResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Debug service
type DebugHandler interface {
Health(context.Context, *HealthRequest, *HealthResponse) error
Stats(context.Context, *StatsRequest, *StatsResponse) error
}
func RegisterDebugHandler(s server.Server, hdlr DebugHandler, opts ...server.HandlerOption) error {
type debug interface {
Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error
Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error
}
type Debug struct {
debug
}
h := &debugHandler{hdlr}
return s.Handle(s.NewHandler(&Debug{h}, opts...))
}
type debugHandler struct {
DebugHandler
}
func (h *debugHandler) Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error {
return h.DebugHandler.Health(ctx, in, out)
}
func (h *debugHandler) Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error {
return h.DebugHandler.Stats(ctx, in, out)
}

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

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

View File

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

View File

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

95
go.mod
View File

@@ -1,84 +1,63 @@
module github.com/micro/go-micro
go 1.12
go 1.13
require (
cloud.google.com/go v0.41.0 // indirect
github.com/BurntSushi/toml v0.3.1
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/beevik/ntp v0.2.0
github.com/bitly/go-simplejson v0.5.0
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/bwmarrin/discordgo v0.19.0
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc // indirect
github.com/cloudflare/cloudflare-go v0.10.4
github.com/coreos/bbolt v1.3.3 // indirect
github.com/coreos/etcd v3.3.17+incompatible
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c
github.com/fsnotify/fsnotify v1.4.7
github.com/fsouza/go-dockerclient v1.4.1
github.com/fsouza/go-dockerclient v1.4.4
github.com/ghodss/yaml v1.0.0
github.com/gliderlabs/ssh v0.2.2 // indirect
github.com/go-kit/kit v0.9.0 // indirect
github.com/go-acme/lego/v3 v3.1.0
github.com/go-log/log v0.1.0
github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc // indirect
github.com/golang/protobuf v1.3.2
github.com/google/uuid v1.1.1
github.com/gorilla/handlers v1.4.1
github.com/gorilla/mux v1.7.3 // indirect
github.com/gorilla/websocket v1.4.0
github.com/hashicorp/consul/api v1.1.0
github.com/hashicorp/go-immutable-radix v1.1.0 // indirect
github.com/hashicorp/go-msgpack v0.5.5 // indirect
github.com/hashicorp/go-retryablehttp v0.5.4 // indirect
github.com/hashicorp/go-rootcerts v1.0.1 // indirect
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/go-version v1.2.0 // indirect
github.com/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/hashicorp/mdns v1.0.1 // indirect
github.com/hashicorp/memberlist v0.1.4
github.com/hashicorp/serf v0.8.3 // indirect
github.com/imdario/mergo v0.3.7
github.com/imdario/mergo v0.3.8
github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1
github.com/json-iterator/go v1.1.6
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c // indirect
github.com/kisielk/errcheck v1.2.0 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/kr/pty v1.1.8 // indirect
github.com/json-iterator/go v1.1.7
github.com/leodido/go-urn v1.1.0 // indirect
github.com/lucas-clemente/quic-go v0.7.1-0.20190710050138-1441923ab031
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/lucas-clemente/quic-go v0.12.1
github.com/mholt/certmagic v0.7.5
github.com/micro/cli v0.2.0
github.com/micro/mdns v0.1.1-0.20190729112526-ef68c9635478
github.com/miekg/dns v1.1.15 // indirect
github.com/mitchellh/gox v1.0.1 // indirect
github.com/micro/mdns v0.3.0
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/nkeys v0.1.0 // indirect
github.com/nlopes/slack v0.5.0
github.com/olekukonko/tablewriter v0.0.1
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/nlopes/slack v0.6.0
github.com/pkg/errors v0.8.1
github.com/posener/complete v1.2.1 // indirect
github.com/prometheus/common v0.6.0 // indirect
github.com/prometheus/procfs v0.0.3 // indirect
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/stretchr/objx v0.2.0 // indirect
github.com/soheilhy/cmux v0.1.4 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f // indirect
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9 // indirect
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9 // indirect
golang.org/x/mod v0.1.0 // indirect
golang.org/x/net v0.0.0-20190628185345-da137c7871d7
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 // indirect
golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8 // indirect
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 // indirect
google.golang.org/grpc v1.22.0
gopkg.in/go-playground/validator.v9 v9.29.0
gopkg.in/src-d/go-billy.v4 v4.3.1 // indirect
gopkg.in/src-d/go-git.v4 v4.12.0
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
go.etcd.io/bbolt v1.3.3 // indirect
go.uber.org/multierr v1.2.0 // indirect
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
golang.org/x/net v0.0.0-20191011234655-491137f69257
google.golang.org/grpc v1.24.0
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v9 v9.30.0
gopkg.in/src-d/go-git.v4 v4.13.1
gopkg.in/telegram-bot-api.v4 v4.6.4
honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b // indirect
sigs.k8s.io/yaml v1.1.0 // indirect
)

471
go.sum
View File

@@ -1,73 +1,130 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw=
github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM=
github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/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/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM=
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY=
github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.10.2 h1:VBodKICVPnwmDxstcW3biKcDSpFIfS/RELUXsZSBYK4=
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
github.com/cloudflare/cloudflare-go v0.10.4 h1:7C1D9mtcNFZLCqmhkHK2BlwKKm9fi4cBqY6qpYtQv5E=
github.com/cloudflare/cloudflare-go v0.10.4/go.mod h1:4HgmUutVbZTRnHg91bS8lvlA0Wx+TgqttLDcwey2S6E=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.17+incompatible h1:f/Z3EoDSx1yjaIjLQGo1diYUlQYSBrrAQ5vP8NjwXwo=
github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 h1:dmUn0SuGx7unKFwxyeQ/oLUHhEfZosEDrpmYM+6MTuc=
github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/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/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c h1:pBgVXWDXju1m8W4lnEeIqTHPOzhTUO81a7yknM/xQR4=
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsouza/go-dockerclient v1.4.1 h1:W7wuJ3IB48WYZv/UBk9dCTIb9oX805+L9KIm65HcUYs=
github.com/fsouza/go-dockerclient v1.4.1/go.mod h1:PUNHxbowDqRXfRgZqMz1OeGtbWC6VKyZvJ99hDjB0qs=
github.com/fsouza/go-dockerclient v1.4.4 h1:Sd5nD4wdAgiPxvrbYUzT2ZZNmPk3z+GGnZ+frvw8z04=
github.com/fsouza/go-dockerclient v1.4.4/go.mod h1:PrwszSL5fbmsESocROrOGq/NULMXRw+bajY0ltzD6MA=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-acme/lego/v3 v3.1.0 h1:yanYFoYW8azFkCvJfIk7edWWfjkYkhDxe45ZsxoW4Xk=
github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE=
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-log/log v0.1.0 h1:wudGTNsiGzrD5ZjgIkVZ517ugi2XRe9Q/xRCzwEO4/U=
github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -77,173 +134,187 @@ github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3yg
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc h1:55rEp52jU6bkyslZ1+C/7NGfpQsEc6pxGLAGDOctqbw=
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA=
github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/handlers v1.4.1 h1:BHvcRGJe/TrL+OqFxoKQGddTgeibiOjaBssV5a/N9sw=
github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.2.0 h1:oPsuzLp2uk7I7rojPKuncWbZ+m5TMoD4Ivs+2Rkeh4Y=
github.com/hashicorp/consul/api v1.2.0/go.mod h1:1SIkFYi2ZTXUE5Kgt179+4hH33djo11+0Eo2XgTAtkw=
github.com/hashicorp/consul/sdk v0.2.0 h1:GWFYFmry/k4b1hEoy7kSkmU8e30GAyI4VZHk0fRxeL4=
github.com/hashicorp/consul/sdk v0.2.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc=
github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/memberlist v0.1.4 h1:gkyML/r71w3FL8gUi74Vk76avkj/9lYAY9lvg0OcoGs=
github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.8.3 h1:MWYcmct5EtKz0efYooPcL0yNkem+7kWxqXDi/UIh+8k=
github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:anPrsicrIi2ColgWTVPk+TrN42hJIWlfPHSBP9S0ZkM=
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84=
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1 h1:lnrOS18wZBYrzdDmnUeg1OVk+kQ3rxG8mZWU89DpMIA=
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1/go.mod h1:DFXrEwSRX0p/aSvxE21319menCBFeQO0jXpRj7LEZUA=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c h1:VAx3LRNjVNvjtgO7KFRuT/3aye/0zJvwn01rHSfoolo=
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/lucas-clemente/quic-go v0.7.1-0.20190710050138-1441923ab031 h1:wjcGvgllMOQw8wNYFH6acq/KlTAdjKMSo1EUYybHXto=
github.com/lucas-clemente/quic-go v0.7.1-0.20190710050138-1441923ab031/go.mod h1:lb5aAxL68VvhZ00e7yYuQVK/9FLggtYy4qo7oI5qzqA=
github.com/lucas-clemente/quic-go v0.11.2 h1:Mop0ac3zALaBR3wGs6j8OYe/tcFvFsxTUFMkE/7yUOI=
github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA=
github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ=
github.com/lucas-clemente/quic-go v0.12.1 h1:BPITli+6KnKogtTxBk2aS4okr5dUHz2LtIDAP1b8UL4=
github.com/lucas-clemente/quic-go v0.12.1/go.mod h1:UXJJPE4RfFef/xPO5wQm0tITK8gNfqwTxjbE7s3Vb8s=
github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/marten-seemann/qtls v0.2.4 h1:mCJ6i1jAqcsm9XODrSGvXECodoAb1STta+TkxJCwCnE=
github.com/marten-seemann/qtls v0.2.4/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/marten-seemann/qtls v0.3.1 h1:ySYIvhFjFY2JsNHY6VACvomMEDy3EvdPA6yciUFAiHw=
github.com/marten-seemann/qtls v0.3.1/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/marten-seemann/qtls v0.3.2 h1:O7awy4bHEzSX/K3h+fZig3/Vo03s/RxlxgsAk9sYamI=
github.com/marten-seemann/qtls v0.3.2/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/certmagic v0.7.5 h1:1ZGHwUI4+zg1S17tPUj5Xxb9Q1ghTjLcUZE5G4yV5SM=
github.com/mholt/certmagic v0.7.5/go.mod h1:91uJzK5K8IWtYQqTi5R2tsxV1pCde+wdGfaRaOZi6aQ=
github.com/micro/cli v0.2.0 h1:ut3rV5JWqZjsXIa2MvGF+qMUP8DAUTvHX9Br5gO4afA=
github.com/micro/cli v0.2.0/go.mod h1:jRT9gmfVKWSS6pkKcXQ8YhUyj6bzwxK8Fp5b0Y7qNnk=
github.com/micro/mdns v0.1.0 h1:fuLybUsfynbigJmCot/54i+gwe0hpc/vtCMvWt2WfDI=
github.com/micro/mdns v0.1.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
github.com/micro/mdns v0.1.1-0.20190729112526-ef68c9635478 h1:L6jnZZ763dMLlvst8P0dWHa1WbUu7ppUY1q3AY2hhIU=
github.com/micro/mdns v0.1.1-0.20190729112526-ef68c9635478/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
github.com/micro/mdns v0.3.0 h1:bYycYe+98AXR3s8Nq5qvt6C573uFTDPIYzJemWON0QE=
github.com/micro/mdns v0.3.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM=
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.14 h1:wkQWn9wIp4mZbwW8XV6Km6owkvRPbOiV004ZM2CkGvA=
github.com/miekg/dns v1.1.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI=
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
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=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
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=
@@ -252,55 +323,77 @@ github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nlopes/slack v0.5.0 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0=
github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/nlopes/slack v0.6.0 h1:jt0jxVQGhssx1Ib7naAOZEZcGdtIhTzkP0nopK0AsRA=
github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk=
github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw=
github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ=
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -308,71 +401,82 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY=
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4=
go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 h1:IcSOAf4PyMp3U3XbIEj1/xJ2BjNN2jWv7JoyOsMxXUU=
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190618124811-92942e4437e2/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190607181551-461777fb6f67 h1:rJJxsykSlULwd2P2+pg/rtnwN2FrWp4IuCxOSyS0V00=
golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191011234655-491137f69257 h1:ry8e2D+cwaV6hk7lb3aRTjjZo24shrbK0e11QEOkTIg=
golang.org/x/net v0.0.0-20191011234655-491137f69257/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -380,113 +484,110 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190621062556-bf70e4678053 h1:T0MJjz97TtCXa3ZNW2Oenb3KQWB91K965zMEbIJ4ThA=
golang.org/x/sys v0.0.0-20190621062556-bf70e4678053/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190530171427-2b03ca6e44eb/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190620191750-1fa568393b23/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190710184609-286818132824/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 h1:9VBRTdmgQxbs6HE0sUnMrSWNePppAJU07NYvX5dIB04=
google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 h1:5pOB7se0B2+IssELuQUs6uoBgYJenkU2AQlvopc2sRw=
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/validator.v9 v9.29.0 h1:5ofssLNYgAA/inWn6rTZ4juWpRJUwEnXc1LG2IeXwgQ=
gopkg.in/go-playground/validator.v9 v9.29.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo=
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
gopkg.in/src-d/go-billy.v4 v4.3.1 h1:OkK1DmefDy1Z6Veu82wdNj/cLpYORhdX4qdaYCPwc7s=
gopkg.in/src-d/go-billy.v4 v4.3.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.30.0 h1:Wk0Z37oBmKj9/n+tPyBHZmeL19LaCoK3Qq48VwYENss=
gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw=
gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.11.0 h1:cJwWgJ0DXifrNrXM6RGN1Y2yR60Rr1zQ9Q5DX5S9qgU=
gopkg.in/src-d/go-git.v4 v4.11.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
gopkg.in/src-d/go-git.v4 v4.12.0 h1:CKgvBCJCcdfNnyXPYI4Cp8PaDDAmAPEN0CtfEdEAbd8=
gopkg.in/src-d/go-git.v4 v4.12.0/go.mod h1:zjlNnzc1Wjn43v3Mtii7RVxiReNP0fIu9npcXKzuNp4=
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
gopkg.in/telegram-bot-api.v4 v4.6.4 h1:hpHWhzn4jTCsAJZZ2loNKfy2QWyPDRJVl3aTFXeMW8g=
gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b/go.mod h1:JlmFZigtG9vBVR3QGIQ9g/Usz4BzH+Xm6Z8iHQWRYUw=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -14,11 +14,19 @@ type serviceKey struct{}
// within go-micro. Its a convenience method for building
// and initialising services.
type Service interface {
// The service name
Name() string
// Init initialises options
Init(...Option)
// Options returns the current options
Options() Options
// Client is used to call services
Client() client.Client
// Server is for handling requests and events
Server() server.Server
// Run the service
Run() error
// The service implementation
String() string
}

338
monitor/default.go Normal file
View File

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

37
monitor/default_test.go Normal file
View File

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

45
monitor/monitor.go Normal file
View File

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

25
monitor/options.go Normal file
View File

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

989
network/default.go Normal file
View File

@@ -0,0 +1,989 @@
package network
import (
"errors"
"fmt"
"hash/fnv"
"sync"
"time"
"github.com/golang/protobuf/proto"
"github.com/micro/go-micro/client"
rtr "github.com/micro/go-micro/client/selector/router"
pbNet "github.com/micro/go-micro/network/proto"
"github.com/micro/go-micro/network/resolver/dns"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/router"
pbRtr "github.com/micro/go-micro/router/proto"
"github.com/micro/go-micro/server"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/tunnel"
tun "github.com/micro/go-micro/tunnel/transport"
"github.com/micro/go-micro/util/backoff"
"github.com/micro/go-micro/util/log"
)
var (
// NetworkChannel is the name of the tunnel channel for passing network messages
NetworkChannel = "network"
// ControlChannel is the name of the tunnel channel for passing control message
ControlChannel = "control"
// DefaultLink is default network link
DefaultLink = "network"
// MaxConnections is the max number of network client connections
MaxConnections = 3
)
var (
// ErrClientNotFound is returned when client for tunnel channel could not be found
ErrClientNotFound = errors.New("client not found")
)
// network implements Network interface
type network struct {
// node is network node
*node
// options configure the network
options Options
// rtr is network router
router router.Router
// prx is network proxy
proxy proxy.Proxy
// tun is network tunnel
tunnel tunnel.Tunnel
// server is network server
server server.Server
// client is network client
client client.Client
// tunClient is a map of tunnel clients keyed over tunnel channel names
tunClient map[string]transport.Client
sync.RWMutex
// connected marks the network as connected
connected bool
// closed closes the network
closed chan bool
}
// newNetwork returns a new network node
func newNetwork(opts ...Option) Network {
options := DefaultOptions()
for _, o := range opts {
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))
address := fmt.Sprintf("%d", hasher.Sum64())
// set the address to advertise
var advertise string
var peerAddress string
if len(options.Advertise) > 0 {
advertise = options.Advertise
peerAddress = options.Advertise
} else {
advertise = options.Address
peerAddress = address
}
// server is network server
server := server.NewServer(
server.Id(options.Id),
server.Address(peerAddress),
server.Advertise(advertise),
server.Name(options.Name),
server.Transport(tunTransport),
)
// client is network client
client := client.NewClient(
client.Transport(tunTransport),
client.Selector(
rtr.NewSelector(
rtr.WithRouter(options.Router),
),
),
)
network := &network{
node: &node{
id: options.Id,
address: peerAddress,
peers: make(map[string]*node),
},
options: options,
router: options.Router,
proxy: options.Proxy,
tunnel: options.Tunnel,
server: server,
client: client,
tunClient: make(map[string]transport.Client),
}
network.node.network = network
return network
}
func (n *network) Init(opts ...Option) error {
n.Lock()
defer n.Unlock()
// TODO: maybe only allow reinit of certain opts
for _, o := range opts {
o(&n.options)
}
return nil
}
// Options returns network options
func (n *network) Options() Options {
n.RLock()
defer n.RUnlock()
options := n.options
return options
}
// Name returns network name
func (n *network) Name() string {
return n.options.Name
}
// resolveNodes resolves network nodes to addresses
func (n *network) resolveNodes() ([]string, error) {
// resolve the network address to network nodes
records, err := n.options.Resolver.Resolve(n.options.Name)
nodeMap := make(map[string]bool)
// collect network node addresses
var nodes []string
var i int
for _, record := range records {
if _, ok := nodeMap[record.Address]; ok {
continue
}
nodeMap[record.Address] = true
nodes = append(nodes, record.Address)
i++
// break once MaxConnection nodes has been reached
if i == MaxConnections {
break
}
}
// use the dns resolver to expand peers
dns := &dns.Resolver{}
// append seed nodes if we have them
for _, node := range n.options.Peers {
// resolve anything that looks like a host name
records, err := dns.Resolve(node)
if err != nil {
log.Debugf("Failed to resolve %v %v", node, err)
continue
}
// add to the node map
for _, record := range records {
if _, ok := nodeMap[record.Address]; !ok {
nodes = append(nodes, record.Address)
}
}
}
return nodes, err
}
// resolve continuously resolves network nodes and initializes network tunnel with resolved addresses
func (n *network) resolve() {
resolve := time.NewTicker(ResolveTime)
defer resolve.Stop()
for {
select {
case <-n.closed:
return
case <-resolve.C:
nodes, err := n.resolveNodes()
if err != nil {
log.Debugf("Network failed to resolve nodes: %v", err)
continue
}
// initialize the tunnel
n.tunnel.Init(
tunnel.Nodes(nodes...),
)
}
}
}
// handleNetConn handles network announcement messages
func (n *network) handleNetConn(sess tunnel.Session, msg chan *transport.Message) {
for {
m := new(transport.Message)
if err := sess.Recv(m); err != nil {
log.Debugf("Network tunnel [%s] receive error: %v", NetworkChannel, err)
if sessErr := sess.Close(); sessErr != nil {
log.Debugf("Network tunnel [%s] closing connection error: %v", sessErr)
}
return
}
select {
case msg <- m:
case <-n.closed:
return
}
}
}
// acceptNetConn accepts connections from NetworkChannel
func (n *network) acceptNetConn(l tunnel.Listener, recv chan *transport.Message) {
var i int
for {
// accept a connection
conn, err := l.Accept()
if err != nil {
sleep := backoff.Do(i)
log.Debugf("Network tunnel [%s] accept error: %v, backing off for %v", ControlChannel, err, sleep)
time.Sleep(sleep)
if i > 5 {
i = 0
}
i++
continue
}
select {
case <-n.closed:
if err := conn.Close(); err != nil {
log.Debugf("Network tunnel [%s] failed to close connection: %v", NetworkChannel, err)
}
return
default:
// go handle NetworkChannel connection
go n.handleNetConn(conn, recv)
}
}
}
// processNetChan processes messages received on NetworkChannel
func (n *network) processNetChan(listener tunnel.Listener) {
// receive network message queue
recv := make(chan *transport.Message, 128)
// accept NetworkChannel connections
go n.acceptNetConn(listener, recv)
for {
select {
case m := <-recv:
// switch on type of message and take action
switch m.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 {
log.Debugf("Network tunnel [%s] connect unmarshal error: %v", NetworkChannel, err)
continue
}
// don't process your own messages
if pbNetConnect.Node.Id == n.options.Id {
continue
}
log.Debugf("Network received connect message from: %s", pbNetConnect.Node.Id)
peer := &node{
id: pbNetConnect.Node.Id,
address: pbNetConnect.Node.Address,
peers: make(map[string]*node),
lastSeen: now,
}
if err := n.node.AddPeer(peer); err == ErrPeerExists {
log.Debugf("Network peer exists, refreshing: %s", peer.id)
// update lastSeen time for the existing node
if err := n.RefreshPeer(peer.id, now); err != nil {
log.Debugf("Network failed refreshing peer %s: %v", peer.id, err)
}
continue
}
// get node peers down to MaxDepth encoded in protobuf
msg := PeersToProto(n.node, MaxDepth)
// advertise yourself to the network
if err := n.sendMsg("peer", msg, NetworkChannel); err != nil {
log.Debugf("Network failed to advertise peers: %v", err)
}
// advertise all the routes when a new node has connected
if err := n.router.Solicit(); err != nil {
log.Debugf("Network failed to solicit routes: %s", err)
}
case "peer":
// mark the time the message has been received
now := time.Now()
pbNetPeer := &pbNet.Peer{}
if err := proto.Unmarshal(m.Body, pbNetPeer); err != nil {
log.Debugf("Network tunnel [%s] peer unmarshal error: %v", NetworkChannel, err)
continue
}
// don't process your own messages
if pbNetPeer.Node.Id == n.options.Id {
continue
}
log.Debugf("Network received peer message from: %s %s", pbNetPeer.Node.Id, pbNetPeer.Node.Address)
peer := &node{
id: pbNetPeer.Node.Id,
address: pbNetPeer.Node.Address,
peers: make(map[string]*node),
lastSeen: now,
}
if err := n.node.AddPeer(peer); err == nil {
// send a solicit message when discovering new peer
msg := &pbRtr.Solicit{
Id: n.options.Id,
}
if err := n.sendMsg("solicit", msg, ControlChannel); err != nil {
log.Debugf("Network failed to send solicit message: %s", err)
}
continue
// we're expecting any error to be ErrPeerExists
} else if err != ErrPeerExists {
log.Debugf("Network got error adding peer %v", err)
continue
}
log.Debugf("Network peer exists, refreshing: %s", pbNetPeer.Node.Id)
// update lastSeen time for the peer
if err := n.RefreshPeer(pbNetPeer.Node.Id, now); err != nil {
log.Debugf("Network failed refreshing peer %s: %v", pbNetPeer.Node.Id, err)
}
// NOTE: we don't unpack MaxDepth toplogy
peer = UnpackPeerTopology(pbNetPeer, now, MaxDepth-1)
log.Debugf("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 {
log.Debugf("Network tunnel [%s] close unmarshal error: %v", NetworkChannel, err)
continue
}
// don't process your own messages
if pbNetClose.Node.Id == n.options.Id {
continue
}
log.Debugf("Network received close message from: %s", pbNetClose.Node.Id)
peer := &node{
id: pbNetClose.Node.Id,
address: pbNetClose.Node.Address,
}
if err := n.DeletePeerNode(peer.id); err != nil {
log.Debugf("Network failed to delete node %s routes: %v", peer.id, err)
}
if err := n.prunePeerRoutes(peer); err != nil {
log.Debugf("Network failed pruning peer %s routes: %v", peer.id, err)
}
}
case <-n.closed:
return
}
}
}
// sendMsg sends a message to the tunnel channel
func (n *network) sendMsg(method string, msg proto.Message, channel string) error {
body, err := proto.Marshal(msg)
if err != nil {
return err
}
// create transport message and chuck it down the pipe
m := transport.Message{
Header: map[string]string{
"Micro-Method": method,
},
Body: body,
}
// check if the channel client is initialized
n.RLock()
client, ok := n.tunClient[channel]
if !ok || client == nil {
n.RUnlock()
return ErrClientNotFound
}
n.RUnlock()
log.Debugf("Network sending %s message from: %s", method, n.options.Id)
if err := client.Send(&m); err != nil {
return err
}
return nil
}
// announce announces node peers to the network
func (n *network) announce(client transport.Client) {
announce := time.NewTicker(AnnounceTime)
defer announce.Stop()
for {
select {
case <-n.closed:
return
case <-announce.C:
msg := PeersToProto(n.node, MaxDepth)
// advertise yourself to the network
if err := n.sendMsg("peer", msg, NetworkChannel); err != nil {
log.Debugf("Network failed to advertise peers: %v", err)
continue
}
}
}
}
// pruneRoutes prunes routes return by given query
func (n *network) pruneRoutes(q ...router.QueryOption) error {
routes, err := n.router.Table().Query(q...)
if err != nil && err != router.ErrRouteNotFound {
return err
}
for _, route := range routes {
if err := n.router.Table().Delete(route); err != nil && err != router.ErrRouteNotFound {
return err
}
}
return nil
}
// pruneNodeRoutes prunes routes that were either originated by or routable via given node
func (n *network) prunePeerRoutes(peer *node) error {
// lookup all routes originated by router
q := []router.QueryOption{
router.QueryRouter(peer.id),
}
if err := n.pruneRoutes(q...); err != nil {
return err
}
// lookup all routes routable via gw
q = []router.QueryOption{
router.QueryGateway(peer.address),
}
if err := n.pruneRoutes(q...); err != nil {
return err
}
return nil
}
// prune deltes node peers that have not been seen for longer than PruneTime seconds
// prune also removes all the routes either originated by or routable by the stale nodes
func (n *network) prune() {
prune := time.NewTicker(PruneTime)
defer prune.Stop()
for {
select {
case <-n.closed:
return
case <-prune.C:
pruned := n.PruneStalePeerNodes(PruneTime)
for id, peer := range pruned {
log.Debugf("Network peer exceeded prune time: %s", id)
if err := n.prunePeerRoutes(peer); err != nil {
log.Debugf("Network failed pruning peer %s routes: %v", id, err)
}
}
// get a list of all routes
routes, err := n.options.Router.Table().List()
if err != nil {
log.Debugf("Network failed listing routes: %v", err)
continue
}
// collect all the router IDs in the routing table
routers := make(map[string]bool)
for _, route := range routes {
if _, ok := routers[route.Router]; !ok {
routers[route.Router] = true
// if the router is NOT in our peer graph, delete all routes originated by it
if peerNode := n.node.GetPeerNode(route.Router); peerNode == nil {
if err := n.pruneRoutes(router.QueryRouter(route.Router)); err != nil {
log.Debugf("Network failed deleting routes by %s: %v", route.Router, err)
}
}
}
}
}
}
}
// handleCtrlConn handles ControlChannel connections
func (n *network) handleCtrlConn(sess tunnel.Session, msg chan *transport.Message) {
for {
m := new(transport.Message)
if err := sess.Recv(m); err != nil {
log.Debugf("Network tunnel advert receive error: %v", err)
return
}
select {
case msg <- m:
case <-n.closed:
return
}
}
}
// acceptCtrlConn accepts connections from ControlChannel
func (n *network) acceptCtrlConn(l tunnel.Listener, recv chan *transport.Message) {
var i int
for {
// accept a connection
conn, err := l.Accept()
if err != nil {
sleep := backoff.Do(i)
log.Debugf("Network tunnel [%s] accept error: %v, backing off for %v", ControlChannel, err, sleep)
time.Sleep(sleep)
if i > 5 {
// reset the counter
i = 0
}
i++
continue
}
select {
case <-n.closed:
if err := conn.Close(); err != nil {
log.Debugf("Network tunnel [%s] failed to close connection: %v", ControlChannel, err)
}
return
default:
// go handle ControlChannel connection
go n.handleCtrlConn(conn, recv)
}
}
}
// 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) {
// we are the origin of the route
if route.Router == n.options.Id {
route.Metric = 1
return
}
// check if the route origin is our peer
if _, ok := n.peers[route.Router]; ok {
route.Metric = 10
return
}
// check if 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
}
}
}
// the origin of the route is beyond our neighbourhood
route.Metric = 1000
}
// processCtrlChan processes messages received on ControlChannel
func (n *network) processCtrlChan(listener tunnel.Listener) {
// receive control message queue
recv := make(chan *transport.Message, 128)
// accept ControlChannel cconnections
go n.acceptCtrlConn(listener, recv)
for {
select {
case m := <-recv:
// switch on type of message and take action
switch m.Header["Micro-Method"] {
case "advert":
pbRtrAdvert := &pbRtr.Advert{}
if err := proto.Unmarshal(m.Body, pbRtrAdvert); err != nil {
log.Debugf("Network fail to unmarshal advert message: %v", err)
continue
}
// don't process your own messages
if pbRtrAdvert.Id == n.options.Id {
continue
}
log.Debugf("Network received advert message from: %s", pbRtrAdvert.Id)
// loookup advertising node in our peer topology
advertNode := n.node.GetPeerNode(pbRtrAdvert.Id)
if advertNode == nil {
// if we can't find the node in our topology (MaxDepth) we skipp prcessing adverts
log.Debugf("Network skipping advert message from unknown peer: %s", pbRtrAdvert.Id)
continue
}
var events []*router.Event
for _, event := range pbRtrAdvert.Events {
// we know the advertising node is not the origin of the route
if pbRtrAdvert.Id != event.Route.Router {
// if the origin router is not the advertising node peer
// we can't rule out potential routing loops so we bail here
if peer := advertNode.GetPeerNode(event.Route.Router); peer == nil {
log.Debugf("Network skipping advert message from peer: %s", pbRtrAdvert.Id)
continue
}
}
route := router.Route{
Service: event.Route.Service,
Address: event.Route.Address,
Gateway: event.Route.Gateway,
Network: event.Route.Network,
Router: event.Route.Router,
Link: event.Route.Link,
Metric: int(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
}
// create router event
e := &router.Event{
Type: router.EventType(event.Type),
Timestamp: time.Unix(0, pbRtrAdvert.Timestamp),
Route: route,
}
events = append(events, e)
}
// 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)
continue
}
// create an advert and process it
advert := &router.Advert{
Id: pbRtrAdvert.Id,
Type: router.AdvertType(pbRtrAdvert.Type),
Timestamp: time.Unix(0, pbRtrAdvert.Timestamp),
TTL: time.Duration(pbRtrAdvert.Ttl),
Events: events,
}
log.Debugf("Network router %s processing advert: %s", n.Id(), advert.Id)
if err := n.router.Process(advert); err != nil {
log.Debugf("Network failed to process advert %s: %v", advert.Id, err)
}
case "solicit":
pbRtrSolicit := &pbRtr.Solicit{}
if err := proto.Unmarshal(m.Body, pbRtrSolicit); err != nil {
log.Debugf("Network fail to unmarshal solicit message: %v", err)
continue
}
log.Debugf("Network received solicit message from: %s", pbRtrSolicit.Id)
// ignore solicitation when requested by you
if pbRtrSolicit.Id == n.options.Id {
continue
}
log.Debugf("Network router flushing routes for: %s", pbRtrSolicit.Id)
// advertise all the routes when a new node has connected
if err := n.router.Solicit(); err != nil {
log.Debugf("Network failed to solicit routes: %s", err)
}
}
case <-n.closed:
return
}
}
}
// advertise advertises routes to the network
func (n *network) advertise(advertChan <-chan *router.Advert) {
hasher := fnv.New64()
for {
select {
// process local adverts and randomly fire them at other nodes
case advert := <-advertChan:
// create a proto advert
var events []*pbRtr.Event
for _, event := range advert.Events {
// the routes service address
address := event.Route.Address
// only hash the address if we're advertising our own local routes
if event.Route.Router == advert.Id {
// hash the service before advertising it
hasher.Reset()
hasher.Write([]byte(event.Route.Address + n.node.id))
address = fmt.Sprintf("%d", hasher.Sum64())
}
// NOTE: we override Gateway, Link and Address here
// TODO: should we avoid overriding gateway?
route := &pbRtr.Route{
Service: event.Route.Service,
Address: address,
Gateway: n.node.Address(),
Network: event.Route.Network,
Router: event.Route.Router,
Link: DefaultLink,
Metric: int64(event.Route.Metric),
}
e := &pbRtr.Event{
Type: pbRtr.EventType(event.Type),
Timestamp: event.Timestamp.UnixNano(),
Route: route,
}
events = append(events, e)
}
msg := &pbRtr.Advert{
Id: advert.Id,
Type: pbRtr.AdvertType(advert.Type),
Timestamp: advert.Timestamp.UnixNano(),
Events: events,
}
if err := n.sendMsg("advert", msg, ControlChannel); err != nil {
log.Debugf("Network failed to advertise routes: %v", err)
continue
}
case <-n.closed:
return
}
}
}
func (n *network) sendConnect() {
// send connect message to NetworkChannel
// NOTE: in theory we could do this as soon as
// Dial to NetworkChannel succeeds, but instead
// we initialize all other node resources first
msg := &pbNet.Connect{
Node: &pbNet.Node{
Id: n.node.id,
Address: n.node.address,
},
}
if err := n.sendMsg("connect", msg, NetworkChannel); err != nil {
log.Debugf("Network failed to send connect message: %s", err)
}
}
// Connect connects the network
func (n *network) Connect() error {
n.Lock()
// try to resolve network nodes
nodes, err := n.resolveNodes()
if err != nil {
log.Debugf("Network failed to resolve nodes: %v", err)
}
// initialize the tunnel to resolved nodes
n.tunnel.Init(
tunnel.Nodes(nodes...),
)
// connect network tunnel
if err := n.tunnel.Connect(); err != nil {
n.Unlock()
return err
}
// return if already connected
if n.connected {
// unlock first
n.Unlock()
// send the connect message
n.sendConnect()
return nil
}
// set our internal node address
// if advertise address is not set
if len(n.options.Advertise) == 0 {
n.server.Init(server.Advertise(n.tunnel.Address()))
}
// dial into ControlChannel to send route adverts
ctrlClient, err := n.tunnel.Dial(ControlChannel, tunnel.DialMode(tunnel.Multicast))
if err != nil {
n.Unlock()
return err
}
n.tunClient[ControlChannel] = ctrlClient
// listen on ControlChannel
ctrlListener, err := n.tunnel.Listen(ControlChannel, tunnel.ListenMode(tunnel.Multicast))
if err != nil {
n.Unlock()
return err
}
// dial into NetworkChannel to send network messages
netClient, err := n.tunnel.Dial(NetworkChannel, tunnel.DialMode(tunnel.Multicast))
if err != nil {
n.Unlock()
return err
}
n.tunClient[NetworkChannel] = netClient
// listen on NetworkChannel
netListener, err := n.tunnel.Listen(NetworkChannel, tunnel.ListenMode(tunnel.Multicast))
if err != nil {
n.Unlock()
return err
}
// create closed channel
n.closed = make(chan bool)
// start the router
if err := n.options.Router.Start(); err != nil {
n.Unlock()
return err
}
// start advertising routes
advertChan, err := n.options.Router.Advertise()
if err != nil {
n.Unlock()
return err
}
// start the server
if err := n.server.Start(); err != nil {
n.Unlock()
return err
}
n.Unlock()
// send the connect message
n.sendConnect()
// go resolving network nodes
go n.resolve()
// broadcast peers
go n.announce(netClient)
// prune stale nodes
go n.prune()
// listen to network messages
go n.processNetChan(netListener)
// advertise service routes
go n.advertise(advertChan)
// accept and process routes
go n.processCtrlChan(ctrlListener)
n.Lock()
n.connected = true
n.Unlock()
return nil
}
func (n *network) close() error {
// stop the server
if err := n.server.Stop(); err != nil {
return err
}
// stop the router
if err := n.router.Stop(); err != nil {
return err
}
// close the tunnel
if err := n.tunnel.Close(); err != nil {
return err
}
return nil
}
// Close closes network connection
func (n *network) Close() error {
n.Lock()
if !n.connected {
n.Unlock()
return nil
}
select {
case <-n.closed:
n.Unlock()
return nil
default:
// TODO: send close message to the network channel
close(n.closed)
// set connected to false
n.connected = false
// unlock the lock otherwise we'll deadlock sending the close
n.Unlock()
msg := &pbNet.Close{
Node: &pbNet.Node{
Id: n.node.id,
Address: n.node.address,
},
}
if err := n.sendMsg("close", msg, NetworkChannel); err != nil {
log.Debugf("Network failed to send close message: %s", err)
}
}
return n.close()
}
// Client returns network client
func (n *network) Client() client.Client {
return n.client
}
// Server returns network server
func (n *network) Server() server.Server {
return n.server
}

View File

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

View File

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

60
network/network.go Normal file
View File

@@ -0,0 +1,60 @@
// Package network is for creating internetworks
package network
import (
"time"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/server"
)
var (
// DefaultName is default network name
DefaultName = "go.micro"
// DefaultAddress is default network address
DefaultAddress = ":0"
// ResolveTime defines time interval to periodically resolve network nodes
ResolveTime = 1 * time.Minute
// AnnounceTime defines time interval to periodically announce node neighbours
AnnounceTime = 30 * time.Second
// PruneTime defines time interval to periodically check nodes that need to be pruned
// due to their not announcing their presence within this time interval
PruneTime = 90 * time.Second
)
// Node is network node
type Node interface {
// Id is node id
Id() string
// Address is node bind address
Address() string
// Peers returns node peers
Peers() []Node
// Network is the network node is in
Network() Network
}
// Network is micro network
type Network interface {
// Node is network node
Node
// Initialise options
Init(...Option) error
// Options returns the network options
Options() Options
// Name of the network
Name() string
// Connect starts the resolver and tunnel server
Connect() error
// Close stops the tunnel and resolving
Close() error
// Client is micro client
Client() client.Client
// Server is micro server
Server() server.Server
}
// NewNetwork returns a new network interface
func NewNetwork(opts ...Option) Network {
return newNetwork(opts...)
}

380
network/node.go Normal file
View File

@@ -0,0 +1,380 @@
package network
import (
"container/list"
"errors"
"sync"
"time"
pb "github.com/micro/go-micro/network/proto"
)
var (
// MaxDepth defines max depth of peer topology
MaxDepth uint = 3
)
var (
// ErrPeerExists is returned when adding a peer which already exists
ErrPeerExists = errors.New("peer already exists")
// ErrPeerNotFound is returned when a peer could not be found in node topology
ErrPeerNotFound = errors.New("peer not found")
)
// node is network node
type node struct {
sync.RWMutex
// id is node id
id string
// address is node address
address string
// peers are nodes with direct link to this node
peers map[string]*node
// network returns the node network
network Network
// lastSeen keeps track of node lifetime and updates
lastSeen time.Time
}
// Id is node ide
func (n *node) Id() string {
return n.id
}
// Address returns node address
func (n *node) Address() string {
return n.address
}
// Network returns node network
func (n *node) Network() Network {
return n.network
}
// walk walks the node graph until some condition is met
func (n *node) walk(until func(peer *node) bool, action func(parent, peer *node)) map[string]*node {
// track the visited nodes
visited := make(map[string]*node)
// queue of the nodes to visit
queue := list.New()
// push node to the back of queue
queue.PushBack(n)
// mark the node as visited
visited[n.id] = n
// keep iterating over the queue until its empty
for queue.Len() > 0 {
// pop the node from the front of the queue
qnode := queue.Front()
if until(qnode.Value.(*node)) {
return visited
}
// iterate through all of the node peers
// mark the visited nodes; enqueue the non-visted
for id, peer := range qnode.Value.(*node).peers {
if _, ok := visited[id]; !ok {
visited[id] = peer
action(qnode.Value.(*node), peer)
queue.PushBack(peer)
}
}
// remove the node from the queue
queue.Remove(qnode)
}
return visited
}
// AddPeer adds a new peer to node topology
// It returns false if the peer already exists
func (n *node) AddPeer(peer *node) error {
n.Lock()
defer n.Unlock()
if _, ok := n.peers[peer.id]; !ok {
n.peers[peer.id] = peer
return nil
}
return ErrPeerExists
}
// DeletePeer deletes a peer from node peers
// It returns true if the peer has been deleted
func (n *node) DeletePeer(id string) bool {
n.Lock()
defer n.Unlock()
delete(n.peers, id)
return true
}
// UpdatePeer updates a peer if it already exists
// It returns error if the peer does not exist
func (n *node) UpdatePeer(peer *node) error {
n.Lock()
defer n.Unlock()
if _, ok := n.peers[peer.id]; ok {
n.peers[peer.id] = peer
return nil
}
return ErrPeerNotFound
}
// RefreshPeer updates node timestamp
// It returns false if the peer has not been found.
func (n *node) RefreshPeer(id string, now time.Time) error {
n.Lock()
defer n.Unlock()
peer, ok := n.peers[id]
if !ok {
return ErrPeerNotFound
}
if peer.lastSeen.Before(now) {
peer.lastSeen = now
}
return nil
}
// Nodes returns a slice of all nodes in the whole node topology
func (n *node) Nodes() []Node {
// we need to freeze the network graph here
// otherwise we might get inconsisten results
n.RLock()
defer n.RUnlock()
// NOTE: this should never be true
untilNoMorePeers := func(node *node) bool {
return node == nil
}
justWalk := func(parent, node *node) {}
visited := n.walk(untilNoMorePeers, justWalk)
var nodes []Node
// collect all the nodes and return them
for _, node := range visited {
nodes = append(nodes, node)
}
return nodes
}
// GetPeerNode returns a node from node MaxDepth topology
// It returns nil if the peer was not found
func (n *node) GetPeerNode(id string) *node {
// get node topology up to MaxDepth
top := n.Topology(MaxDepth)
untilFoundPeer := func(n *node) bool {
return n.id == id
}
justWalk := func(paent, node *node) {}
visited := top.walk(untilFoundPeer, justWalk)
peerNode, ok := visited[id]
if !ok {
return nil
}
return peerNode
}
// DeletePeerNode removes peer node from node topology
func (n *node) DeletePeerNode(id string) error {
n.Lock()
defer n.Unlock()
untilNoMorePeers := func(node *node) bool {
return node == nil
}
deleted := make(map[string]*node)
deletePeer := func(parent, node *node) {
if node.id != n.id && node.id == id {
delete(parent.peers, node.id)
deleted[node.id] = node
}
}
n.walk(untilNoMorePeers, deletePeer)
if _, ok := deleted[id]; !ok {
return ErrPeerNotFound
}
return nil
}
// PruneStalePeerNodes prune the peers that have not been seen for longer than given time
// It returns a map of the the nodes that got pruned
func (n *node) PruneStalePeerNodes(pruneTime time.Duration) map[string]*node {
n.Lock()
defer n.Unlock()
untilNoMorePeers := func(node *node) bool {
return node == nil
}
pruned := make(map[string]*node)
pruneStalePeer := func(parent, node *node) {
if node.id != n.id && time.Since(node.lastSeen) > PruneTime {
delete(parent.peers, node.id)
pruned[node.id] = node
}
}
n.walk(untilNoMorePeers, pruneStalePeer)
return pruned
}
// getTopology traverses node graph and builds node topology
// NOTE: this function is not thread safe
func (n *node) getTopology(depth uint) *node {
// make a copy of yourself
node := &node{
id: n.id,
address: n.address,
peers: make(map[string]*node),
network: n.network,
lastSeen: n.lastSeen,
}
// return if we reach requested depth or we have no more peers
if depth == 0 || len(n.peers) == 0 {
return node
}
// decrement the depth
depth--
// iterate through our peers and update the node peers
for _, peer := range n.peers {
nodePeer := peer.getTopology(depth)
if _, ok := node.peers[nodePeer.id]; !ok {
node.peers[nodePeer.id] = nodePeer
}
}
return node
}
// Topology returns a copy of the node topology down to given depth
// NOTE: the returned node is a node graph - not a single node
func (n *node) Topology(depth uint) *node {
n.RLock()
defer n.RUnlock()
return n.getTopology(depth)
}
// Peers returns node peers up to MaxDepth
func (n *node) Peers() []Node {
n.RLock()
defer n.RUnlock()
var peers []Node
for _, nodePeer := range n.peers {
peer := nodePeer.getTopology(MaxDepth)
peers = append(peers, peer)
}
return peers
}
// UnpackPeerTopology unpacks pb.Peer into node topology of given depth
func UnpackPeerTopology(pbPeer *pb.Peer, lastSeen time.Time, depth uint) *node {
peerNode := &node{
id: pbPeer.Node.Id,
address: pbPeer.Node.Address,
peers: make(map[string]*node),
lastSeen: lastSeen,
}
// return if have either reached the depth or have no more peers
if depth == 0 || len(pbPeer.Peers) == 0 {
return peerNode
}
// decrement the depth
depth--
peers := make(map[string]*node)
for _, pbPeer := range pbPeer.Peers {
peer := UnpackPeerTopology(pbPeer, lastSeen, depth)
peers[pbPeer.Node.Id] = peer
}
peerNode.peers = peers
return peerNode
}
func peerProtoTopology(peer Node, depth uint) *pb.Peer {
node := &pb.Node{
Id: peer.Id(),
Address: peer.Address(),
}
// set the network name if network is not nil
if peer.Network() != nil {
node.Network = peer.Network().Name()
}
pbPeers := &pb.Peer{
Node: node,
Peers: make([]*pb.Peer, 0),
}
// return if we reached the end of topology or depth
if depth == 0 || len(peer.Peers()) == 0 {
return pbPeers
}
// decrement the depth
depth--
// iterate through peers of peers aka pops
for _, pop := range peer.Peers() {
peer := peerProtoTopology(pop, depth)
pbPeers.Peers = append(pbPeers.Peers, peer)
}
return pbPeers
}
// PeersToProto returns node peers graph encoded into protobuf
func PeersToProto(node Node, depth uint) *pb.Peer {
// network node aka root node
pbNode := &pb.Node{
Id: node.Id(),
Address: node.Address(),
}
// set the network name if network is not nil
if node.Network() != nil {
pbNode.Network = node.Network().Name()
}
// we will build proto topology into this
pbPeers := &pb.Peer{
Node: pbNode,
Peers: make([]*pb.Peer, 0),
}
for _, peer := range node.Peers() {
pbPeer := peerProtoTopology(peer, depth)
pbPeers.Peers = append(pbPeers.Peers, pbPeer)
}
return pbPeers
}

319
network/node_test.go Normal file
View File

@@ -0,0 +1,319 @@
package network
import (
"testing"
"time"
pb "github.com/micro/go-micro/network/proto"
)
var (
testNodeId = "testNode"
testNodeAddress = "testAddress"
testNodeNetName = "testNetwork"
testNodePeerIds = []string{"peer1", "peer2", "peer3"}
testPeerOfPeerIds = []string{"peer11", "peer12"}
)
func testSetup() *node {
testNode := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: newNetwork(Name(testNodeNetName)),
}
// add some peers to the node
for _, id := range testNodePeerIds {
testNode.peers[id] = &node{
id: id,
address: testNode.address + "-" + id,
peers: make(map[string]*node),
network: testNode.network,
}
}
// add peers to peer1
// NOTE: these are peers of peers!
for _, id := range testPeerOfPeerIds {
testNode.peers["peer1"].peers[id] = &node{
id: id,
address: testNode.address + "-" + id,
peers: make(map[string]*node),
network: testNode.network,
}
}
// connect peer1 with peer2
testNode.peers["peer1"].peers["peer2"] = testNode.peers["peer2"]
// connect peer2 with peer3
testNode.peers["peer2"].peers["peer3"] = testNode.peers["peer3"]
return testNode
}
func TestNodeId(t *testing.T) {
node := testSetup()
if node.Id() != testNodeId {
t.Errorf("Expected id: %s, found: %s", testNodeId, node.Id())
}
}
func TestNodeAddress(t *testing.T) {
node := testSetup()
if node.Address() != testNodeAddress {
t.Errorf("Expected address: %s, found: %s", testNodeAddress, node.Address())
}
}
func TestNodeNetwork(t *testing.T) {
node := testSetup()
if node.Network().Name() != testNodeNetName {
t.Errorf("Expected network: %s, found: %s", testNodeNetName, node.Network().Name())
}
}
func TestNodes(t *testing.T) {
// single node
single := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: newNetwork(Name(testNodeNetName)),
}
// get all the nodes including yourself
nodes := single.Nodes()
nodeCount := 1
if len(nodes) != nodeCount {
t.Errorf("Expected to find %d nodes, found: %d", nodeCount, len(nodes))
}
// complicated node graph
node := testSetup()
// get all the nodes including yourself
nodes = node.Nodes()
// compile a list of ids of all nodes in the network into map for easy indexing
nodeIds := make(map[string]bool)
// add yourself
nodeIds[node.id] = true
// add peer Ids
for _, id := range testNodePeerIds {
nodeIds[id] = true
}
// add peer1 peers i.e. peers of peer
for _, id := range testPeerOfPeerIds {
nodeIds[id] = true
}
// we should return the correct number of nodes
if len(nodes) != len(nodeIds) {
t.Errorf("Expected %d nodes, found: %d", len(nodeIds), len(nodes))
}
// iterate through the list of nodes and makes sure all have been returned
for _, node := range nodes {
if _, ok := nodeIds[node.Id()]; !ok {
t.Errorf("Expected to find %s node", node.Id())
}
}
// this is a leaf node
id := "peer11"
if nodePeer := node.GetPeerNode(id); nodePeer == nil {
t.Errorf("Expected to find %s node", id)
}
}
func collectPeerIds(peer Node, ids map[string]bool) map[string]bool {
if len(peer.Peers()) == 0 {
return ids
}
// iterate through the whole graph
for _, peer := range peer.Peers() {
ids = collectPeerIds(peer, ids)
if _, ok := ids[peer.Id()]; !ok {
ids[peer.Id()] = true
}
}
return ids
}
func TestPeers(t *testing.T) {
// single node
single := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: newNetwork(Name(testNodeNetName)),
}
// get node peers
peers := single.Peers()
// there should be no peers
peerCount := 0
if len(peers) != peerCount {
t.Errorf("Expected to find %d nodes, found: %d", peerCount, len(peers))
}
// complicated node graph
node := testSetup()
// list of ids of nodes of MaxDepth
peerIds := make(map[string]bool)
// add peer Ids
for _, id := range testNodePeerIds {
peerIds[id] = true
}
// add peers of peers to peerIds
for _, id := range testPeerOfPeerIds {
peerIds[id] = true
}
// get node peers
peers = node.Peers()
// we will collect all returned Peer Ids into this map
resPeerIds := make(map[string]bool)
for _, peer := range peers {
resPeerIds[peer.Id()] = true
resPeerIds = collectPeerIds(peer, resPeerIds)
}
// if correct, we must collect all peerIds
if len(resPeerIds) != len(peerIds) {
t.Errorf("Expected to find %d peers, found: %d", len(peerIds), len(resPeerIds))
}
for id := range resPeerIds {
if _, ok := peerIds[id]; !ok {
t.Errorf("Expected to find %s peer", id)
}
}
}
func TestDeletePeerNode(t *testing.T) {
// complicated node graph
node := testSetup()
nodeCount := len(node.Nodes())
// should not find non-existent peer node
if err := node.DeletePeerNode("foobar"); err != ErrPeerNotFound {
t.Errorf("Expected: %v, got: %v", ErrPeerNotFound, err)
}
// lets pick one of the peer1 peers
if err := node.DeletePeerNode(testPeerOfPeerIds[0]); err != nil {
t.Errorf("Error deleting peer node: %v", err)
}
nodeDelCount := len(node.Nodes())
if nodeDelCount != nodeCount-1 {
t.Errorf("Expected node count: %d, got: %d", nodeCount-1, nodeDelCount)
}
}
func TestPruneStalePeerNodes(t *testing.T) {
// complicated node graph
node := testSetup()
nodes := node.Nodes()
pruneTime := 10 * time.Millisecond
time.Sleep(pruneTime)
// should delete all nodes besides node
pruned := node.PruneStalePeerNodes(pruneTime)
if len(pruned) != len(nodes)-1 {
t.Errorf("Expected pruned node count: %d, got: %d", len(nodes)-1, len(pruned))
}
}
func TestUnpackPeerTopology(t *testing.T) {
pbPeer := &pb.Peer{
Node: &pb.Node{
Id: "newPeer",
Address: "newPeerAddress",
},
Peers: make([]*pb.Peer, 0),
}
// it should add pbPeer to the single node peers
peer := UnpackPeerTopology(pbPeer, time.Now(), 5)
if peer.id != pbPeer.Node.Id {
t.Errorf("Expected peer id %s, found: %s", pbPeer.Node.Id, peer.id)
}
node := testSetup()
// build a simple topology to update node peer1
peer1 := node.peers["peer1"]
pbPeer1Node := &pb.Node{
Id: peer1.id,
Address: peer1.address,
}
pbPeer111 := &pb.Peer{
Node: &pb.Node{
Id: "peer111",
Address: "peer111Address",
},
Peers: make([]*pb.Peer, 0),
}
pbPeer121 := &pb.Peer{
Node: &pb.Node{
Id: "peer121",
Address: "peer121Address",
},
Peers: make([]*pb.Peer, 0),
}
// topology to update
pbPeer1 := &pb.Peer{
Node: pbPeer1Node,
Peers: []*pb.Peer{pbPeer111, pbPeer121},
}
// unpack peer1 topology
peer = UnpackPeerTopology(pbPeer1, time.Now(), 5)
// make sure peer1 topology has been correctly updated
newPeerIds := []string{pbPeer111.Node.Id, pbPeer121.Node.Id}
for _, id := range newPeerIds {
if _, ok := peer.peers[id]; !ok {
t.Errorf("Expected %s to be a peer of %s", id, "peer1")
}
}
}
func TestPeersToProto(t *testing.T) {
// single node
single := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: newNetwork(Name(testNodeNetName)),
}
topCount := 0
protoPeers := PeersToProto(single, 0)
if len(protoPeers.Peers) != topCount {
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
}
// complicated node graph
node := testSetup()
topCount = 3
// list of ids of nodes of depth 1 i.e. node peers
peerIds := make(map[string]bool)
// add peer Ids
for _, id := range testNodePeerIds {
peerIds[id] = true
}
// depth 1 should give us immmediate neighbours only
protoPeers = PeersToProto(node, 1)
if len(protoPeers.Peers) != topCount {
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
}
}

111
network/options.go Normal file
View File

@@ -0,0 +1,111 @@
package network
import (
"github.com/google/uuid"
"github.com/micro/go-micro/network/resolver"
"github.com/micro/go-micro/network/resolver/registry"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/proxy/mucp"
"github.com/micro/go-micro/router"
"github.com/micro/go-micro/tunnel"
)
type Option func(*Options)
// Options configure network
type Options struct {
// Id of the node
Id string
// Name of the network
Name string
// Address to bind to
Address string
// Advertise sets the address to advertise
Advertise string
// Peers is a list of peers to connect to
Peers []string
// Tunnel is network tunnel
Tunnel tunnel.Tunnel
// Router is network router
Router router.Router
// Proxy is network proxy
Proxy proxy.Proxy
// Resolver is network resolver
Resolver resolver.Resolver
}
// Id sets the id of the network node
func Id(id string) Option {
return func(o *Options) {
o.Id = id
}
}
// Name sets the network name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Address sets the network address
func Address(a string) Option {
return func(o *Options) {
o.Address = a
}
}
// Advertise sets the address to advertise
func Advertise(a string) Option {
return func(o *Options) {
o.Advertise = a
}
}
// Peers is a list of peers to connect to
func Peers(n ...string) Option {
return func(o *Options) {
o.Peers = n
}
}
// Tunnel sets the network tunnel
func Tunnel(t tunnel.Tunnel) Option {
return func(o *Options) {
o.Tunnel = t
}
}
// Router sets the network router
func Router(r router.Router) Option {
return func(o *Options) {
o.Router = r
}
}
// Proxy sets the network proxy
func Proxy(p proxy.Proxy) Option {
return func(o *Options) {
o.Proxy = p
}
}
// Resolver is the network resolver
func Resolver(r resolver.Resolver) Option {
return func(o *Options) {
o.Resolver = r
}
}
// DefaultOptions returns network default options
func DefaultOptions() Options {
return Options{
Id: uuid.New().String(),
Name: DefaultName,
Address: DefaultAddress,
Tunnel: tunnel.NewTunnel(),
Router: router.DefaultRouter,
Proxy: mucp.NewProxy(),
Resolver: &registry.Resolver{},
}
}

View File

@@ -0,0 +1,170 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/go-micro/network/proto/network.proto
package go_micro_network
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
_ "github.com/micro/go-micro/router/proto"
math "math"
)
import (
context "context"
client "github.com/micro/go-micro/client"
server "github.com/micro/go-micro/server"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ client.Option
var _ server.Option
// Client API for Network service
type NetworkService interface {
// Connect to the network
Connect(ctx context.Context, in *ConnectRequest, opts ...client.CallOption) (*ConnectResponse, error)
// Returns the entire network graph
Graph(ctx context.Context, in *GraphRequest, opts ...client.CallOption) (*GraphResponse, error)
// Returns a list of known nodes in the network
Nodes(ctx context.Context, in *NodesRequest, opts ...client.CallOption) (*NodesResponse, error)
// Returns a list of known routes in the network
Routes(ctx context.Context, in *RoutesRequest, opts ...client.CallOption) (*RoutesResponse, error)
// Returns a list of known services based on routes
Services(ctx context.Context, in *ServicesRequest, opts ...client.CallOption) (*ServicesResponse, error)
}
type networkService struct {
c client.Client
name string
}
func NewNetworkService(name string, c client.Client) NetworkService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.network"
}
return &networkService{
c: c,
name: name,
}
}
func (c *networkService) Connect(ctx context.Context, in *ConnectRequest, opts ...client.CallOption) (*ConnectResponse, error) {
req := c.c.NewRequest(c.name, "Network.Connect", in)
out := new(ConnectResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) Graph(ctx context.Context, in *GraphRequest, opts ...client.CallOption) (*GraphResponse, error) {
req := c.c.NewRequest(c.name, "Network.Graph", in)
out := new(GraphResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) Nodes(ctx context.Context, in *NodesRequest, opts ...client.CallOption) (*NodesResponse, error) {
req := c.c.NewRequest(c.name, "Network.Nodes", in)
out := new(NodesResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) Routes(ctx context.Context, in *RoutesRequest, opts ...client.CallOption) (*RoutesResponse, error) {
req := c.c.NewRequest(c.name, "Network.Routes", in)
out := new(RoutesResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) Services(ctx context.Context, in *ServicesRequest, opts ...client.CallOption) (*ServicesResponse, error) {
req := c.c.NewRequest(c.name, "Network.Services", in)
out := new(ServicesResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Network service
type NetworkHandler interface {
// Connect to the network
Connect(context.Context, *ConnectRequest, *ConnectResponse) error
// Returns the entire network graph
Graph(context.Context, *GraphRequest, *GraphResponse) error
// Returns a list of known nodes in the network
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 RegisterNetworkHandler(s server.Server, hdlr NetworkHandler, opts ...server.HandlerOption) error {
type network interface {
Connect(ctx context.Context, in *ConnectRequest, out *ConnectResponse) error
Graph(ctx context.Context, in *GraphRequest, out *GraphResponse) error
Nodes(ctx context.Context, in *NodesRequest, out *NodesResponse) error
Routes(ctx context.Context, in *RoutesRequest, out *RoutesResponse) error
Services(ctx context.Context, in *ServicesRequest, out *ServicesResponse) error
}
type Network struct {
network
}
h := &networkHandler{hdlr}
return s.Handle(s.NewHandler(&Network{h}, opts...))
}
type networkHandler struct {
NetworkHandler
}
func (h *networkHandler) Connect(ctx context.Context, in *ConnectRequest, out *ConnectResponse) error {
return h.NetworkHandler.Connect(ctx, in, out)
}
func (h *networkHandler) Graph(ctx context.Context, in *GraphRequest, out *GraphResponse) error {
return h.NetworkHandler.Graph(ctx, in, out)
}
func (h *networkHandler) Nodes(ctx context.Context, in *NodesRequest, out *NodesResponse) error {
return h.NetworkHandler.Nodes(ctx, in, out)
}
func (h *networkHandler) Routes(ctx context.Context, in *RoutesRequest, out *RoutesResponse) error {
return h.NetworkHandler.Routes(ctx, in, out)
}
func (h *networkHandler) Services(ctx context.Context, in *ServicesRequest, out *ServicesResponse) error {
return h.NetworkHandler.Services(ctx, in, out)
}

953
network/proto/network.pb.go Normal file
View File

@@ -0,0 +1,953 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: github.com/micro/go-micro/network/proto/network.proto
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"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Query is passed in a LookupRequest
type Query struct {
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
Gateway string `protobuf:"bytes,3,opt,name=gateway,proto3" json:"gateway,omitempty"`
Router string `protobuf:"bytes,4,opt,name=router,proto3" json:"router,omitempty"`
Network string `protobuf:"bytes,5,opt,name=network,proto3" json:"network,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Query) Reset() { *m = Query{} }
func (m *Query) String() string { return proto.CompactTextString(m) }
func (*Query) ProtoMessage() {}
func (*Query) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{0}
}
func (m *Query) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Query.Unmarshal(m, b)
}
func (m *Query) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Query.Marshal(b, m, deterministic)
}
func (m *Query) XXX_Merge(src proto.Message) {
xxx_messageInfo_Query.Merge(m, src)
}
func (m *Query) XXX_Size() int {
return xxx_messageInfo_Query.Size(m)
}
func (m *Query) XXX_DiscardUnknown() {
xxx_messageInfo_Query.DiscardUnknown(m)
}
var xxx_messageInfo_Query proto.InternalMessageInfo
func (m *Query) GetService() string {
if m != nil {
return m.Service
}
return ""
}
func (m *Query) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
func (m *Query) GetGateway() string {
if m != nil {
return m.Gateway
}
return ""
}
func (m *Query) GetRouter() string {
if m != nil {
return m.Router
}
return ""
}
func (m *Query) GetNetwork() string {
if m != nil {
return m.Network
}
return ""
}
type ConnectRequest struct {
Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ConnectRequest) Reset() { *m = ConnectRequest{} }
func (m *ConnectRequest) String() string { return proto.CompactTextString(m) }
func (*ConnectRequest) ProtoMessage() {}
func (*ConnectRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{1}
}
func (m *ConnectRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConnectRequest.Unmarshal(m, b)
}
func (m *ConnectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ConnectRequest.Marshal(b, m, deterministic)
}
func (m *ConnectRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ConnectRequest.Merge(m, src)
}
func (m *ConnectRequest) XXX_Size() int {
return xxx_messageInfo_ConnectRequest.Size(m)
}
func (m *ConnectRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ConnectRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ConnectRequest proto.InternalMessageInfo
func (m *ConnectRequest) GetNodes() []*Node {
if m != nil {
return m.Nodes
}
return nil
}
type ConnectResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ConnectResponse) Reset() { *m = ConnectResponse{} }
func (m *ConnectResponse) String() string { return proto.CompactTextString(m) }
func (*ConnectResponse) ProtoMessage() {}
func (*ConnectResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{2}
}
func (m *ConnectResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConnectResponse.Unmarshal(m, b)
}
func (m *ConnectResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ConnectResponse.Marshal(b, m, deterministic)
}
func (m *ConnectResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ConnectResponse.Merge(m, src)
}
func (m *ConnectResponse) XXX_Size() int {
return xxx_messageInfo_ConnectResponse.Size(m)
}
func (m *ConnectResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ConnectResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ConnectResponse proto.InternalMessageInfo
// PeerRequest requests list of peers
type NodesRequest struct {
// node topology depth
Depth uint32 `protobuf:"varint,1,opt,name=depth,proto3" json:"depth,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NodesRequest) Reset() { *m = NodesRequest{} }
func (m *NodesRequest) String() string { return proto.CompactTextString(m) }
func (*NodesRequest) ProtoMessage() {}
func (*NodesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{3}
}
func (m *NodesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodesRequest.Unmarshal(m, b)
}
func (m *NodesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NodesRequest.Marshal(b, m, deterministic)
}
func (m *NodesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_NodesRequest.Merge(m, src)
}
func (m *NodesRequest) XXX_Size() int {
return xxx_messageInfo_NodesRequest.Size(m)
}
func (m *NodesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_NodesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_NodesRequest proto.InternalMessageInfo
func (m *NodesRequest) GetDepth() uint32 {
if m != nil {
return m.Depth
}
return 0
}
// PeerResponse is returned by ListPeers
type NodesResponse struct {
// return peer topology
Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NodesResponse) Reset() { *m = NodesResponse{} }
func (m *NodesResponse) String() string { return proto.CompactTextString(m) }
func (*NodesResponse) ProtoMessage() {}
func (*NodesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{4}
}
func (m *NodesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodesResponse.Unmarshal(m, b)
}
func (m *NodesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NodesResponse.Marshal(b, m, deterministic)
}
func (m *NodesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_NodesResponse.Merge(m, src)
}
func (m *NodesResponse) XXX_Size() int {
return xxx_messageInfo_NodesResponse.Size(m)
}
func (m *NodesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_NodesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_NodesResponse proto.InternalMessageInfo
func (m *NodesResponse) GetNodes() []*Node {
if m != nil {
return m.Nodes
}
return nil
}
type GraphRequest struct {
// node topology depth
Depth uint32 `protobuf:"varint,1,opt,name=depth,proto3" json:"depth,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GraphRequest) Reset() { *m = GraphRequest{} }
func (m *GraphRequest) String() string { return proto.CompactTextString(m) }
func (*GraphRequest) ProtoMessage() {}
func (*GraphRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{5}
}
func (m *GraphRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GraphRequest.Unmarshal(m, b)
}
func (m *GraphRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GraphRequest.Marshal(b, m, deterministic)
}
func (m *GraphRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GraphRequest.Merge(m, src)
}
func (m *GraphRequest) XXX_Size() int {
return xxx_messageInfo_GraphRequest.Size(m)
}
func (m *GraphRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GraphRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GraphRequest proto.InternalMessageInfo
func (m *GraphRequest) GetDepth() uint32 {
if m != nil {
return m.Depth
}
return 0
}
type GraphResponse struct {
Root *Peer `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GraphResponse) Reset() { *m = GraphResponse{} }
func (m *GraphResponse) String() string { return proto.CompactTextString(m) }
func (*GraphResponse) ProtoMessage() {}
func (*GraphResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{6}
}
func (m *GraphResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GraphResponse.Unmarshal(m, b)
}
func (m *GraphResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GraphResponse.Marshal(b, m, deterministic)
}
func (m *GraphResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GraphResponse.Merge(m, src)
}
func (m *GraphResponse) XXX_Size() int {
return xxx_messageInfo_GraphResponse.Size(m)
}
func (m *GraphResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GraphResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GraphResponse proto.InternalMessageInfo
func (m *GraphResponse) GetRoot() *Peer {
if m != nil {
return m.Root
}
return nil
}
type RoutesRequest struct {
// filter based on
Query *Query `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RoutesRequest) Reset() { *m = RoutesRequest{} }
func (m *RoutesRequest) String() string { return proto.CompactTextString(m) }
func (*RoutesRequest) ProtoMessage() {}
func (*RoutesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{7}
}
func (m *RoutesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RoutesRequest.Unmarshal(m, b)
}
func (m *RoutesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RoutesRequest.Marshal(b, m, deterministic)
}
func (m *RoutesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RoutesRequest.Merge(m, src)
}
func (m *RoutesRequest) XXX_Size() int {
return xxx_messageInfo_RoutesRequest.Size(m)
}
func (m *RoutesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RoutesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RoutesRequest proto.InternalMessageInfo
func (m *RoutesRequest) GetQuery() *Query {
if m != nil {
return m.Query
}
return nil
}
type RoutesResponse struct {
Routes []*proto1.Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RoutesResponse) Reset() { *m = RoutesResponse{} }
func (m *RoutesResponse) String() string { return proto.CompactTextString(m) }
func (*RoutesResponse) ProtoMessage() {}
func (*RoutesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{8}
}
func (m *RoutesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RoutesResponse.Unmarshal(m, b)
}
func (m *RoutesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RoutesResponse.Marshal(b, m, deterministic)
}
func (m *RoutesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RoutesResponse.Merge(m, src)
}
func (m *RoutesResponse) XXX_Size() int {
return xxx_messageInfo_RoutesResponse.Size(m)
}
func (m *RoutesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RoutesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RoutesResponse proto.InternalMessageInfo
func (m *RoutesResponse) GetRoutes() []*proto1.Route {
if m != nil {
return m.Routes
}
return nil
}
type ServicesRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ServicesRequest) Reset() { *m = ServicesRequest{} }
func (m *ServicesRequest) String() string { return proto.CompactTextString(m) }
func (*ServicesRequest) ProtoMessage() {}
func (*ServicesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{9}
}
func (m *ServicesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ServicesRequest.Unmarshal(m, b)
}
func (m *ServicesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ServicesRequest.Marshal(b, m, deterministic)
}
func (m *ServicesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ServicesRequest.Merge(m, src)
}
func (m *ServicesRequest) XXX_Size() int {
return xxx_messageInfo_ServicesRequest.Size(m)
}
func (m *ServicesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ServicesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ServicesRequest proto.InternalMessageInfo
type ServicesResponse struct {
Services []string `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ServicesResponse) Reset() { *m = ServicesResponse{} }
func (m *ServicesResponse) String() string { return proto.CompactTextString(m) }
func (*ServicesResponse) ProtoMessage() {}
func (*ServicesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{10}
}
func (m *ServicesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ServicesResponse.Unmarshal(m, b)
}
func (m *ServicesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ServicesResponse.Marshal(b, m, deterministic)
}
func (m *ServicesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ServicesResponse.Merge(m, src)
}
func (m *ServicesResponse) XXX_Size() int {
return xxx_messageInfo_ServicesResponse.Size(m)
}
func (m *ServicesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ServicesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ServicesResponse proto.InternalMessageInfo
func (m *ServicesResponse) GetServices() []string {
if m != nil {
return m.Services
}
return nil
}
// Node is network node
type Node struct {
// node id
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// node address
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
// the network
Network string `protobuf:"bytes,3,opt,name=network,proto3" json:"network,omitempty"`
// associated metadata
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Node) Reset() { *m = Node{} }
func (m *Node) String() string { return proto.CompactTextString(m) }
func (*Node) ProtoMessage() {}
func (*Node) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{11}
}
func (m *Node) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Node.Unmarshal(m, b)
}
func (m *Node) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Node.Marshal(b, m, deterministic)
}
func (m *Node) XXX_Merge(src proto.Message) {
xxx_messageInfo_Node.Merge(m, src)
}
func (m *Node) XXX_Size() int {
return xxx_messageInfo_Node.Size(m)
}
func (m *Node) XXX_DiscardUnknown() {
xxx_messageInfo_Node.DiscardUnknown(m)
}
var xxx_messageInfo_Node proto.InternalMessageInfo
func (m *Node) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Node) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
func (m *Node) GetNetwork() string {
if m != nil {
return m.Network
}
return ""
}
func (m *Node) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
// Connect is sent when the node connects to the network
type Connect struct {
// network mode
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Connect) Reset() { *m = Connect{} }
func (m *Connect) String() string { return proto.CompactTextString(m) }
func (*Connect) ProtoMessage() {}
func (*Connect) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{12}
}
func (m *Connect) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Connect.Unmarshal(m, b)
}
func (m *Connect) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Connect.Marshal(b, m, deterministic)
}
func (m *Connect) XXX_Merge(src proto.Message) {
xxx_messageInfo_Connect.Merge(m, src)
}
func (m *Connect) XXX_Size() int {
return xxx_messageInfo_Connect.Size(m)
}
func (m *Connect) XXX_DiscardUnknown() {
xxx_messageInfo_Connect.DiscardUnknown(m)
}
var xxx_messageInfo_Connect proto.InternalMessageInfo
func (m *Connect) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
// Close is sent when the node disconnects from the network
type Close struct {
// network node
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Close) Reset() { *m = Close{} }
func (m *Close) String() string { return proto.CompactTextString(m) }
func (*Close) ProtoMessage() {}
func (*Close) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{13}
}
func (m *Close) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Close.Unmarshal(m, b)
}
func (m *Close) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Close.Marshal(b, m, deterministic)
}
func (m *Close) XXX_Merge(src proto.Message) {
xxx_messageInfo_Close.Merge(m, src)
}
func (m *Close) XXX_Size() int {
return xxx_messageInfo_Close.Size(m)
}
func (m *Close) XXX_DiscardUnknown() {
xxx_messageInfo_Close.DiscardUnknown(m)
}
var xxx_messageInfo_Close proto.InternalMessageInfo
func (m *Close) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
// Peer is used to advertise node peers
type Peer struct {
// network node
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
// node peers
Peers []*Peer `protobuf:"bytes,2,rep,name=peers,proto3" json:"peers,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Peer) Reset() { *m = Peer{} }
func (m *Peer) String() string { return proto.CompactTextString(m) }
func (*Peer) ProtoMessage() {}
func (*Peer) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{14}
}
func (m *Peer) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Peer.Unmarshal(m, b)
}
func (m *Peer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Peer.Marshal(b, m, deterministic)
}
func (m *Peer) XXX_Merge(src proto.Message) {
xxx_messageInfo_Peer.Merge(m, src)
}
func (m *Peer) XXX_Size() int {
return xxx_messageInfo_Peer.Size(m)
}
func (m *Peer) XXX_DiscardUnknown() {
xxx_messageInfo_Peer.DiscardUnknown(m)
}
var xxx_messageInfo_Peer proto.InternalMessageInfo
func (m *Peer) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
func (m *Peer) GetPeers() []*Peer {
if m != nil {
return m.Peers
}
return nil
}
func init() {
proto.RegisterType((*Query)(nil), "go.micro.network.Query")
proto.RegisterType((*ConnectRequest)(nil), "go.micro.network.ConnectRequest")
proto.RegisterType((*ConnectResponse)(nil), "go.micro.network.ConnectResponse")
proto.RegisterType((*NodesRequest)(nil), "go.micro.network.NodesRequest")
proto.RegisterType((*NodesResponse)(nil), "go.micro.network.NodesResponse")
proto.RegisterType((*GraphRequest)(nil), "go.micro.network.GraphRequest")
proto.RegisterType((*GraphResponse)(nil), "go.micro.network.GraphResponse")
proto.RegisterType((*RoutesRequest)(nil), "go.micro.network.RoutesRequest")
proto.RegisterType((*RoutesResponse)(nil), "go.micro.network.RoutesResponse")
proto.RegisterType((*ServicesRequest)(nil), "go.micro.network.ServicesRequest")
proto.RegisterType((*ServicesResponse)(nil), "go.micro.network.ServicesResponse")
proto.RegisterType((*Node)(nil), "go.micro.network.Node")
proto.RegisterMapType((map[string]string)(nil), "go.micro.network.Node.MetadataEntry")
proto.RegisterType((*Connect)(nil), "go.micro.network.Connect")
proto.RegisterType((*Close)(nil), "go.micro.network.Close")
proto.RegisterType((*Peer)(nil), "go.micro.network.Peer")
}
func init() {
proto.RegisterFile("github.com/micro/go-micro/network/proto/network.proto", fileDescriptor_0b7953b26a7c4730)
}
var fileDescriptor_0b7953b26a7c4730 = []byte{
// 573 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x61, 0x6a, 0xdb, 0x4c,
0x10, 0x8d, 0x2c, 0xcb, 0x76, 0xe6, 0x8b, 0xfd, 0xb9, 0x4b, 0x49, 0x85, 0x7e, 0xb4, 0xee, 0xe2,
0x1f, 0xa1, 0x34, 0x32, 0xc4, 0x04, 0x4a, 0x43, 0x43, 0x20, 0x94, 0x42, 0x21, 0x21, 0x55, 0x2e,
0x50, 0xc5, 0x1a, 0x6c, 0x93, 0x58, 0xeb, 0xac, 0xd6, 0x09, 0x3e, 0x41, 0x8f, 0xd0, 0x33, 0xf5,
0x56, 0x65, 0x77, 0x47, 0x8a, 0x1d, 0xcb, 0xa2, 0xf9, 0xe7, 0xd1, 0xbc, 0xf7, 0x66, 0x67, 0xe6,
0x8d, 0xe1, 0x78, 0x3c, 0x55, 0x93, 0xc5, 0x4d, 0x38, 0x12, 0xb3, 0xc1, 0x6c, 0x3a, 0x92, 0x62,
0x30, 0x16, 0x87, 0xf6, 0x47, 0x8a, 0xea, 0x51, 0xc8, 0xdb, 0xc1, 0x5c, 0x0a, 0x55, 0x44, 0xa1,
0x89, 0x58, 0x77, 0x2c, 0x42, 0x83, 0x0a, 0xe9, 0x7b, 0x30, 0xdc, 0x2e, 0x24, 0xc5, 0x42, 0xa1,
0x24, 0x1d, 0x1b, 0x58, 0x19, 0xfe, 0xcb, 0x01, 0xef, 0xc7, 0x02, 0xe5, 0x92, 0xf9, 0xd0, 0xcc,
0x50, 0x3e, 0x4c, 0x47, 0xe8, 0x3b, 0x3d, 0xe7, 0x60, 0x37, 0xca, 0x43, 0x9d, 0x89, 0x93, 0x44,
0x62, 0x96, 0xf9, 0x35, 0x9b, 0xa1, 0x50, 0x67, 0xc6, 0xb1, 0xc2, 0xc7, 0x78, 0xe9, 0xbb, 0x36,
0x43, 0x21, 0xdb, 0x87, 0x86, 0xad, 0xe3, 0xd7, 0x4d, 0x82, 0x22, 0xcd, 0xa0, 0xf7, 0xfa, 0x9e,
0x65, 0x50, 0xc8, 0x4f, 0xa1, 0x73, 0x2e, 0xd2, 0x14, 0x47, 0x2a, 0xc2, 0xfb, 0x05, 0x66, 0x8a,
0x7d, 0x04, 0x2f, 0x15, 0x09, 0x66, 0xbe, 0xd3, 0x73, 0x0f, 0xfe, 0x3b, 0xda, 0x0f, 0x9f, 0xb7,
0x1c, 0x5e, 0x8a, 0x04, 0x23, 0x0b, 0xe2, 0xaf, 0xe0, 0xff, 0x82, 0x9f, 0xcd, 0x45, 0x9a, 0x21,
0xef, 0xc3, 0x9e, 0x46, 0x64, 0xb9, 0xe0, 0x6b, 0xf0, 0x12, 0x9c, 0xab, 0x89, 0x69, 0xb0, 0x1d,
0xd9, 0x80, 0x7f, 0x81, 0x36, 0xa1, 0x2c, 0xed, 0x85, 0x75, 0xfb, 0xb0, 0xf7, 0x4d, 0xc6, 0xf3,
0x49, 0x75, 0x91, 0x13, 0x68, 0x13, 0x8a, 0x8a, 0x7c, 0x80, 0xba, 0x14, 0x42, 0x19, 0x54, 0x69,
0x8d, 0x2b, 0x44, 0x19, 0x19, 0x0c, 0x3f, 0x85, 0x76, 0xa4, 0xc7, 0x57, 0x34, 0x72, 0x08, 0xde,
0xbd, 0x5e, 0x1a, 0xb1, 0xdf, 0x6c, 0xb2, 0xcd, 0x4e, 0x23, 0x8b, 0xe2, 0x67, 0xd0, 0xc9, 0xf9,
0x54, 0x3d, 0xa4, 0xf5, 0x94, 0xf4, 0x48, 0xf6, 0x30, 0x04, 0x5a, 0x9b, 0x19, 0xee, 0xb5, 0x75,
0x43, 0xfe, 0x06, 0x1e, 0x42, 0xf7, 0xe9, 0x13, 0xc9, 0x06, 0xd0, 0x22, 0xd3, 0x58, 0xe1, 0xdd,
0xa8, 0x88, 0xf9, 0x1f, 0x07, 0xea, 0x7a, 0x6e, 0xac, 0x03, 0xb5, 0x69, 0x42, 0x1e, 0xab, 0x4d,
0x93, 0x6a, 0x7b, 0xe5, 0x66, 0x71, 0xd7, 0xcc, 0xc2, 0xce, 0xa0, 0x35, 0x43, 0x15, 0x27, 0xb1,
0x8a, 0xfd, 0xba, 0xe9, 0xa0, 0x5f, 0xbe, 0xa5, 0xf0, 0x82, 0x60, 0x5f, 0x53, 0x25, 0x97, 0x51,
0xc1, 0x0a, 0x4e, 0xa0, 0xbd, 0x96, 0x62, 0x5d, 0x70, 0x6f, 0x71, 0x49, 0xef, 0xd2, 0x3f, 0xf5,
0x26, 0x1f, 0xe2, 0xbb, 0x05, 0xd2, 0xb3, 0x6c, 0xf0, 0xb9, 0xf6, 0xc9, 0xe1, 0xc7, 0xd0, 0x24,
0xaf, 0xe9, 0x3d, 0x6a, 0x1f, 0x6c, 0xdf, 0xa3, 0xf1, 0x8a, 0xc1, 0xf0, 0x21, 0x78, 0xe7, 0x77,
0xc2, 0x2e, 0xff, 0x9f, 0x49, 0x3f, 0xa1, 0xae, 0xad, 0xf0, 0x12, 0x8e, 0x76, 0xf0, 0x1c, 0x51,
0xea, 0x81, 0xba, 0x15, 0xee, 0xb2, 0xa0, 0xa3, 0xdf, 0x2e, 0x34, 0x2f, 0x69, 0xb0, 0x57, 0x4f,
0x9d, 0xf5, 0x36, 0x59, 0xeb, 0x07, 0x1a, 0xbc, 0xaf, 0x40, 0xd0, 0x09, 0xee, 0xb0, 0xef, 0xe0,
0x19, 0xe7, 0xb3, 0xb7, 0x9b, 0xe8, 0xd5, 0xc3, 0x09, 0xde, 0x6d, 0xcd, 0xaf, 0x6a, 0x99, 0x53,
0x2d, 0xd3, 0x5a, 0xbd, 0xf4, 0x32, 0xad, 0xb5, 0x1b, 0xe7, 0x3b, 0xec, 0x02, 0x1a, 0xf6, 0x28,
0x58, 0x09, 0x78, 0xed, 0xdc, 0x82, 0xde, 0x76, 0x40, 0x21, 0x77, 0x0d, 0xad, 0xfc, 0x1c, 0x58,
0xc9, 0x5c, 0x9e, 0x5d, 0x4f, 0xc0, 0xab, 0x20, 0xb9, 0xe8, 0x4d, 0xc3, 0xfc, 0x49, 0x0f, 0xff,
0x06, 0x00, 0x00, 0xff, 0xff, 0x79, 0x8a, 0x5f, 0xf0, 0x24, 0x06, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
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",
}

102
network/proto/network.proto Normal file
View File

@@ -0,0 +1,102 @@
syntax = "proto3";
package go.micro.network;
import "github.com/micro/go-micro/router/proto/router.proto";
// Network service is usesd to gain visibility into networks
service Network {
// Connect to the network
rpc Connect(ConnectRequest) returns (ConnectResponse) {};
// Returns the entire network graph
rpc Graph(GraphRequest) returns (GraphResponse) {};
// Returns a list of known nodes in the network
rpc Nodes(NodesRequest) returns (NodesResponse) {};
// Returns a list of known routes in the network
rpc Routes(RoutesRequest) returns (RoutesResponse) {};
// Returns a list of known services based on routes
rpc Services(ServicesRequest) returns (ServicesResponse) {};
}
// Query is passed in a LookupRequest
message Query {
string service = 1;
string address = 2;
string gateway = 3;
string router = 4;
string network = 5;
}
message ConnectRequest {
repeated Node nodes = 1;
}
message ConnectResponse {}
// PeerRequest requests list of peers
message NodesRequest {
// node topology depth
uint32 depth = 1;
}
// PeerResponse is returned by ListPeers
message NodesResponse {
// return peer topology
repeated Node nodes = 1;
}
message GraphRequest {
// node topology depth
uint32 depth = 1;
}
message GraphResponse {
Peer root = 1;
}
message RoutesRequest {
// filter based on
Query query = 1;
}
message RoutesResponse {
repeated go.micro.router.Route routes = 1;
}
message ServicesRequest {}
message ServicesResponse {
repeated string services = 1;
}
// Node is network node
message Node {
// node id
string id = 1;
// node address
string address = 2;
// the network
string network = 3;
// associated metadata
map<string,string> metadata = 4;
}
// Connect is sent when the node connects to the network
message Connect {
// network mode
Node node = 1;
}
// Close is sent when the node disconnects from the network
message Close {
// network node
Node node = 1;
}
// Peer is used to advertise node peers
message Peer {
// network node
Node node = 1;
// node peers
repeated Peer peers = 2;
}

View File

@@ -1,30 +1,42 @@
// Package dns resolves names to dns srv records
// Package dns resolves names to dns records
package dns
import (
"fmt"
"net"
"github.com/micro/go-micro/network/resolver"
)
// Resolver is a DNS network resolve
type Resolver struct{}
// Resolve assumes ID is a domain name e.g micro.mu
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
_, addrs, err := net.LookupSRV("network", "udp", name)
host, port, err := net.SplitHostPort(name)
if err != nil {
host = name
port = "8085"
}
if len(host) == 0 {
host = "localhost"
}
addrs, err := net.LookupHost(host)
if err != nil {
return nil, err
}
var records []*resolver.Record
for _, addr := range addrs {
address := addr.Target
if addr.Port > 0 {
address = fmt.Sprintf("%s:%d", addr.Target, addr.Port)
}
// join resolved record with port
address := net.JoinHostPort(addr, port)
// append to record set
records = append(records, &resolver.Record{
Address: address,
})
}
return records, nil
}

View File

@@ -0,0 +1,31 @@
// Package dns srv resolves names to dns srv records
package dnssrv
import (
"fmt"
"net"
"github.com/micro/go-micro/network/resolver"
)
// Resolver is a DNS network resolve
type Resolver struct{}
// Resolve assumes ID is a domain name e.g micro.mu
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
_, addrs, err := net.LookupSRV("network", "udp", name)
if err != nil {
return nil, err
}
var records []*resolver.Record
for _, addr := range addrs {
address := addr.Target
if addr.Port > 0 {
address = fmt.Sprintf("%s:%d", addr.Target, addr.Port)
}
records = append(records, &resolver.Record{
Address: address,
})
}
return records, nil
}

View File

@@ -3,6 +3,7 @@ package http
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/url"
@@ -10,17 +11,26 @@ import (
"github.com/micro/go-micro/network/resolver"
)
// Resolver is a HTTP network resolver
type Resolver struct {
// If not set, defaults to http
Proto string
// Path sets the path to lookup. Defaults to /network
Path string
// Host url to use for the query
Host string
}
type Response struct {
Nodes []*resolver.Record `json:"nodes,omitempty"`
}
// Resolve assumes ID is a domain which can be converted to a http://name/network request
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
proto := "http"
proto := "https"
host := "micro.mu"
path := "/network"
if len(r.Proto) > 0 {
@@ -31,29 +41,38 @@ func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
path = r.Path
}
if len(r.Host) > 0 {
host = r.Host
}
uri := &url.URL{
Scheme: proto,
Path: path,
Host: name,
Host: host,
}
q := uri.Query()
q.Set("name", name)
uri.RawQuery = q.Encode()
rsp, err := http.Get(uri.String())
if err != nil {
return nil, err
}
defer rsp.Body.Close()
if rsp.StatusCode != 200 {
return nil, errors.New("non 200 response")
}
b, err := ioutil.ReadAll(rsp.Body)
if err != nil {
return nil, err
}
// encoding format is assumed to be json
var records []*resolver.Record
var response *Response
if err := json.Unmarshal(b, &records); err != nil {
if err := json.Unmarshal(b, &response); err != nil {
return nil, err
}
return records, nil
return response.Nodes, nil
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/micro/go-micro/registry"
)
// Resolver is a registry network resolver
type Resolver struct {
// Registry is the registry to use otherwise we use the defaul
Registry registry.Registry

View File

@@ -5,7 +5,7 @@ package resolver
// via the name to connect to. This is done based on Network.Name().
// Before we can be part of any network, we have to connect to it.
type Resolver interface {
// Resolve returns a list of addresses for an name
// Resolve returns a list of addresses for a name
Resolve(name string) ([]*Record, error)
}

View File

@@ -0,0 +1,33 @@
// Package static is a static resolver
package registry
import (
"github.com/micro/go-micro/network/resolver"
)
// Resolver returns a static list of nodes. In the event the node list
// is not present it will return the name of the network passed in.
type Resolver struct {
// A static list of nodes
Nodes []string
}
// Resolve returns the list of nodes
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
// if there are no nodes just return the name
if len(r.Nodes) == 0 {
return []*resolver.Record{
{Address: name},
}, nil
}
var records []*resolver.Record
for _, node := range r.Nodes {
records = append(records, &resolver.Record{
Address: node,
})
}
return records, nil
}

View File

@@ -0,0 +1,211 @@
// Package handler implements network RPC handler
package handler
import (
"context"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/network"
pbNet "github.com/micro/go-micro/network/proto"
"github.com/micro/go-micro/router"
pbRtr "github.com/micro/go-micro/router/proto"
"github.com/micro/go-micro/util/log"
)
// Network implements network handler
type Network struct {
Network network.Network
}
func flatten(n network.Node, visited map[string]bool) []network.Node {
// if node is nil runaway
if n == nil {
return nil
}
// set visisted
if visited == nil {
visited = make(map[string]bool)
}
// create new list of nodes
var nodes []network.Node
// check if already visited
if visited[n.Id()] == false {
// append the current node
nodes = append(nodes, n)
}
// set to visited
visited[n.Id()] = true
// visit the list of peers
for _, node := range n.Peers() {
nodes = append(nodes, flatten(node, visited)...)
}
return nodes
}
func (n *Network) Connect(ctx context.Context, req *pbNet.ConnectRequest, resp *pbNet.ConnectResponse) error {
if len(req.Nodes) == 0 {
return nil
}
// get list of existing nodes
nodes := n.Network.Options().Peers
// generate a node map
nodeMap := make(map[string]bool)
for _, node := range nodes {
nodeMap[node] = true
}
for _, node := range req.Nodes {
// TODO: we may have been provided a network only
// so process anad resolve node.Network
if len(node.Address) == 0 {
continue
}
// already exists
if _, ok := nodeMap[node.Address]; ok {
continue
}
nodeMap[node.Address] = true
nodes = append(nodes, node.Address)
}
log.Infof("Network.Connect setting peers: %v", nodes)
// reinitialise the peers
n.Network.Init(
network.Peers(nodes...),
)
// call the connect method
n.Network.Connect()
return nil
}
// Nodes returns the list of nodes
func (n *Network) Nodes(ctx context.Context, req *pbNet.NodesRequest, resp *pbNet.NodesResponse) error {
depth := uint(req.Depth)
if depth <= 0 || depth > network.MaxDepth {
depth = network.MaxDepth
}
// root node
nodes := map[string]network.Node{}
// get peers encoded into protobuf
peers := flatten(n.Network, nil)
// walk all the peers
for _, peer := range peers {
if peer == nil {
continue
}
if _, ok := nodes[peer.Id()]; ok {
continue
}
// add to visited list
nodes[n.Network.Id()] = peer
resp.Nodes = append(resp.Nodes, &pbNet.Node{
Id: peer.Id(),
Address: peer.Address(),
})
}
return nil
}
// Graph returns the network graph from this root node
func (n *Network) Graph(ctx context.Context, req *pbNet.GraphRequest, resp *pbNet.GraphResponse) error {
depth := uint(req.Depth)
if depth <= 0 || depth > network.MaxDepth {
depth = network.MaxDepth
}
// get peers encoded into protobuf
peers := network.PeersToProto(n.Network, depth)
// set the root node
resp.Root = peers
return nil
}
// Routes returns a list of routing table routes
func (n *Network) Routes(ctx context.Context, req *pbNet.RoutesRequest, resp *pbNet.RoutesResponse) error {
// build query
var qOpts []router.QueryOption
if q := req.Query; q != nil {
if len(q.Service) > 0 {
qOpts = append(qOpts, router.QueryService(q.Service))
}
if len(q.Address) > 0 {
qOpts = append(qOpts, router.QueryAddress(q.Address))
}
if len(q.Gateway) > 0 {
qOpts = append(qOpts, router.QueryGateway(q.Gateway))
}
if len(q.Router) > 0 {
qOpts = append(qOpts, router.QueryRouter(q.Router))
}
if len(q.Network) > 0 {
qOpts = append(qOpts, router.QueryNetwork(q.Network))
}
}
routes, err := n.Network.Options().Router.Table().Query(qOpts...)
if err != nil {
return errors.InternalServerError("go.micro.network", "failed to list routes: %s", err)
}
var respRoutes []*pbRtr.Route
for _, route := range routes {
respRoute := &pbRtr.Route{
Service: route.Service,
Address: route.Address,
Gateway: route.Gateway,
Network: route.Network,
Router: route.Router,
Link: route.Link,
Metric: int64(route.Metric),
}
respRoutes = append(respRoutes, respRoute)
}
resp.Routes = respRoutes
return nil
}
// Services returns a list of services based on the routing table
func (n *Network) Services(ctx context.Context, req *pbNet.ServicesRequest, resp *pbNet.ServicesResponse) error {
routes, err := n.Network.Options().Router.Table().List()
if err != nil {
return errors.InternalServerError("go.micro.network", "failed to list services: %s", err)
}
services := make(map[string]bool)
for _, route := range routes {
if _, ok := services[route.Service]; ok {
continue
}
services[route.Service] = true
resp.Services = append(resp.Services, route.Service)
}
return nil
}

View File

@@ -1,337 +0,0 @@
package tunnel
import (
"crypto/sha256"
"errors"
"fmt"
"sync"
"github.com/google/uuid"
"github.com/micro/go-micro/network/link"
"github.com/micro/go-micro/transport"
)
// tun represents a network tunnel
type tun struct {
// the link on top of which we build a tunnel
link link.Link
sync.RWMutex
// to indicate if we're connected or not
connected bool
// the send channel for all messages
send chan *message
// close channel
closed chan bool
// a map of sockets based on Micro-Tunnel-Id
sockets map[string]*socket
}
// create new tunnel on top of a link
func newTunnel(link link.Link) *tun {
return &tun{
link: link,
send: make(chan *message, 128),
closed: make(chan bool),
sockets: make(map[string]*socket),
}
}
// getSocket returns a socket from the internal socket map.
// It does this based on the Micro-Tunnel-Id and Micro-Tunnel-Session
func (t *tun) getSocket(id, session string) (*socket, bool) {
// get the socket
t.RLock()
s, ok := t.sockets[id+session]
t.RUnlock()
return s, ok
}
// newSocket creates a new socket and saves it
func (t *tun) newSocket(id, session string) (*socket, bool) {
// hash the id
h := sha256.New()
h.Write([]byte(id))
id = fmt.Sprintf("%x", h.Sum(nil))
// new socket
s := &socket{
id: id,
session: session,
closed: make(chan bool),
recv: make(chan *message, 128),
send: t.send,
wait: make(chan bool),
}
// save socket
t.Lock()
_, ok := t.sockets[id+session]
if ok {
// socket already exists
t.Unlock()
return nil, false
}
t.sockets[id+session] = s
t.Unlock()
// return socket
return s, true
}
// TODO: use tunnel id as part of the session
func (t *tun) newSession() string {
return uuid.New().String()
}
// process outgoing messages sent by all local sockets
func (t *tun) process() {
// manage the send buffer
// all pseudo sockets throw everything down this
for {
select {
case msg := <-t.send:
nmsg := &transport.Message{
Header: msg.data.Header,
Body: msg.data.Body,
}
// set the tunnel id on the outgoing message
nmsg.Header["Micro-Tunnel-Id"] = msg.id
// set the session id
nmsg.Header["Micro-Tunnel-Session"] = msg.session
// send the message via the interface
if err := t.link.Send(nmsg); err != nil {
// no op
// TODO: do something
}
case <-t.closed:
return
}
}
}
// process incoming messages
func (t *tun) listen() {
for {
// process anything via the net interface
msg := new(transport.Message)
err := t.link.Recv(msg)
if err != nil {
return
}
// first check Micro-Tunnel
switch msg.Header["Micro-Tunnel"] {
case "connect":
// assuming new connection
// TODO: do something with this
continue
case "close":
// assuming connection closed
// TODO: do something with this
continue
}
// the tunnel id
id := msg.Header["Micro-Tunnel-Id"]
// the session id
session := msg.Header["Micro-Tunnel-Session"]
// if the session id is blank there's nothing we can do
// TODO: check this is the case, is there any reason
// why we'd have a blank session? Is the tunnel
// used for some other purpose?
if len(id) == 0 || len(session) == 0 {
continue
}
// get the socket based on the tunnel id and session
// this could be something we dialed in which case
// we have a session for it otherwise its a listener
s, exists := t.getSocket(id, session)
if !exists {
// try get it based on just the tunnel id
// the assumption here is that a listener
// has no session but its set a listener session
s, exists = t.getSocket(id, "listener")
if !exists {
// drop it, we don't care about
// messages we don't know about
continue
}
}
// is the socket closed?
select {
case <-s.closed:
// closed
delete(t.sockets, id)
continue
default:
// process
}
// is the socket new?
select {
// if its new the socket is actually blocked waiting
// for a connection. so we check if its waiting.
case <-s.wait:
// if its waiting e.g its new then we close it
default:
// set remote address of the socket
s.remote = msg.Header["Remote"]
close(s.wait)
}
// construct a new transport message
tmsg := &transport.Message{
Header: msg.Header,
Body: msg.Body,
}
// construct the internal message
imsg := &message{
id: id,
session: session,
data: tmsg,
}
// append to recv backlog
// we don't block if we can't pass it on
select {
case s.recv <- imsg:
default:
}
}
}
func (t *tun) connect() error {
return t.link.Send(&transport.Message{
Header: map[string]string{
"Micro-Tunnel": "connect",
},
})
}
func (t *tun) close() error {
return t.link.Send(&transport.Message{
Header: map[string]string{
"Micro-Tunnel": "close",
},
})
}
// Close the tunnel
func (t *tun) Close() error {
t.Lock()
defer t.Unlock()
if !t.connected {
return nil
}
select {
case <-t.closed:
return nil
default:
// close all the sockets
for _, s := range t.sockets {
s.Close()
}
// close the connection
close(t.closed)
t.connected = false
// send a close message
// we don't close the link
// just the tunnel
return t.close()
}
return nil
}
// Connect the tunnel
func (t *tun) Connect() error {
t.Lock()
defer t.Unlock()
// already connected
if t.connected {
return nil
}
// send the connect message
if err := t.connect(); err != nil {
return err
}
// set as connected
t.connected = true
// create new close channel
t.closed = make(chan bool)
// process messages to be sent
go t.process()
// process incoming messages
go t.listen()
return nil
}
// Dial an address
func (t *tun) Dial(addr string) (Conn, error) {
c, ok := t.newSocket(addr, t.newSession())
if !ok {
return nil, errors.New("error dialing " + addr)
}
// set remote
c.remote = addr
// set local
c.local = t.link.Local()
return c, nil
}
// Accept a connection on the address
func (t *tun) Listen(addr string) (Listener, error) {
// create a new socket by hashing the address
c, ok := t.newSocket(addr, "listener")
if !ok {
return nil, errors.New("already listening on " + addr)
}
// set remote. it will be replaced by the first message received
c.remote = t.link.Remote()
// set local
c.local = addr
tl := &tunListener{
addr: addr,
// the accept channel
accept: make(chan *socket, 128),
// the channel to close
closed: make(chan bool),
// the connection
conn: c,
// the listener socket
socket: c,
}
// this kicks off the internal message processor
// for the listener so it can create pseudo sockets
// per session if they do not exist or pass messages
// to the existign sessions
go tl.process()
// return the listener
return tl, nil
}

View File

@@ -1,101 +0,0 @@
package tunnel
import (
"io"
)
type tunListener struct {
// address of the listener
addr string
// the accept channel
accept chan *socket
// the channel to close
closed chan bool
// the connection
conn Conn
// the listener socket
socket *socket
}
func (t *tunListener) process() {
// our connection map for session
conns := make(map[string]*socket)
for {
select {
case <-t.closed:
return
// receive a new message
case m := <-t.socket.recv:
// get a socket
sock, ok := conns[m.session]
if !ok {
// create a new socket session
sock = &socket{
// our tunnel id
id: m.id,
// the session id
session: m.session,
// close chan
closed: make(chan bool),
// recv called by the acceptor
recv: make(chan *message, 128),
// use the internal send buffer
send: t.socket.send,
// wait
wait: make(chan bool),
}
// first message
sock.recv <- m
// save the socket
conns[m.session] = sock
// send to accept chan
select {
case <-t.closed:
return
case t.accept <- sock:
}
}
// send this to the accept chan
select {
case <-sock.closed:
delete(conns, m.session)
case sock.recv <- m:
}
}
}
}
func (t *tunListener) Addr() string {
return t.addr
}
func (t *tunListener) Close() error {
select {
case <-t.closed:
return nil
default:
close(t.closed)
}
return nil
}
// Everytime accept is called we essentially block till we get a new connection
func (t *tunListener) Accept() (Conn, error) {
select {
// if the socket is closed return
case <-t.closed:
return nil, io.EOF
// wait for a new connection
case c, ok := <-t.accept:
if !ok {
return nil, io.EOF
}
return c, nil
}
return nil, nil
}

View File

@@ -1,90 +0,0 @@
package tunnel
import (
"errors"
"github.com/micro/go-micro/transport"
)
// socket is our pseudo socket for transport.Socket
type socket struct {
// socket id based on Micro-Tunnel
id string
// the session id based on Micro.Tunnel-Session
session string
// closed
closed chan bool
// remote addr
remote string
// local addr
local string
// send chan
send chan *message
// recv chan
recv chan *message
// wait until we have a connection
wait chan bool
}
// message is sent over the send channel
type message struct {
// tunnel id
id string
// the session id
session string
// transport data
data *transport.Message
}
func (s *socket) Remote() string {
return s.remote
}
func (s *socket) Local() string {
return s.local
}
func (s *socket) Id() string {
return s.id
}
func (s *socket) Session() string {
return s.session
}
func (s *socket) Send(m *transport.Message) error {
select {
case <-s.closed:
return errors.New("socket is closed")
default:
// no op
}
// append to backlog
s.send <- &message{id: s.id, session: s.session, data: m}
return nil
}
func (s *socket) Recv(m *transport.Message) error {
select {
case <-s.closed:
return errors.New("socket is closed")
default:
// no op
}
// recv from backlog
msg := <-s.recv
// set message
*m = *msg.data
// return nil
return nil
}
func (s *socket) Close() error {
select {
case <-s.closed:
// no op
default:
close(s.closed)
}
return nil
}

View File

@@ -1,44 +0,0 @@
// Package tunnel provides gre network tunnelling
package tunnel
import (
"github.com/micro/go-micro/network/link"
"github.com/micro/go-micro/transport"
)
// Tunnel creates a gre network tunnel on top of a link.
// It establishes multiple streams using the Micro-Tunnel-Id header
// and Micro-Tunnel-Session header. The tunnel id is a hash of
// the address being requested.
type Tunnel interface {
// Connect connects the tunnel
Connect() error
// Close closes the tunnel
Close() error
// Dial an endpoint
Dial(addr string) (Conn, error)
// Accept connections
Listen(addr string) (Listener, error)
}
// The listener provides similar constructs to the transport.Listener
type Listener interface {
Addr() string
Close() error
Accept() (Conn, error)
}
// Conn is a connection dialed or accepted which includes the tunnel id and session
type Conn interface {
// Specifies the tunnel id
Id() string
// The session
Session() string
// a transport socket
transport.Socket
}
// NewTunnel creates a new tunnel on top of a link
func NewTunnel(l link.Link) Tunnel {
return newTunnel(l)
}

View File

@@ -1,113 +0,0 @@
package tunnel
import (
"testing"
"github.com/micro/go-micro/network/link"
"github.com/micro/go-micro/transport"
)
// testAccept will accept connections on the transport, create a new link and tunnel on top
func testAccept(t *testing.T, l transport.Listener, wait chan bool) error {
// accept new connections on the transport
// establish a link and tunnel
return l.Accept(func(s transport.Socket) {
// convert the socket into a link
li := link.NewLink(
link.Socket(s),
)
// connect the link e.g start internal buffers
if err := li.Connect(); err != nil {
t.Fatal(err)
}
// create a new tunnel
tun := NewTunnel(li)
// connect the tunnel
if err := tun.Connect(); err != nil {
t.Fatal(err)
}
// listen on some virtual address
tl, err := tun.Listen("test-tunnel")
if err != nil {
t.Fatal(err)
return
}
// accept a connection
c, err := tl.Accept()
if err != nil {
t.Fatal(err)
}
// get a message
for {
m := new(transport.Message)
if err := c.Recv(m); err != nil {
t.Fatal(err)
}
close(wait)
return
}
})
}
// testSend will create a new link to an address and then a tunnel on top
func testSend(t *testing.T, addr string) {
// create a new link
l := link.NewLink(
link.Address(addr),
)
// connect the link, this includes dialing
if err := l.Connect(); err != nil {
t.Fatal(err)
}
// create a tunnel on the link
tun := NewTunnel(l)
// connect the tunnel with the remote side
if err := tun.Connect(); err != nil {
t.Fatal(err)
}
// dial a new session
c, err := tun.Dial("test-tunnel")
if err != nil {
t.Fatal(err)
}
m := transport.Message{
Header: map[string]string{
"test": "header",
},
}
if err := c.Send(&m); err != nil {
t.Fatal(err)
}
}
func TestTunnel(t *testing.T) {
// create a new listener
tr := transport.NewTransport()
l, err := tr.Listen(":0")
if err != nil {
t.Fatal(err)
}
defer l.Close()
wait := make(chan bool)
// start accepting connections
go testAccept(t, l, wait)
// send a message
testSend(t, l.Addr())
// wait until message is received
<-wait
}

125
plugin/default.go Normal file
View File

@@ -0,0 +1,125 @@
// Package plugin provides the ability to load plugins
package plugin
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
pg "plugin"
"strings"
"text/template"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/server"
"github.com/micro/go-micro/transport"
)
type plugin struct{}
// Init sets up the plugin
func (p *plugin) Init(c *Config) error {
switch c.Type {
case "broker":
pg, ok := c.NewFunc.(func(...broker.Option) broker.Broker)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultBrokers[c.Name] = pg
case "client":
pg, ok := c.NewFunc.(func(...client.Option) client.Client)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultClients[c.Name] = pg
case "registry":
pg, ok := c.NewFunc.(func(...registry.Option) registry.Registry)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultRegistries[c.Name] = pg
case "selector":
pg, ok := c.NewFunc.(func(...selector.Option) selector.Selector)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultSelectors[c.Name] = pg
case "server":
pg, ok := c.NewFunc.(func(...server.Option) server.Server)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultServers[c.Name] = pg
case "transport":
pg, ok := c.NewFunc.(func(...transport.Option) transport.Transport)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultTransports[c.Name] = pg
default:
return fmt.Errorf("Unknown plugin type: %s for %s", c.Type, c.Name)
}
return nil
}
// Load loads a plugin created with `go build -buildmode=plugin`
func (p *plugin) Load(path string) (*Config, error) {
plugin, err := pg.Open(path)
if err != nil {
return nil, err
}
s, err := plugin.Lookup("Plugin")
if err != nil {
return nil, err
}
pl, ok := s.(*Config)
if !ok {
return nil, errors.New("could not cast Plugin object")
}
return pl, nil
}
// Generate creates a go file at the specified path.
// You must use `go build -buildmode=plugin`to build it.
func (p *plugin) Generate(path string, c *Config) error {
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()
t, err := template.New(c.Name).Parse(tmpl)
if err != nil {
return err
}
return t.Execute(f, c)
}
// Build generates a dso plugin using the go command `go build -buildmode=plugin`
func (p *plugin) Build(path string, c *Config) error {
path = strings.TrimSuffix(path, ".so")
// create go file in tmp path
temp := os.TempDir()
base := filepath.Base(path)
goFile := filepath.Join(temp, base+".go")
// generate .go file
if err := p.Generate(goFile, c); err != nil {
return err
}
// remove .go file
defer os.Remove(goFile)
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil && !os.IsExist(err) {
return fmt.Errorf("Failed to create dir %s: %v", filepath.Dir(path), err)
}
cmd := exec.Command("go", "build", "-buildmode=plugin", "-o", path+".so", goFile)
return cmd.Run()
}

46
plugin/plugin.go Normal file
View File

@@ -0,0 +1,46 @@
// Package plugin provides the ability to load plugins
package plugin
// Plugin is a plugin loaded from a file
type Plugin interface {
// Initialise a plugin with the config
Init(c *Config) error
// Load loads a .so plugin at the given path
Load(path string) (*Config, error)
// Build a .so plugin with config at the path specified
Build(path string, c *Config) error
}
// Config is the plugin config
type Config struct {
// Name of the plugin e.g rabbitmq
Name string
// Type of the plugin e.g broker
Type string
// Path specifies the import path
Path string
// NewFunc creates an instance of the plugin
NewFunc interface{}
}
var (
// Default plugin loader
DefaultPlugin = NewPlugin()
)
// NewPlugin creates a new plugin interface
func NewPlugin() Plugin {
return &plugin{}
}
func Build(path string, c *Config) error {
return DefaultPlugin.Build(path, c)
}
func Load(path string) (*Config, error) {
return DefaultPlugin.Load(path)
}
func Init(c *Config) error {
return DefaultPlugin.Init(c)
}

20
plugin/template.go Normal file
View File

@@ -0,0 +1,20 @@
package plugin
var (
tmpl = `
package main
import (
"github.com/micro/go-micro/plugin"
"{{.Path}}"
)
var Plugin = plugin.Config{
Name: "{{.Name}}",
Type: "{{.Type}}",
Path: "{{.Path}}",
NewFunc: {{.Name}}.{{.NewFunc}},
}
`
)

View File

@@ -10,7 +10,8 @@ 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/network/proxy"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/server"
)
@@ -61,6 +62,10 @@ 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")
}
// ServeRequest honours the server.Proxy interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
// set default client

View File

@@ -10,9 +10,10 @@ 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/network/proxy"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/server"
)
@@ -44,6 +45,10 @@ 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")
}
// ServeRequest honours the server.Router interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
if p.Endpoint == "" {

View File

@@ -5,15 +5,18 @@ import (
"context"
"fmt"
"io"
"sort"
"strings"
"sync"
"time"
"github.com/micro/go-micro/client"
"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/network/proxy"
"github.com/micro/go-micro/network/router"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/router"
"github.com/micro/go-micro/server"
)
@@ -26,9 +29,12 @@ type Proxy struct {
// Endpoint specifies the fixed service endpoint to call.
Endpoint string
// The client to use for outbound requests
// The client to use for outbound requests in the local network
Client client.Client
// Links are used for outbound requests not in the local network
Links map[string]client.Client
// The router for routes
Router router.Router
@@ -76,7 +82,7 @@ func readLoop(r server.Request, s client.Stream) error {
}
// toNodes returns a list of node addresses from given routes
func toNodes(routes map[uint64]router.Route) []string {
func toNodes(routes []router.Route) []string {
var nodes []string
for _, node := range routes {
address := node.Address
@@ -88,37 +94,63 @@ func toNodes(routes map[uint64]router.Route) []string {
return nodes
}
func (p *Proxy) getRoute(service string) ([]string, error) {
func (p *Proxy) getLink(r router.Route) (client.Client, error) {
if r.Link == "local" || len(p.Links) == 0 {
return p.Client, nil
}
l, ok := p.Links[r.Link]
if !ok {
return nil, errors.InternalServerError("go.micro.proxy", "link not found")
}
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
}
// lookup the route cache first
p.Lock()
routes, ok := p.Routes[service]
if ok {
p.Unlock()
return toNodes(routes), nil
return toSlice(routes), nil
}
p.Routes[service] = make(map[uint64]router.Route)
p.Unlock()
// if the router is broken return error
if status := p.Router.Status(); status.Code == router.Error {
return nil, status.Error
}
// lookup the routes in the router
results, err := p.Router.Lookup(router.NewQuery(router.QueryService(service)))
results, err := p.Router.Lookup(router.QueryService(service))
if err != nil {
// check the status of the router
if status := p.Router.Status(); status.Code == router.Error {
return nil, status.Error
}
// otherwise return the error
return nil, err
}
// update the proxy cache
p.Lock()
for _, route := range results {
// create if does not exist
if _, ok := p.Routes[service]; !ok {
p.Routes[service] = make(map[uint64]router.Route)
}
p.Routes[service][route.Hash()] = route
}
routes = p.Routes[service]
p.Unlock()
return toNodes(routes), nil
return toSlice(routes), nil
}
// manageRouteCache applies action on a given route to Proxy route cache
@@ -130,9 +162,6 @@ func (p *Proxy) manageRouteCache(route router.Route, action string) error {
}
p.Routes[route.Service][route.Hash()] = route
case "delete":
if _, ok := p.Routes[route.Service]; !ok {
return fmt.Errorf("route not found")
}
delete(p.Routes[route.Service], route.Hash())
default:
return fmt.Errorf("unknown action: %s", action)
@@ -171,12 +200,31 @@ func (p *Proxy) watchRoutes() {
}
}
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
return errors.InternalServerError("go.micro.proxy", "SendRequest is unsupported")
}
// ServeRequest honours the server.Router interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
// service name
service := req.Service()
endpoint := req.Endpoint()
// determine if its local routing
var local bool
// address to call
var addresses []string
// routes
var routes []router.Route
// service name to call
service := req.Service()
// endpoint to call
endpoint := req.Endpoint()
if len(service) == 0 {
return errors.BadRequest("go.micro.proxy", "service name is blank")
}
// are we network routing or local routing
if len(p.Links) == 0 {
local = true
}
// call a specific backend endpoint either by name or address
if len(p.Endpoint) > 0 {
@@ -190,7 +238,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
return err
}
// set the address
addresses = addr
routes = addr
// set the name
service = p.Endpoint
}
@@ -201,16 +249,66 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
if err != nil {
return err
}
addresses = addr
routes = addr
}
var opts []client.CallOption
// set address if available
// 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...))
}
// there's no links e.g we're local routing then just serve it with addresses
if local {
var opts []client.CallOption
// set address if available via routes or specific endpoint
if len(routes) > 0 {
addresses := toNodes(routes)
opts = append(opts, client.WithAddress(addresses...))
}
// serve the normal way
return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...)
}
var gerr error
// we're routing globally with multiple links
// so we need to pick a link per route
for _, route := range routes {
// pick the link or error out
link, err := p.getLink(route)
if err != nil {
// ok let's try again
gerr = err
continue
}
// set the address to call
addresses := toNodes([]router.Route{route})
// do the request with the link
gerr = p.serveRequest(ctx, link, service, endpoint, req, rsp, client.WithAddress(addresses...))
// return on no error since we succeeded
if gerr == nil {
return nil
}
// return where the context deadline was exceeded
if gerr == context.Canceled || gerr == context.DeadlineExceeded {
return err
}
// otherwise attempt to do it all over again
}
// if we got here something went really badly wrong
return gerr
}
func (p *Proxy) serveRequest(ctx context.Context, link client.Client, service, endpoint string, req server.Request, rsp server.Response, opts ...client.CallOption) error {
// read initial request
body, err := req.Read()
if err != nil {
@@ -218,16 +316,33 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
}
// create new request with raw bytes body
creq := p.Client.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType()))
creq := link.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType()))
// not a stream so make a client.Call request
if !req.Stream() {
crsp := new(bytes.Frame)
// make a call to the backend
if err := link.Call(ctx, creq, crsp, opts...); err != nil {
return err
}
// write the response
if err := rsp.Write(crsp.Data); err != nil {
return err
}
return nil
}
// create new stream
stream, err := p.Client.Stream(ctx, creq, opts...)
stream, err := link.Stream(ctx, creq, opts...)
if err != nil {
return err
}
defer stream.Close()
// create client request read loop
// create client request read loop if streaming
go readLoop(req, stream)
// get raw response
@@ -283,6 +398,7 @@ func NewSingleHostProxy(endpoint string) *Proxy {
// NewProxy returns a new proxy which will route based on mucp headers
func NewProxy(opts ...options.Option) proxy.Proxy {
p := new(Proxy)
p.Links = map[string]client.Client{}
p.Options = options.NewOptions(opts...)
p.Options.Init(options.WithString("mucp"))
@@ -303,6 +419,12 @@ func NewProxy(opts ...options.Option) proxy.Proxy {
p.Client = client.DefaultClient
}
// get client
links, ok := p.Options.Values().Get("proxy.links")
if ok {
p.Links = links.(map[string]client.Client)
}
// get router
r, ok := p.Options.Values().Get("proxy.router")
if ok {
@@ -319,7 +441,16 @@ func NewProxy(opts ...options.Option) proxy.Proxy {
// watch router service routes
p.errChan = make(chan error, 1)
go p.watchRoutes()
go func() {
// continuously attempt to watch routes
for {
// watch the routes
p.watchRoutes()
// in case of failure just wait a second
time.Sleep(time.Second)
}
}()
return p
}

View File

@@ -6,13 +6,15 @@ import (
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/network/router"
"github.com/micro/go-micro/router"
"github.com/micro/go-micro/server"
)
// 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
ServeRequest(context.Context, server.Request, server.Response) error
}
@@ -35,3 +37,20 @@ func WithClient(c client.Client) options.Option {
func WithRouter(r router.Router) options.Option {
return options.WithValue("proxy.router", r)
}
// WithLink sets a link for outbound requests
func WithLink(name string, c client.Client) options.Option {
return func(o *options.Values) error {
var links map[string]client.Client
v, ok := o.Get("proxy.links")
if ok {
links = v.(map[string]client.Client)
} else {
links = map[string]client.Client{}
}
links[name] = c
// save the links
o.Set("proxy.links", links)
return nil
}
}

View File

@@ -36,7 +36,13 @@ type cache struct {
ttls map[string]time.Time
watched map[string]bool
// used to stop the cache
exit chan bool
// status of the registry
// used to hold onto the cache
// in failure state
status error
}
var (
@@ -50,6 +56,18 @@ func backoff(attempts int) time.Duration {
return time.Duration(math.Pow(10, float64(attempts))) * time.Millisecond
}
func (c *cache) getStatus() error {
c.RLock()
defer c.RUnlock()
return c.status
}
func (c *cache) setStatus(err error) {
c.Lock()
c.status = err
c.Unlock()
}
// isValid checks if the service is valid
func (c *cache) isValid(services []*registry.Service, ttl time.Time) bool {
// no services exist
@@ -81,6 +99,11 @@ func (c *cache) quit() bool {
}
func (c *cache) del(service string) {
// don't blow away cache in error state
if err := c.status; err != nil {
return
}
// otherwise delete entries
delete(c.cache, service)
delete(c.ttls, service)
}
@@ -93,25 +116,38 @@ func (c *cache) get(service string) ([]*registry.Service, error) {
services := c.cache[service]
// get cache ttl
ttl := c.ttls[service]
// make a copy
cp := registry.Copy(services)
// got services && within ttl so return cache
if c.isValid(services, ttl) {
// make a copy
cp := registry.Copy(services)
// unlock the read
if c.isValid(cp, ttl) {
c.RUnlock()
// return servics
// return services
return cp, nil
}
// get does the actual request for a service and cache it
get := func(service string) ([]*registry.Service, error) {
get := func(service string, cached []*registry.Service) ([]*registry.Service, error) {
// ask the registry
services, err := c.Registry.GetService(service)
if err != nil {
// check the cache
if len(cached) > 0 {
// set the error status
c.setStatus(err)
// return the stale cache
return cached, nil
}
// otherwise return error
return nil, err
}
// reset the status
if err := c.getStatus(); err != nil {
c.setStatus(nil)
}
// cache results
c.Lock()
c.set(service, registry.Copy(services))
@@ -129,7 +165,7 @@ func (c *cache) get(service string) ([]*registry.Service, error) {
c.RUnlock()
// get and return services
return get(service)
return get(service, cp)
}
func (c *cache) set(service string, services []*registry.Service) {
@@ -283,6 +319,7 @@ func (c *cache) run(service string) {
}
d := backoff(a)
c.setStatus(err)
if a > 3 {
log.Log("rcache: ", err, " backing off ", d)
@@ -305,6 +342,7 @@ func (c *cache) run(service string) {
}
d := backoff(b)
c.setStatus(err)
if b > 3 {
log.Log("rcache: ", err, " backing off ", d)
@@ -325,20 +363,36 @@ func (c *cache) run(service string) {
// watch loops the next event and calls update
// it returns if there's an error
func (c *cache) watch(w registry.Watcher) error {
defer w.Stop()
// used to stop the watch
stop := make(chan bool)
// manage this loop
go func() {
defer w.Stop()
select {
// wait for exit
<-c.exit
w.Stop()
case <-c.exit:
return
// we've been stopped
case <-stop:
return
}
}()
for {
res, err := w.Next()
if err != nil {
close(stop)
return err
}
// reset the error status since we succeeded
if err := c.getStatus(); err != nil {
// reset status
c.setStatus(nil)
}
c.update(res)
}
}
@@ -369,7 +423,7 @@ func (c *cache) Stop() {
}
func (c *cache) String() string {
return "rcache"
return "cache"
}
// New returns a new cache

View File

@@ -13,14 +13,17 @@ import (
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
Client *consul.Client
Address []string
opts registry.Options
client *consul.Client
config *consul.Config
// connect enabled
connect bool
@@ -95,24 +98,33 @@ func configure(c *consulRegistry, opts ...registry.Option) {
}
// check if there are any addrs
if len(c.opts.Addrs) > 0 {
addr, port, err := net.SplitHostPort(c.opts.Addrs[0])
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 = c.opts.Addrs[0]
config.Address = fmt.Sprintf("%s:%s", addr, port)
addr = address
addrs = append(addrs, net.JoinHostPort(addr, port))
} else if err == nil {
config.Address = fmt.Sprintf("%s:%s", addr, port)
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)
@@ -123,12 +135,14 @@ func configure(c *consulRegistry, opts ...registry.Option) {
config.HttpClient.Timeout = c.opts.Timeout
}
// create the client
client, _ := consul.NewClient(config)
// set the config
c.config = config
// set address/client
c.Address = config.Address
c.Client = client
// remove client
c.client = nil
// setup the client
c.Client()
}
func (c *consulRegistry) Init(opts ...registry.Option) error {
@@ -148,7 +162,7 @@ func (c *consulRegistry) Deregister(s *registry.Service) error {
c.Unlock()
node := s.Nodes[0]
return c.Client.Agent().ServiceDeregister(node.Id)
return c.Client().Agent().ServiceDeregister(node.Id)
}
func (c *consulRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
@@ -193,7 +207,7 @@ func (c *consulRegistry) Register(s *registry.Service, opts ...registry.Register
if time.Since(lastChecked) <= getDeregisterTTL(regInterval) {
return nil
}
services, _, err := c.Client.Health().Checks(s.Name, c.queryOptions)
services, _, err := c.Client().Health().Checks(s.Name, c.queryOptions)
if err == nil {
for _, v := range services {
if v.ServiceID == node.Id {
@@ -204,7 +218,7 @@ func (c *consulRegistry) Register(s *registry.Service, opts ...registry.Register
} 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 {
if err := c.Client().Agent().PassTTL("service:"+node.Id, ""); err == nil {
return nil
}
}
@@ -237,6 +251,9 @@ func (c *consulRegistry) Register(s *registry.Service, opts ...registry.Register
}
host, pt, _ := net.SplitHostPort(node.Address)
if host == "" {
host = node.Address
}
port, _ := strconv.Atoi(pt)
// register the service
@@ -256,7 +273,7 @@ func (c *consulRegistry) Register(s *registry.Service, opts ...registry.Register
}
}
if err := c.Client.Agent().ServiceRegister(asr); err != nil {
if err := c.Client().Agent().ServiceRegister(asr); err != nil {
return err
}
@@ -272,7 +289,7 @@ func (c *consulRegistry) Register(s *registry.Service, opts ...registry.Register
}
// pass the healthcheck
return c.Client.Agent().PassTTL("service:"+node.Id, "")
return c.Client().Agent().PassTTL("service:"+node.Id, "")
}
func (c *consulRegistry) GetService(name string) ([]*registry.Service, error) {
@@ -281,9 +298,9 @@ func (c *consulRegistry) GetService(name string) ([]*registry.Service, error) {
// if we're connect enabled only get connect services
if c.connect {
rsp, _, err = c.Client.Health().Connect(name, "", false, c.queryOptions)
rsp, _, err = c.Client().Health().Connect(name, "", false, c.queryOptions)
} else {
rsp, _, err = c.Client.Health().Service(name, "", false, c.queryOptions)
rsp, _, err = c.Client().Health().Service(name, "", false, c.queryOptions)
}
if err != nil {
return nil, err
@@ -338,7 +355,7 @@ func (c *consulRegistry) GetService(name string) ([]*registry.Service, error) {
svc.Nodes = append(svc.Nodes, &registry.Node{
Id: id,
Address: fmt.Sprintf("%s:%d", address, s.Service.Port),
Address: mnet.HostPort(address, s.Service.Port),
Metadata: decodeMetadata(s.Service.Tags),
})
}
@@ -351,7 +368,7 @@ func (c *consulRegistry) GetService(name string) ([]*registry.Service, error) {
}
func (c *consulRegistry) ListServices() ([]*registry.Service, error) {
rsp, _, err := c.Client.Catalog().Services(c.queryOptions)
rsp, _, err := c.Client().Catalog().Services(c.queryOptions)
if err != nil {
return nil, err
}
@@ -377,6 +394,36 @@ 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{},

View File

@@ -50,22 +50,24 @@ func newConsulTestRegistry(r *mockRegistry) (*consulRegistry, func()) {
}
cfg := consul.DefaultConfig()
cfg.Address = l.Addr().String()
cl, _ := consul.NewClient(cfg)
go newMockServer(r, l)
return &consulRegistry{
Address: cfg.Address,
Client: cl,
opts: registry.Options{},
register: make(map[string]uint64),
lastChecked: make(map[string]time.Time),
queryOptions: &consul.QueryOptions{
AllowStale: true,
},
}, func() {
l.Close()
}
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 {

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