Compare commits

...

642 Commits

Author SHA1 Message Date
Asim Aslam
1222d076f2 There can be only one! (#1445)
* There can be only one

* fix proto?
2020-03-31 10:18:50 +01:00
ben-toogood
76ade7efd9 Auth - Swap Refresh to Token and change secrets to be strings, not tokens (#1444)
* Refresh => Token

* Secret is no longer a token

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-31 10:06:13 +01:00
c706ebe3fb auth proto: provide help to protoc-gen-go (#1442)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-31 00:35:11 +03:00
9e6db79860 regenerate all proto (#1440)
* regenerate all proto

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* regenerate from proto

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* regenerate from proto

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-30 21:58:32 +01:00
756b346672 auth/service: move all proto files to single dir (#1439)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-30 18:23:00 +03:00
ben-toogood
4db2f5e79d Add Namespace to Auth (#1438)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-30 09:51:37 +01:00
3d7d5ce6b4 api: add static router and improve path parser in rpc handler (#1437)
* api: add static router and improve path parser in rpc handler

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* expose metadata context key to be able to get unmodified map keys

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server/grpc: fix jsonpb codec for protobuf msg

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* api/handler/rpc: write 204 status code when rsp is nil

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* api/handler/rpc: add check for nil response for non javascript

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-30 09:04:59 +01:00
Socket
8282e781e4 grpc pool should check state (#1435)
Co-authored-by: huangshaojie <huangshaojie@corp.netease.com>
2020-03-28 08:48:25 +00:00
e4acc63d5f add mdns registry debug (#1434)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-27 22:33:49 +03:00
Asim Aslam
45ee5e9ad1 Move error for api validation to trace level (#1432)
* remove error on endpoint validation

* trace level
2020-03-27 14:01:47 +00:00
Asim Aslam
b60fde0e64 Pass through source and metadata in Update and Delete calls to runtime (#1431) 2020-03-27 11:37:12 +00:00
Lars Lehtonen
011a783a9e store/cockroach: fix dropped test errors (#1419)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-27 10:15:37 +00:00
ben-toogood
6723d17b22 Default auth, return account secret on Inspect (#1430)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-27 09:54:29 +00:00
ben-toogood
47c7181d41 Default Auth: Add blank secret to account to prevent nil errors (#1429)
* Remove debug auth logs

* Default auth, return account secret on Inspect

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-27 09:46:17 +00:00
b38da6ced0 api/handler/rpc: process all methods and merge url params to json body (#1427)
* api/handler/rpc: process all methods and merge url params to json body

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* add merge json test

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-27 07:59:31 +00:00
ben-toogood
1a53307a78 Remove debug auth logs (#1426)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 19:00:24 +00:00
Asim Aslam
e204f3e2e8 Add metadata Get method (#1425) 2020-03-26 18:50:00 +00:00
ben-toogood
329bd09f93 Fix Auth Init bug (#1424)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 18:09:51 +00:00
ben-toogood
4648fd0d09 Auth debugging (#1423)
* More auth debugging

* More auth debugging

* Increase auth debugging

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 17:55:35 +00:00
ben-toogood
c905df3be6 Log auth verify requests (#1422)
* More auth debugging

* More auth debugging

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 17:35:28 +00:00
Asim Aslam
62f9a054a4 100mb (#1421) 2020-03-26 16:57:31 +00:00
ben-toogood
00e7804f96 Auth - Add debugging to loading rules (#1420)
* Fix auth multi-rule edgecase

* Add logging to auth rules

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 16:30:31 +00:00
ben-toogood
42b6bf5bbf Fix auth multi-rule edgecase (#1418)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 15:52:48 +00:00
ben-toogood
844c456839 Refactor Auth Service Protos, Add Access Rules (#1411)
* Refactor auth/service into two protos

* Accounts Proto

* Store Prefixes

* Misc

* Tweak Protos

Co-authored-by: Ben Toogood <ben@micro.mu>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-26 13:12:43 +00:00
Asim Aslam
7182ca1fd0 fix server logging (#1417) 2020-03-26 13:08:06 +00:00
02839cfba5 api/handler: use http.MaxBytesReader and buffer pool (#1415)
* api/handler: use http.MaxBytesReader

protect api handlers from OOM cases

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-26 14:29:28 +03:00
Asim Aslam
776a7d6cd6 Update filter comment for proxy (#1416) 2020-03-26 08:05:00 +00:00
beaa434610 logger: fix reading env var (#1414)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-25 22:00:43 +00:00
Asim Aslam
6efc5556e5 use requested service (#1413) 2020-03-25 20:59:37 +00:00
Asim Aslam
8d0826a031 Add check for k8s condition (#1412) 2020-03-25 19:32:41 +00:00
ben-toogood
378d03eb66 Tidying up auth (#1410)
* Don't clear auth rules if request fails

* Add jitter to auth service loading rules

* Remove unused error from ContextWithToken result

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 18:34:13 +00:00
ben-toogood
56af826230 Update auth to pass seconds and not nanoseconds (#1409)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 17:03:45 +00:00
ben-toogood
511ebd8ec2 Fix Token Expiry Bug (#1408)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 14:40:37 +00:00
ben-toogood
1057ef6acb Add ContextWithToken (#1407)
* Add ContextWithToken

* Tidying up BearerScheme

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 11:20:53 +00:00
ben-toogood
35e2a68a98 Fix auth bug restricting access to unauthorised endpoints (#1405)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 10:31:33 +00:00
ben-toogood
0e56382107 Fix service level auth, add improved error descriptions to aid with debugging (#1403)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 09:35:29 +00:00
Jake Sanders
dff98355be Missing ; in SQL query 2020-03-24 23:49:09 +00:00
8100d26430 api/router/registry: use logger (#1402)
* api/router/registry: use logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* api/server/acme: use logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-24 20:45:11 +00:00
Jake Sanders
397a8638f4 Cockroach Store bugfix (#1401) 2020-03-24 17:16:38 +00:00
Jake Sanders
eb4d2ae6aa Remove useless variable from cockroach store (#1400) 2020-03-24 15:37:30 +00:00
Jake Sanders
914340585c Trim space from env variables (#1399) 2020-03-24 14:51:43 +00:00
ben-toogood
84b4eb5404 Fix missing loop (#1398)
* WithRoles variadic args

* Load Rules

* Timer => Ticker

* Add missing for loop in auth service

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-24 14:16:57 +00:00
ben-toogood
fd664f4392 Auth load rules (#1397)
* WithRoles variadic args

* Load Rules

* Timer => Ticker

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-24 13:48:37 +00:00
ben-toogood
86272a3064 WithRoles variadic args (#1395)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-24 10:18:34 +00:00
ben-toogood
c1978265ab Auth Wildcard Endpoints (#1394)
* Auth Wildcard Endpoints

* Fix joinkey bug, improve tests

* Change joinKey

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-24 09:39:33 +00:00
ben-toogood
e0e77f3983 Updated auth interface (#1384)
* Updated  auth interface

* Add Rule

* Remove Rule

* Return token from Renew

* Renew => Refresh

* Implement Tokens & Default Auth Implementation

* Change default auth to noop

* Change default auth to noop

* Move token.Token to auth.Token

* Remove Token from Account

* Auth service implementation

* Decode JWT locally

* Cookie for secret

* Move string to bottom of interface definition

* Depricate auth_exclude

* Update auth wrappers

* Update go.sum

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-23 16:19:30 +00:00
9826ddbd64 api/handler/rpc: log errors (#1390)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-23 10:31:35 +03:00
0987363
87617be227 Add client header to rpcRequest header; issue #957 (#1378)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-21 23:25:23 +00:00
d559587807 client/grpc: remove json-iterator usage (#1387)
* minimize external deps and binary size
* if user wants json-iterator codec it must be used in server and
  client code. so best to use it via go-plugins

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-21 09:40:58 +00:00
Janos Dobronszki
9501512219 Auth util func RequestToContext (#1386) 2020-03-20 15:23:12 +00:00
Asim Aslam
d2f153d795 Add type of service (#1385) 2020-03-20 12:48:12 +00:00
Asim Aslam
e49be1da42 fix local runtime (#1383) 2020-03-19 22:38:37 +00:00
Jake Sanders
4c6f68d537 Implement store read cache (#1366)
* Implement store read cache

* Added cache tests and fixed a bug in memory store
2020-03-19 18:19:07 +00:00
cbb958def5 config: fix panic on multiple Close() (#1374)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-19 12:54:59 +03:00
Asim Aslam
40ff6ddfcf sigh, further status changes (#1371) 2020-03-18 22:47:03 +00:00
Asim Aslam
5ad7c36bd4 Fix labels for k8s (#1370) 2020-03-18 22:13:21 +00:00
Asim Aslam
99c3fe2bb8 fix status parsing (#1368) 2020-03-18 21:50:52 +00:00
Asim Aslam
1bd340701b add k8s service ip to metadata (#1367)
* add k8s service ip to metadata

* go fmt

* use same port as container
2020-03-18 18:27:29 +00:00
Jake Sanders
c91bf7e9e7 [WIP] Store Sync (#1365)
* Initial cache implementation

* Write queue implementation

* Accidentally started writing the storage sync service
2020-03-18 16:39:36 +00:00
41f8a8cb00 errors: add FromError func (#1362)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-18 03:10:38 +03:00
ben-toogood
cd04111e3d Pass redirect_to param on auth (#1361)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-17 20:04:16 +00:00
ben-toogood
00cd2448a4 Fix bug where auth token is not set from cookie when excluded endpoint (#1360)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-17 19:24:10 +00:00
ben-toogood
8a41d369f2 Auth JWT ID Fix (#1359)
* Auth JWT ID Fix

* Remove unused ID in jwt claims

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-17 16:59:57 +00:00
Jake Sanders
638c219736 Cockroach store feature completion (#1358)
* Start fixing cockroach store

* Add prefix, suffix, limit, offset for cockroachdb store
2020-03-17 16:15:23 +00:00
ben-toogood
b3c631dd38 Support Wildcard Auth Excludes (#1357)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-17 16:03:49 +00:00
ab73127063 grpc client/server fixes (#1355)
* grpc client/server fixes

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-17 14:27:20 +03:00
Asim Aslam
03031a694d use pod phase/status (#1356) 2020-03-16 23:47:34 +00:00
li.peng
5712aafba9 fix: context cancel (#1350)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-16 10:45:33 +00:00
ac333d9d47 client/grpc: unwrap error after call (#1352)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-16 13:33:38 +03:00
ben-toogood
247707f583 Return store.ErrNotFound if not found when calling over rpc (#1353)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-16 10:30:56 +00:00
ben-toogood
d91c14eb30 grpc client error fix (#1351)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-16 12:53:15 +03:00
Asim Aslam
ca8684a886 fix k8s issues (#1349) 2020-03-15 15:09:18 +00:00
Asim Aslam
0449138f61 fix panic (#1348) 2020-03-14 21:18:41 +00:00
609f4826b3 server: remove duplicate code (#1346)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-13 22:15:09 +00:00
60993e6275 config/source/service: base64 fix (#1345)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-13 21:44:55 +00:00
Asim Aslam
e803fb0855 Runtime hacks (#1344)
* Add Args/Image to runtime

* remove the hacks
2020-03-13 18:39:59 +00:00
3543b275e0 fix log level helper (#1342)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-13 17:36:42 +03:00
fbde872e7f Revert "server/grpc: fix ordering of register and check for registered (#1338)" (#1341)
This reverts commit 62a644ddd8.
2020-03-13 09:30:44 +00:00
Asim Aslam
078dd4eb9b fix etcd (#1340)
* fix etcd

* update go mod
2020-03-13 08:55:23 +00:00
62a644ddd8 server/grpc: fix ordering of register and check for registered (#1338)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-12 22:04:11 +00:00
Asim Aslam
d8cfa7a295 add config to cmd (#1337)
* add config to cmd

* fix build
2020-03-12 18:47:40 +00:00
ben-toogood
47f1203e97 Add Config to service options (#1336)
Co-authored-by: Ben Toogood <ben@micro.mu>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-12 18:13:03 +00:00
Jake Sanders
1b4e881d74 Rewrite the store interface (#1335)
* WIP store rewrite

* Fix memory store tests

* Store hard expiry times rather than duration!

* Clarify memory test

* Add limit to store interface

* Implement suffix option

* Don't return nils from noop store

* Fix syncmap

* Start fixing store service

* wip service and cache

* Use _ for special characters in cockroachdb namespace

* Improve cockroach namespace comment

* Use service name as default store namespace

* Fixes

* Implement Store Scope

* Start fixing etcd

* implement read and write with expiry and prefix

* Fix etcd tests

* Fix cockroach store

* Fix cloudflare interface

* Fix certmagic / cloudflare store

* comment lint

* cache isn't implemented yet

* Only prepare DB staements once

Co-authored-by: Ben Toogood <ben@micro.mu>
Co-authored-by: ben-toogood <bentoogood@gmail.com>
2020-03-12 13:41:30 +00:00
ben-toogood
20ce61da5a Oauth google fixes (#1330)
* Fix Auth Headers

* Tweak Oauth to work for Google

Co-authored-by: Ben Toogood <ben@micro.mu>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-12 13:11:35 +00:00
chengguoqiang
eef4825be4 Update etcd.go (#1334)
add leaseId to the trace log
2020-03-12 10:09:38 +00:00
Asim Aslam
be9c6141f5 delete options (#1333) 2020-03-12 09:05:09 +00:00
Asim Aslam
1ca4619506 return store.ErrNotFound (#1332) 2020-03-11 23:09:42 +00:00
Asim Aslam
f55493993c set namespace rather than key (#1331) 2020-03-11 22:31:24 +00:00
7b385bf163 minimize allocations in logger and tunnel code (#1323)
* logs alloc

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* fix allocs

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* fix allocs

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* tunnel allocs

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* try to fix tunnel

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* cache cipher for send

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* more logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* more logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* more logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* more logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* more logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* more logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* more logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-11 17:55:39 +00:00
Jake Sanders
4125ae8d53 Add secrets interface to config/secrets (#1325)
* Interface for secrets

* Add secretbox secrets implementation

* Start working on box

* typo

* Add asymmetric encryption implementation

* go mod tidy

* Fix review comments

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-10 22:52:06 +00:00
ben-toogood
48b2a5c37c Fix Auth Headers (#1324)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-10 16:47:01 +00:00
Asim Aslam
ed83c27f0e add websocket streaming to api rpc handler (#1326) 2020-03-10 15:21:43 +00:00
241614ff68 add helper function to determine logger level (#1321)
* add helper function to determine logger level

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-09 23:43:05 +03:00
mlboy
1a4f608ed1 add: auth add generate options Expiry for set token expires (#1319)
Co-authored-by: mlboy <ml3@meitu.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-09 17:16:31 +00:00
43b0dbb123 tunnel: reduce allocation and improve performance (#1320)
* tunnel: reduce allocation and improve performance

BenchmarkSha256Old-16 100000 156748 ns/op 11835 B/op 168 allocs/op
BenchmarkSha256Old-16 100000 156229 ns/op 11819 B/op 168 allocs/op

BenchmarkSha256New-16 100000 154751 ns/op 11107 B/op 161 allocs/op
BenchmarkSha256New-16 100000 154263 ns/op 11110 B/op 161 allocs/op

simple change lowers allocations and brings performance

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* fix

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* tunnel: reuse buf in Decrypt

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* fix unneeded conversations

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* base32 string is smaller than hex string

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-09 17:10:08 +00:00
ben-toogood
b344171c80 URL Encode Provider.Endpoint() (#1317)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-09 10:21:49 +00:00
ben-toogood
e3ce45495a os.Exit on log.Fatal (#1316)
* os.Exit on log.Fatal

* Fix TestOptions

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-09 09:23:42 +00:00
f01664a551 Merge pull request #1313 from micro/upstream
fix ipv6 address usage in mdns registry and util/addr
2020-03-07 23:50:03 +03:00
8ecbdc1cd6 registry/mdns: add logging for invalid endpoint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-07 23:19:48 +03:00
55c19afb0b registry/mdns: fix ipv6 addr in mdns registry
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-07 23:17:00 +03:00
077063c212 util/addr: check ip addrs before return
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-07 23:13:56 +03:00
ben-toogood
9a7a65f05e Auth Provider (#1309)
* auth provider mock interface

* Auth Provider Options

* Implement API Server Auth Package

* Add weh utils

* Add Login URL

* Auth Provider Options

* Add auth provider scope and setting token in cookie

* Remove auth_login_url flag

Co-authored-by: Asim Aslam <asim@aslam.me>
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-07 11:06:57 +00:00
8ee5607254 [WIP]: broker ErrorHandler option (#1296)
* broker ErrorHandler option

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* rewrite Event interface, add error

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* implement new interface

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* change ErrorHandler func to broker.Handler

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* fix

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-06 21:25:16 +00:00
11be2c68b9 util/stream: fix imports (#1310)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-06 21:17:57 +00:00
a864f812f1 web: fix ipv6 address issue (#1308)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-06 18:44:56 +03:00
Asim Aslam
ae60bea8d8 add stream fix (#1305) 2020-03-06 14:40:47 +00:00
Jake Sanders
a851b9db7a Comment typo in gRPC subscriber (#1304) 2020-03-05 14:55:46 +00:00
d807dac2a7 server/grpc: avoid panic in case of nil Header (#1303)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-05 17:45:31 +03:00
ce2ba71002 server: subscribe to topic with own name if router not nil (#1295)
* server: subscribe to topic with own name if router not nil

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-05 10:29:50 +03:00
Asim Aslam
67c26c71b6 add jitter (#1298) 2020-03-04 15:37:17 +00:00
ben-toogood
9386f36a13 Exit on log.Fatal (#1297) 2020-03-04 13:46:01 +00:00
ben-toogood
6d803d9e45 Implement api/server/cors (#1294) 2020-03-04 11:40:53 +00:00
ben-toogood
6a9001bdb1 Set auth account in context (#1293) 2020-03-04 09:54:52 +00:00
Jake Sanders
3f0c28a815 Expiration is actually a unix timestamp (#1290)
* Expiration is actually a unix timestamp

* int -> int64
2020-03-03 18:15:50 +00:00
Asim Aslam
49ffc60afb Use Foo.Call on /foo (#1286)
Co-authored-by: Jake Sanders <i@am.so-aweso.me>
2020-03-03 16:47:15 +00:00
Jake Sanders
beb5e80e87 Fix nil pointer dereference (#1289) 2020-03-03 13:54:56 +00:00
Jake Sanders
eebd69c995 Change from renekroon/ttlcache to patrickmn/go-cache (#1288) 2020-03-03 13:35:49 +00:00
Jake Sanders
bc71989e2c int64 -> time.Duration (#1287) 2020-03-03 13:15:26 +00:00
89ba602e17 logger fixes and improvements (#1285)
* fix helper fields
* add metadata output for default logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-03 11:07:37 +03:00
Jake Sanders
f6102bde70 Add a cache to workers KV storage implementation (#1284)
* cloudflare-cache

* go mod tidy
2020-03-02 18:14:25 +00:00
Pieter Voorwinden
7cad77bfc0 Initialize header to prevent assignment to entry in nil map error (#1282)
Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-02 19:17:26 +03:00
ben-toogood
1f2e067f71 k8s runtime - get status from pods (#1283) 2020-03-02 15:49:10 +00:00
b555269b1b copy fields in helper (#1281)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-02 14:18:36 +00:00
ben-toogood
9200c70202 Replace validation error with regex for cockroach namespace (#1270)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-01 22:09:06 +00:00
Sumanth Chinthagunta
d8377e09c9 feat(dockerfile): adding dumb-init to base image (#1278) 2020-02-29 21:55:15 +00:00
0754229878 broker/memory: add codec support (#1276)
allow easy testing of other services with memory broker
and also allows to more deeply simulate real brokers

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-29 23:00:29 +03:00
6b8930a960 add new helper method to logger (#1273)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-29 00:39:41 +00:00
d0a978bd50 redesign logger (#1272)
* redesign logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-29 03:31:59 +03:00
ben-toogood
afe6861e2f Update the k8s deployment to use metadata labels & custom source (#1271) 2020-02-28 15:07:55 +00:00
ben-toogood
962567ef42 Implement config singleton (#1268)
* Implement config singleton

* Pass token in grpc request headers

* Refactor BearerScheme

* Fix typo
2020-02-28 12:58:27 +00:00
Asim Aslam
e21ed3a183 gen account on base32 decode failure (#1269) 2020-02-27 16:11:05 +00:00
64a5ce9607 various fixes (#1267)
* logger: remove Panic log level

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server/grpc: add missing Unlock in Subscribe error

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server: minor code change

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server/grpc: extend test suite with pub/sub testing

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server/grpc: fix invalid check and allow subscriber error to be returned

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server/grpc: add pubsub tests

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* client/grpc: check for nil req/rsp

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-26 18:34:40 +00:00
Asim Aslam
d651b16acd generate pseudo accounts (#1264)
* generate pseudo accounts

* when you think you're being clever

* return garbage pseudo account when no token
2020-02-26 13:42:32 +00:00
Eric
1034837f69 Adjusting the BeforeStart () position (#1263)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-02-26 11:44:10 +03:00
Lars Lehtonen
80f2bfd5d0 config: remove unused sep variable (#1262) 2020-02-26 08:25:33 +00:00
Asim Aslam
6aaaf54275 add MICRO_AUTH_TOKEN, parse token in wrapper, preload config and othe… (#1261)
* add MICRO_AUTH_TOKEN, parse token in wrapper, preload config and other things

* fix wrapper panic
2020-02-25 22:15:44 +00:00
Di Wu
603d37b135 Set option and cli args to the service profile (#1259) 2020-02-25 16:42:42 +00:00
Eric
53c3bff819 add Panic & Panicf to logger (#1258)
* add Panic & Panicf to logger
2020-02-25 17:44:29 +03:00
ben-toogood
dcf859098b Fix k8s commands for github (#1257) 2020-02-25 11:39:03 +00:00
b4a743898e fix router panic (#1254)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-24 23:15:59 +00:00
ben-toogood
f1e7ea3020 Handle non IsNotExist errors in config (#1251)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-02-24 18:07:11 +00:00
ben-toogood
5e8d5834eb Dynamic Runtime source for k8s with github packages (#1252)
* Dynamic Runtime source for k8s

* Still check for source

* Replace / with - for k8s service names

* Simplify sourceForName function
2020-02-24 17:47:47 +00:00
ben-toogood
ffdf986aac Refactor auth: add token and store implementations (#1230)
* Refactor auth: add token and memory implementations

* Fix typo

* Remove memory auth (implemented already by the store implementation), revert default to noop

* Add grpc header

* Global Config

* config/global => util/config

* Rename package to remove confict

* Tweak

* Improve Error Handling
2020-02-24 15:07:27 +00:00
Jake Sanders
56f8115ea8 Rename PR job (#1250) 2020-02-24 14:16:51 +00:00
5b0175c2e5 allocations improvements and tunnel fixes (#1248)
* reduce allocations in tunnel code

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* another allocation fix

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* allocate maps with len if it known

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* allocate key for send once

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-24 14:15:20 +00:00
Jake Sanders
01d88601c0 Split PR and merge tests (#1249) 2020-02-24 14:11:17 +00:00
Lars Lehtonen
d467236f8f broker/nats: remove unused setPublishOption() (#1234)
broker/nats: remove unused setSubscribeOption()
2020-02-24 13:49:27 +00:00
24d574ae71 server/grpc: add MaxConn option to limit max inflight requests (#1247)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-24 13:48:56 +00:00
cf0b39eaac logger fixes (#1244)
* logger: fix race conditions

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* restore util/log for compatibility

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-24 13:07:40 +00:00
1f767ba18c update go modules (#1240)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-23 20:47:44 +00:00
Asim Aslam
915c424213 Add docker build (#1239) 2020-02-23 15:57:21 +00:00
117f56ebf7 prune util/log and user logger (#1237)
* prune util/log and user logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* plaintext logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* add newline

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-23 13:45:20 +00:00
Gao.QiLin
ceed8942fc Update README.md change godoc => go.dev (#1236)
* Update README.md add go dev

* Update README.zh-cn.md add go dev
2020-02-22 08:56:42 +00:00
d1e25e7ead add metadata set method (#1232)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-21 23:04:47 +03:00
Jake Sanders
7e24c0c1cf Also run tests on PR (#1233) 2020-02-21 17:57:07 +00:00
Jake Sanders
ca251ba111 Switch from Travis CI to GitHub Actions (#1231)
* Create Github Action to run tests on push

* -v

* Update tests.yml

* TODO: Fix tests

* Fix colours

* Delete .travis.yml

* Update tests.yml

* builds -> build
2020-02-21 17:43:50 +00:00
Asim Aslam
116855572b Add log level helper funtions (#1229) 2020-02-21 08:43:23 +00:00
Asim Aslam
ee977acfef strip SetGlobalLevel (#1228) 2020-02-21 08:28:21 +00:00
Sumanth Chinthagunta
3fa7c26946 logger with helper methods (#1216)
* support unix daemon socket

* refactor(logger): logger fields changed to map[string]interface{}

* improvement(logger): adding string to Level Parser

* improvement(logger): rename ParseLevel to GetLevel

* refactor(logger): adding basic logger

adding micro default logger, and refactor logger interface

* refactor(logger): moved basic logger to top level package

* refactor(logger): adding default logger
2020-02-21 07:57:59 +00:00
Lars Lehtonen
88457b812e tunnel: Prune Unused Functions (#1224)
* tunnel: remove unused link.setLoopback()

* tunnel: remove unused link.accept()

* tunnel: remove unused link.connect()
2020-02-20 17:05:49 +00:00
Asim Aslam
78df154a4d move log level setting to option (#1222) 2020-02-20 08:26:12 +00:00
Lars Lehtonen
c7eed618c2 server/grpc: Prune Unused Code (#1220)
* server/grpc: remove unused grpcServer.newCodec()

* server/grpc: remove unused defaultRPCCodecs
2020-02-19 20:58:22 +00:00
ben-toogood
36bcd3bd82 Improve JWT Package Errors (#1206)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-02-19 09:51:43 +01:00
Asim Aslam
f4118dc357 secure the grpc client (#1215)
* secure the grpc client

* make compile

* Add system cert pool

* Revert manually adding ca certs

* Tweak comment

Co-authored-by: ben-toogood <bentoogood@gmail.com>
2020-02-19 08:44:35 +00:00
58598d0fe0 fixes for safe conversation and avoid panics (#1213)
* fixes for safe convertation

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* fix client publish panic

If broker connect returns error we dont check it status and use
it later to publish message, mostly this is unexpected because
broker connection failed and we cant use it.
Also proposed solution have benefit - we flag connection status
only when we have succeseful broker connection

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* api/handler/broker: fix possible broker publish panic

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-18 23:05:38 +00:00
6248f05f74 add missing option to client.NewMessage (#1212)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-18 14:18:59 +03:00
ben-toogood
aa9a0a8d23 Fix Micro Proxy nil Transport Bug (#1208) 2020-02-17 12:28:48 +03:00
ben-toogood
1e40c86dfe Ignore gRPC Proxy (#1205) 2020-02-17 08:14:45 +00:00
Asim Aslam
9696efde02 reorder auth interface (#1204) 2020-02-16 19:36:45 +00:00
Asim Aslam
b3fc8be24e normalise proxy name (#1203) 2020-02-15 21:57:30 +00:00
Sumanth Chinthagunta
fc5339a368 [W.I.P] refactor(logger): logger fields changed to map[string]interface{} (#1198)
* support unix daemon socket

* refactor(logger): logger fields changed to map[string]interface{}

* improvement(logger): adding string to Level Parser

* improvement(logger): rename ParseLevel to GetLevel
2020-02-15 18:19:28 +00:00
Asim Aslam
964b7dee3f add tls config to server (#1202)
* add tls config

* add TLSConfig to acme provider
2020-02-15 15:10:26 +00:00
Asim Aslam
158949d0d0 accept Listen option in grpc server (#1201) 2020-02-15 14:09:24 +00:00
Asim Aslam
eed8a0bf50 delete proxy cached route before updating (#1200) 2020-02-15 12:05:22 +00:00
Asim Aslam
c691d116ab when the stream errors cleanup the connection (#1199) 2020-02-15 11:35:08 +00:00
Eric
cbe8b7dd09 Removed redundant spaces (#1196) 2020-02-14 10:32:02 +03:00
Asim Aslam
203486fd31 check for etcd watcher canceled value 2020-02-13 22:34:56 +00:00
Asim Aslam
d9b3b17582 set dial timeout in stream 2020-02-13 18:51:32 +00:00
ben-toogood
e080ecb43a Auth Improvements (#1195)
* Exclude Stats & Trace from Auth

* Update Excluded Endpoints Format

* Tweak Implementation
2020-02-13 14:07:14 +00:00
ben-toogood
ea70711dd3 Exclude Stats & Trace from Auth (#1192) 2020-02-13 12:02:29 +00:00
6dc942bc19 client/grpc: fix panic on invalid message (#1191)
* client/grpc: fix panic on invalid message
* travis: disable lint and race for now

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-13 14:57:21 +03:00
Janos Dobronszki
d76baf59de Trace type is now being recorded (#1188) 2020-02-12 10:57:17 +00:00
79ad1e6fe3 various fixes for broker and messaging in server (#1187)
* provide broker disconnect messages in server

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* broker/eats: another fix

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-11 18:41:23 +00:00
2764de9a1a broker/eats: broker disconnect fix (#1186)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-11 15:46:50 +00:00
Asim Aslam
d1d6eada98 parse url encoded form in rpc handler (#1183)
* parse url encoded form in rpc handler

* Remove comment
2020-02-11 11:27:16 +00:00
ben-toogood
4a03183481 Return a 401 error on invalid auth tokens (#1184) 2020-02-11 11:22:22 +00:00
Asim Aslam
8ea84ac3eb Fix router panic for nil watcher 2020-02-10 15:38:41 +00:00
ben-toogood
4401c12e6c Auth Wrapper (#1174)
* Auth Wrapper

* Tweak cmd flag

* auth_excludes => auth_exclude

* Make Auth.Excludes variadic

* Use metadata.Get (passes through http and http2 it will go through various case formats)

* fix auth wrapper auth.Auth interface initialisation

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-02-10 08:26:28 +00:00
c706afcf04 logger helper to pass down it via context (#1180)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-09 21:26:46 +00:00
Lars Lehtonen
ca1d0b94c3 config/cmd: remove 8 unused variables (#1175) 2020-02-08 11:19:10 +00:00
67acd9288b config/source/cli: fix tests (#1179)
* config/source/cli: fix tests
* skip mdns test in travis

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-08 02:45:32 +03:00
0bf6c9fc08 config/source/cli: fix default flag value loading (#1178)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-08 02:14:34 +03:00
99807a680c strip Micro-Topic header from incoming context in client.Call (#1177)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-07 22:09:52 +00:00
f0f7f860d6 add some docs (#1176)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-07 21:48:24 +00:00
Asim Aslam
ef537270ad Don't store traces for Debug endpoints 2020-02-07 20:58:03 +00:00
Asim Aslam
c7f075d157 rename file 2020-02-07 14:00:27 +00:00
Asim Aslam
812ea78604 Fix runtime deadlock 2020-02-07 14:00:09 +00:00
Asim Aslam
0755084a59 fix deadlock 2020-02-07 13:55:55 +00:00
ben-toogood
fe7f5a4134 Runtime Retries Limit (#1163)
* Implement Runtime Retries

* Remove Debug

* Action Feedback

* Refactor Retries

* Fix WithRetires Typo
2020-02-07 12:02:41 +00:00
ben-toogood
19c454ec4b Fix Local Runtime Default Command (#1173)
* Auth API Proto

* Fix local runtime bug

* Add Platform Proto

* Restructuring
2020-02-07 11:39:26 +00:00
Asim Aslam
0e9b4c26a4 import with braces 2020-02-06 21:39:08 +00:00
Asim Aslam
d40b13a045 mux to mtx 2020-02-06 21:37:17 +00:00
Asim Aslam
4079b22c1e reorder logger methods 2020-02-06 21:36:33 +00:00
Shu xian
fdfb2bc4c1 [WIP] logger first (#1161)
* logger first

* log->logger

* update comment

* add context in Options

* add Fields

* remove logfi

* add field encode

* add common Field Types

* update logger field
2020-02-06 21:35:46 +00:00
Asim Aslam
dbeb7cfe9c remove errors import 2020-02-06 18:45:12 +00:00
Asim Aslam
512df2628f trim source url if its set to github.com/ 2020-02-06 18:34:16 +00:00
Janos Dobronszki
92571db693 Tracing: traces now correctly form a tree (#1170)
* First cut of trace

* Dial it back yo

* Defensive programming
2020-02-06 17:22:16 +00:00
ben-toogood
16552620e0 Runtime Custom Source (Part 2) (#1169) 2020-02-06 16:16:01 +00:00
Ben Toogood
5414195dc3 Add Args 2020-02-06 12:31:54 +00:00
Ben Toogood
0591760932 Arg => Args 2020-02-06 12:17:16 +00:00
Ben Toogood
48b9f3f5e9 Fix 2020-02-06 11:28:34 +00:00
Ben Toogood
e46278a766 Test 2020-02-06 11:24:56 +00:00
Ben Toogood
2925b1615c Fix 2020-02-06 11:15:30 +00:00
Ben Toogood
fc4191c647 Fix 2020-02-06 11:12:40 +00:00
Ben Toogood
111126c780 Debugging 2020-02-06 11:00:14 +00:00
Ben Toogood
54371bba6a Debugging 2020-02-06 10:55:41 +00:00
Ben Toogood
c28737e88e Debugging 2020-02-06 10:54:11 +00:00
Ben Toogood
c7d922fac2 Debugging 2020-02-06 10:49:01 +00:00
Ben Toogood
f8e696bd30 Debugging 2020-02-06 10:44:12 +00:00
Ben Toogood
6aef28dad2 Runtime set service source 2020-02-06 10:33:23 +00:00
7105e4099c pass micro errors from grpc server to grpc client (#1167)
* pass micro errors from grpc server to grpc client

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* wrap micro errors.Error to grpc status

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-06 10:18:33 +00:00
Ben Toogood
f0762bbb6b Improve Logging 2020-02-06 10:16:32 +00:00
Ben Toogood
243c6a4246 Debug 2020-02-06 10:08:56 +00:00
Ben Toogood
9983aea928 Tidying Up 2020-02-06 09:29:27 +00:00
Ben Toogood
aa58a9749b Action Asim's Feedback 2020-02-06 09:17:10 +00:00
ben-toogood
d8110b70a3 Runtime custom docker img (#1168)
* Add DeploymentOptions to K8s Client

* WithBaseImage for  Runtime

* Revert Change

* Fix sequencing
2020-02-06 08:52:25 +00:00
a44dc90d45 fix ctx.Done issue #720 (#1166)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-05 21:13:14 +00:00
Shu xian
12181bd441 fix LevelInfo > LevelWarn (#1162) 2020-02-05 18:16:57 +00:00
Ben Toogood
8d44f7226f Merge branch 'master' of https://github.com/micro/go-micro 2020-02-05 13:59:51 +00:00
ben-toogood
bf747a86f4 Runtime (#1160)
* Add String to Runtime interface

* Setup Dynamic Runtime Configuration
2020-02-05 13:59:35 +00:00
Lars Lehtonen
4333f00a43 runtime/kubernetes: remove unused constants (#1159) 2020-02-04 21:02:05 +00:00
7ab3a31ac4 update micro/cli, tidy mod (#1156)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-03 23:29:44 +00:00
Ben Toogood
a1d5d6831f Add String to Runtime interface 2020-02-03 15:56:16 +00:00
ben-toogood
1b9cabd654 Update Micro Auth Protocol Buffer to use V2 (#1155) 2020-02-03 08:26:57 +00:00
ben-toogood
d621548120 Auth (#1147)
Implement the Auth interface, with JWT and service implementations.

* Update Auth Interface

* Define Auth Service Implementation

* Support Service Auth

* Add Auth Service Proto

* Remove erronious files

* Implement Auth Service Package

* Update Auth Interface

* Update Auth Interface. Add Validate, remove Add/Remove roles

* Make Revoke interface more explicit

* Refactor serializing and deserializing service accounts

* Fix srv name & update interface to be more explicit

* Require jwt public key for auth

* Rename Variables (Resource.ID => Resource.Name & ServiceAccount => Account)

* Implement JWT Auth Package

* Remove parent, add ID

* Update auth imports to v2. Add String() to auth interface
2020-02-03 08:16:02 +00:00
tpam28
449bcb46fe New backoff (#1153)
* new backoff function

* use backoff from util/backoff

* remove reset atemts

* change comment

* fmt
2020-02-02 20:32:55 +00:00
Asim Aslam
079102ea59 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-02-02 19:50:09 +00:00
Asim Aslam
a9d371e727 fatal on command error 2020-02-02 19:49:59 +00:00
Asim Aslam
27efbc8779 Merge pull request #1150 from unistack-org/grpc_race
fix map race condition in grpc server
2020-02-01 00:10:46 +00:00
efb59d9709 fix map race condition in grpc server
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-01 02:52:53 +03:00
Asim Aslam
003f00b483 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-01-30 23:24:54 +00:00
Asim Aslam
0be22c98c6 add tracing 2020-01-30 23:24:46 +00:00
Asim Aslam
bafedf1aad Merge pull request #1149 from alrs/remove-unused-kubernetes-variable
runtime/kubernetes: remove unused name variable
2020-01-30 17:30:43 +00:00
Lars Lehtonen
98d55545fd runtime/kubernetes: remove unused name variable 2020-01-30 09:25:50 -08:00
Asim Aslam
5f6271b044 Merge pull request #1148 from tpam28/master
fix test and description
2020-01-30 17:09:41 +00:00
Evgeniy
87753ad289 format results in TestBacloff 2020-01-30 20:08:03 +03:00
Evgeniy
ffb9da0230 fix test and description 2020-01-30 19:43:03 +03:00
Asim Aslam
50ac642666 Merge pull request #1146 from tpam28/master
exponentialBackoff was changed from power function to exponential
2020-01-30 14:35:17 +00:00
Evgeniy
f6fcfcb8fc exponentialBackoff was changed from power function to exponential function 2020-01-30 17:25:07 +03:00
Asim Aslam
21e0932339 Merge pull request #1144 from unistack-org/v2
fix import paths for v2 release
2020-01-30 11:46:29 +00:00
f23638c036 fix import paths for v2 release
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-01-30 14:44:40 +03:00
Asim Aslam
2b1e0a6fd6 go mod tidy 2020-01-30 10:33:40 +00:00
Asim Aslam
0889b814bb Merge pull request #1143 from crufter/bump-dep
Bump dep to fix platform web using new debug.Stats proto fields
2020-01-30 10:31:12 +00:00
Janos Dobronszki
a648c0d99c Bump dep to fix platform web using new debug.Stats proto fields 2020-01-30 10:27:21 +00:00
Asim Aslam
a291af59c4 Merge pull request #1142 from micro/tracing
Fix null tracer bug
2020-01-30 07:55:58 +00:00
Ben Toogood
d9f12731e1 Fix null tracer bug 2020-01-30 07:53:31 +00:00
Asim Aslam
4a838a8210 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-01-29 23:14:16 +00:00
Asim Aslam
5969cc358e nats-e => eats 2020-01-29 23:14:05 +00:00
Asim Aslam
9f1a7e1139 Merge pull request #1127 from Allenxuxu/master
handle Loader.Load  return value error
2020-01-29 22:44:29 +00:00
Asim Aslam
dc257f5066 update to fix tracer 2020-01-29 22:43:40 +00:00
Asim Aslam
49b86c56e3 go fmt 2020-01-29 22:40:43 +00:00
Asim Aslam
1be8258721 fix initialisation 2020-01-29 22:39:31 +00:00
Asim Aslam
b2980aecb7 fix debug handler in proxy 2020-01-29 22:31:57 +00:00
Asim Aslam
de3c3b27b2 Merge pull request #1140 from micro/tracing
Tracing by Ben Toogood
2020-01-29 22:28:49 +00:00
Micro
a09eea8d4d Update the Debug Handler to use the servers tracer 2020-01-29 16:05:58 +00:00
Micro
62c067adcd Refactor debug/trace ready for Jaeger 2020-01-29 15:45:11 +00:00
Asim Aslam
59f6ca5564 Merge pull request #1139 from alrs/prune-unused-config-reader-func
config/reader/json: remove unused newValue()
2020-01-28 19:50:03 +00:00
Lars Lehtonen
895aa896bc config/reader/json: remove unused newValue() 2020-01-28 11:38:55 -08:00
Asim Aslam
74762edc42 Merge pull request #1138 from printfcoder/master
cockroach store supports URL connection string
2020-01-28 17:59:49 +00:00
shu xian
101017a29c cockroach supports URL connection string 2020-01-29 00:47:41 +08:00
Shu xian
c725b4c797 Merge pull request #3 from micro/master
merge
2020-01-29 00:45:27 +08:00
Asim Aslam
1108cc5e91 Merge pull request #1136 from unistack-org/flags
update micro/cli to urfave/cli/v2 and fix go-micro
2020-01-26 19:12:06 +00:00
Asim Aslam
6a9f5fac61 fire send in a go routine to prevent blocking other requests 2020-01-25 23:16:00 +00:00
e6a34bcbe7 update micro/cli to urfave/cli/v2 and fix go-micro
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-01-25 15:57:08 +03:00
Asim Aslam
1d00f2f771 add trace handler 2020-01-24 22:02:35 +00:00
Asim Aslam
4a6570a772 trace wrapper 2020-01-24 21:58:29 +00:00
Asim Aslam
a997a86e49 add trace context 2020-01-24 21:44:48 +00:00
Asim Aslam
2d28ff72d7 add trace 2020-01-24 21:32:07 +00:00
Asim Aslam
2b1844971c go fmt 2020-01-24 21:31:57 +00:00
Asim Aslam
49cc022ca2 add trace to handler 2020-01-24 21:29:29 +00:00
Asim Aslam
8d2dc8a822 trace Read endpoint 2020-01-24 21:24:51 +00:00
Asim Aslam
eeb6944ce5 Merge pull request #1133 from alrs/drop-unused-broker-code
broker: drop unused registryKey variable
2020-01-23 21:57:58 +00:00
Lars Lehtonen
add78f2967 broker: drop unused registryKey variable 2020-01-23 12:51:57 -08:00
Shu xian
bd6a5af2b5 Merge pull request #2 from micro/master
merge
2020-01-23 22:17:54 +08:00
Asim Aslam
ae08e9c106 check if event is nil 2020-01-23 12:41:22 +00:00
Asim Aslam
77c2a021da Add event id to router events 2020-01-23 11:44:06 +00:00
Asim Aslam
1c19678d04 Update proto Service => Config 2020-01-23 11:37:54 +00:00
Asim Aslam
6d636b7ab3 whoa bad commit, bad asim 2020-01-22 17:07:56 +00:00
Asim Aslam
009c598049 Change version to latest 2020-01-22 17:03:38 +00:00
Asim Aslam
5cceb00df2 Merge pull request #1131 from micro/router-refactor
refactor and cleanup some router code
2020-01-22 16:54:20 +00:00
Asim Aslam
3e24276eb1 fix break 2020-01-22 16:44:34 +00:00
Asim Aslam
29c1076950 refactor and cleanup some router code 2020-01-22 16:33:31 +00:00
Asim Aslam
8dbacb34f8 Merge pull request #1118 from printfcoder/master
add mucp for config/source
2020-01-21 22:35:35 +00:00
Asim Aslam
8b306780ee Merge pull request #1130 from micro/net-noloop
avoid connecting to self
2020-01-21 15:05:33 +00:00
Asim Aslam
9f7d374691 avoid connecting to self 2020-01-21 12:36:05 +00:00
Asim Aslam
a18e53f028 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-01-20 21:31:16 +00:00
Asim Aslam
2208839027 Merge pull request #1129 from alrs/remove-unused-server-var
server: remove unused invalidRequest
2020-01-20 18:24:42 +00:00
Lars Lehtonen
7a17a221ff server: remove unused invalidRequest 2020-01-20 10:09:27 -08:00
Shu xian
8e1ff80b9e Merge pull request #1 from micro/master
merge
2020-01-20 20:08:21 +08:00
shu xian
94bb0f4c08 watch supports path 2020-01-20 18:31:18 +08:00
Allenxuxu
dbe209ebe4 Merge branch 'master' of https://github.com/micro/go-micro 2020-01-20 08:37:34 +08:00
Asim Aslam
fa0d884cfe fix bad import 2020-01-19 23:31:09 +00:00
Asim Aslam
ed2bd68d28 fix break in router service 2020-01-19 23:22:41 +00:00
Asim Aslam
97928e88f8 stop watcher 2020-01-19 23:15:57 +00:00
Asim Aslam
04cf86070c close stream 2020-01-19 22:55:57 +00:00
Asim Aslam
9df19e826e cancel stream 2020-01-19 22:53:56 +00:00
Asim Aslam
3f3c1919f4 strip certain plugins 2020-01-19 17:56:59 +00:00
Asim Aslam
d918694346 reorganise runtime 2020-01-19 17:47:27 +00:00
Asim Aslam
54fb61bba4 Move proto to service/ 2020-01-19 17:31:24 +00:00
Asim Aslam
fac75866d9 Move pool to util 2020-01-19 17:30:49 +00:00
Asim Aslam
a47ff65529 remove mock client 2020-01-19 17:24:22 +00:00
Asim Aslam
093bcedfe7 remove http broker 2020-01-19 17:21:55 +00:00
Asim Aslam
7b6d560bec Update README.md 2020-01-19 13:55:23 +00:00
Asim Aslam
43aa4e23bd update readme 2020-01-19 13:51:31 +00:00
Asim Aslam
bdd9ec560b strip sub comments 2020-01-19 13:47:14 +00:00
Asim Aslam
0c03bf064b only connect broker if there are subscribers 2020-01-19 13:45:28 +00:00
Asim Aslam
9a8c1b7ab8 update broker 2020-01-19 13:35:23 +00:00
Asim Aslam
e1ca40c1fc go fmt 2020-01-19 13:32:30 +00:00
Asim Aslam
e75b99f89c go fmt 2020-01-19 13:32:24 +00:00
Allenxuxu
ee7304a795 NewConfig return value error 2020-01-19 16:42:05 +08:00
Allenxuxu
a82fd19209 handle Loader.Load return value error 2020-01-19 16:31:02 +08:00
Asim Aslam
1983d607f3 set nats-e 2020-01-19 01:47:30 +00:00
Asim Aslam
10093a0ea2 set to nats-e 2020-01-19 01:29:00 +00:00
Asim Aslam
fc08a9146c Add broker comments on server subscribe 2020-01-19 01:16:36 +00:00
Asim Aslam
cafd280718 Default to grpc in registry service for now 2020-01-19 01:13:14 +00:00
Asim Aslam
11b104677a Shift embedded nats to the default 2020-01-19 00:55:01 +00:00
Asim Aslam
105596a0e5 use mucp server 2020-01-18 20:48:08 +00:00
Asim Aslam
0a37767127 Fix service registration with registry service 2020-01-18 19:53:51 +00:00
Asim Aslam
31e195bac7 strip image pull policy always 2020-01-18 18:37:38 +00:00
Asim Aslam
bdf1d20f4e extract an ip that can be advertised in embedded nats 2020-01-18 15:39:26 +00:00
shu xian
8d6f82707a update to standard name convention 2020-01-18 23:16:23 +08:00
Asim Aslam
058fd8adbf trace 1 2020-01-18 10:20:46 +00:00
Asim Aslam
13d1d2fa08 hard stop if graceful stop fails after 1 second 2020-01-18 10:18:23 +00:00
Asim Aslam
e666d0b807 add missing commit 2020-01-18 02:28:44 +00:00
Asim Aslam
39d7938405 Extract k8s run error 2020-01-18 02:13:24 +00:00
Asim Aslam
65df711b01 move nats local logic 2020-01-18 01:29:53 +00:00
Asim Aslam
fd6eb23307 do not wait to stop 2020-01-18 00:58:27 +00:00
Asim Aslam
fb3927fb8c Merge pull request #1126 from milosgajdos83/metric-update
Update route metric when receiving Sync routes
2020-01-17 18:29:55 +00:00
Milos Gajdos
891af703be Update route metric when receiving Sync routes 2020-01-17 18:24:36 +00:00
Asim Aslam
474472eedd Merge pull request #1124 from milosgajdos83/flush-advert-query
Use the same logic for advertising routes in Router and Network
2020-01-17 16:49:11 +00:00
Milos Gajdos
23d65145e6 Use the same logic for advertising routes in Router and Network
router.Query() allows to query the routes with given router.Strategy.
It uses the same logic as was implemented in flushRoutes but the code
was never updated. This way we are consistent across both router and
network packages.
2020-01-17 16:25:18 +00:00
shu xian
9ea4919b9b rename mucp source directory to service 2020-01-17 23:53:33 +08:00
Asim Aslam
f78e30770e Merge pull request #1123 from milosgajdos83/sync-metric
Update route metric before sending the Sync message
2020-01-17 15:44:49 +00:00
Milos Gajdos
bf9f319cdf Update route metric before sending the Sync message 2020-01-17 15:38:28 +00:00
shu xian
ad28b72dd3 rename mucpSource to service 2020-01-17 23:27:41 +08:00
Asim Aslam
8425ae77f8 Merge pull request #1122 from micro/blocking
don't block forever
2020-01-17 15:24:03 +00:00
Asim Aslam
d7b9b2713b don't block forever 2020-01-17 15:23:10 +00:00
Asim Aslam
be788415ad minor runtime fixes 2020-01-17 14:14:47 +00:00
shu xian
a03791c581 set DefaultClient 2020-01-17 21:32:00 +08:00
Asim Aslam
ee922a3da6 Merge pull request #1121 from milosgajdos83/no-routes-found
Continue processing Sync if no routes were returned from router Query
2020-01-17 13:05:47 +00:00
Milos Gajdos
624f1c1980 Continue processing Sync if no routes were returned from router Query 2020-01-17 12:58:13 +00:00
Milos Gajdos
607a226e34 Updated debug logs to make them less verbose 2020-01-17 12:14:56 +00:00
Asim Aslam
bac1bbfd97 Merge pull request #1120 from milosgajdos83/remove-solicit
Remove Solicitation from the network
2020-01-16 20:02:05 +00:00
Milos Gajdos
7f9b3b5556 Remove Solicitation from the network
Instead, when a new peer is discovered it is sent a sync message i.e. we
do the full sync when discovering peers
2020-01-16 19:43:10 +00:00
Asim Aslam
ba12513199 Merge pull request #1119 from milosgajdos83/sync-best-routes-only
Send only best routes via Sync. Only apply best routes.
2020-01-16 18:17:26 +00:00
Milos Gajdos
8fcfbc0d20 Strip unnecessary continue statement 2020-01-16 17:33:53 +00:00
Milos Gajdos
472186c1be Code consistency. Small bug fix. 2020-01-16 17:04:04 +00:00
Milos Gajdos
60c05bd899 Find the best routes in the routes we would advertise based on Strategy 2020-01-16 16:53:39 +00:00
Milos Gajdos
793e6013e5 Advertise routes with configured strategy. Simplify Sync apply logic 2020-01-16 16:42:23 +00:00
shu xian
071ab7aede add mucp for config/source 2020-01-17 00:10:15 +08:00
Milos Gajdos
eda8b00f84 Send only best routes via Sync. Only apply best routes. 2020-01-16 16:08:49 +00:00
Asim Aslam
eac2ab3c28 Merge pull request #1117 from micro/scheduler
Switch notifier to scheduler
2020-01-16 13:41:13 +00:00
Asim Aslam
76fba34c2f Merge pull request #1116 from milosgajdos83/query-strategy
QueryStrategy to allow querying routes based on Advertising Strategy
2020-01-16 13:39:39 +00:00
Asim Aslam
491a42d352 Switch notifier to scheduler 2020-01-16 13:34:04 +00:00
Milos Gajdos
5e85194a13 QueryStrategy to allow querying routes based on Advertising Strategy 2020-01-16 12:48:36 +00:00
Asim Aslam
689ae7cfc7 Storing tunnel.Session rather than transport.Client 2020-01-16 00:28:58 +00:00
Asim Aslam
19dbd77402 fix net masking in listed routes 2020-01-16 00:12:38 +00:00
Asim Aslam
b194b3adc9 Merge pull request #1115 from micro/net-mask
mask the route before sending
2020-01-15 23:13:53 +00:00
Asim Aslam
33a9b3bc17 mask the route before sending 2020-01-15 23:06:58 +00:00
Asim Aslam
6562154573 Fix the next panic 2020-01-15 21:38:37 +00:00
Asim Aslam
b32ebddf85 update nlopes/slack dep 2020-01-15 21:22:07 +00:00
Asim Aslam
b3e3dac975 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-01-15 21:02:58 +00:00
Asim Aslam
f20e4daa60 fix rand panic 2020-01-15 21:02:53 +00:00
Asim Aslam
f67d87e99d Merge pull request #1114 from milosgajdos83/network-backward-compatibility
Fixed bug:m network.proto backwards compatibility unmarshal
2020-01-15 19:57:57 +00:00
Milos Gajdos
36928b716c Fixed bug:m network.proto backwards compatibility unmarshal 2020-01-15 19:45:43 +00:00
Asim Aslam
7c7b0ced5f Merge pull request #1104 from milosgajdos83/network-hackery
[WIP] Network hackery
2020-01-14 20:28:32 +00:00
Milos Gajdos
c67ef7e017 Bug fix: skip sending sync message if the peer is not in our graph 2020-01-14 19:37:50 +00:00
Milos Gajdos
dcd925f1e5 Code cleanup; Indentation. 2020-01-14 18:48:42 +00:00
Milos Gajdos
0ea56a5ffe Fixed tests 2020-01-14 18:22:58 +00:00
Milos Gajdos
821fda41ae Added Status method to network.Node fixed random segfaults. 2020-01-14 18:12:36 +00:00
Asim Aslam
1d311ab457 Embedded NATS Broker (#1110)
* if the address is produced by a default route don't hash it

* embedded nats

* fix url parsing

* don't override help

* add ready flag
2020-01-14 13:23:16 +00:00
Asim Aslam
b699d969e4 if the address is produced by a default route don't hash it (#1108) 2020-01-14 11:20:13 +00:00
Milos Gajdos
994d371ff1 Removed redundant comments. Add proper PruneStalePeers test. 2020-01-14 10:49:34 +00:00
Milos Gajdos
a91dad04ee Increment node error count and prune when Max limit is hit 2020-01-13 22:22:12 +00:00
Milos Gajdos
b4261e8cf9 Updated log and comments 2020-01-13 20:14:30 +00:00
Milos Gajdos
efcac3d009 Define tunnel errors 2020-01-13 20:14:30 +00:00
Milos Gajdos
770c7686ba Fix nasty bug when graph action may not have been executed in some
branches
2020-01-13 20:14:30 +00:00
Milos Gajdos
11904e1137 Regular sync with network every 5 minutes. Apply routes before peering. 2020-01-13 20:14:29 +00:00
Milos Gajdos
1e009e52dd Avoid having the same log statements in initNodes and resolveNodes 2020-01-13 20:14:29 +00:00
Milos Gajdos
bf42c028fb Added sync message. Refactored connect flow. Adverts are gossipped.
This commit adds a Sync message which is sent as a reply to Connect
message. This should in theory speed up convergence of a (re)connecting
node.

We respond to Sync message by sending a peer message back to the peer
origin of the Sync message. We consequently update our routing table and
peer graph with the data sent in via Sync message.

We now gossip advertse to up to 3 randomly selected peers instead of
sending a multicast message to the network.

Equally, Solicitation i.e. full table adverts are gossipped to a
randomly selected peer. If that fails we send a multicast message to the
network.
2020-01-13 20:14:29 +00:00
Milos Gajdos
0a4bd02503 Add RefreshSync method for Sync bookkeeping 2020-01-13 20:14:29 +00:00
Milos Gajdos
63edfaa852 Added Sync message
Sync message will be sent between peers when a new node connects/joins
the network
2020-01-13 20:14:29 +00:00
Milos Gajdos
802cc8239a Send solicit message properly. Updated comments. 2020-01-13 20:14:29 +00:00
Matthew Costa
75b1a62af3 Replace service prefix with FQDN style prefix (#1107)
* Replace service prefix with FQDN style prefix

According to the k8s documentation, the label and annotation prefixes should be in the format of a FQDN, with dot separated labels of no more than 63 characters. The current label and annotation paramteres are rejected by the k8s api, most likely because they have two forward slashes in them.

* Use go.micro as service and annotation prefix
2020-01-12 14:37:12 +00:00
Maarten Bezemer
50b20413d3 RPC stream client/server mutex fix (#884)
* Unlock RPC client while actually receiving a message

As receiving a message might block for a long time, unblocking the client allows to let it send messages in the meanwhile without using 'tricks'

* Unlock RPC server while actually receiving a message

As receiving a message might block for a long time, unblocking the client allows to let it send messages in the meanwhile without using 'tricks'

* Protect Close() against race conditions

* Concurrency and Sequence tests
2020-01-12 09:13:14 +00:00
Shu xian
fa5b3ee9d9 config/reader.Values add Set for specific path merge (#1099)
* add Set for specific path merge

* add Set

* add Del
2020-01-11 20:50:09 +00:00
Asim Aslam
f50a50eeb3 go fmt 2020-01-10 21:54:36 +00:00
Asim Aslam
e1e6199743 normalise runtime service status 2020-01-10 21:54:28 +00:00
Asim Aslam
61dd2b8489 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-01-10 19:13:59 +00:00
Asim Aslam
6ca298c61d set default store, fix store options bug, add String method 2020-01-10 19:13:55 +00:00
f4fb923fb2 pass additional context for broker subscribe (#1105)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-01-10 15:04:15 +00:00
shikbupt
32a2005f6d add option for web service signal handler (#1091) 2020-01-10 14:25:28 +00:00
Asim Aslam
37d1139a57 ensure we close the grpc stream (#1098)
* ensure we close the grpc stream

* use g.Close

* use closed bool flag for checking connection close
2020-01-09 17:00:14 +00:00
Asim Aslam
a90a74c9e2 Change the store interface to remove variadic args (#1095) 2020-01-08 22:23:14 +00:00
Milos Gajdos
78aed5beed Fixed tunnel race conditions. (#1094) 2020-01-08 14:48:38 +00:00
Milos Gajdos
59fccb82ec Updated comments. Tiny cleanup changes. (#1093) 2020-01-08 13:18:11 +00:00
Asim Aslam
048065fe96 support ability to set store, address and namespace via flags and env vars (#1092) 2020-01-08 12:11:31 +00:00
Asim Aslam
0b8ff3a8bb fix grpc json streaming by setting content sub type (#1089) 2020-01-07 18:37:34 +00:00
Asim Aslam
1892bd05a5 only add api endpoint metadata if it exists (#1087) 2020-01-06 22:22:36 +00:00
Asim Aslam
be6e8a7c78 add store to defaults (#1086) 2020-01-06 17:44:32 +00:00
Asim Aslam
df9055f69c continue to process messages even after the connection is closed 2020-01-03 20:43:53 +00:00
Asim Aslam
649dd235c3 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-01-03 19:46:24 +00:00
Asim Aslam
1af82df8b1 Check link is grpc 2020-01-03 19:46:14 +00:00
Eagle Wu
7098e59b5c remove ignore error in method publish (#1075) 2020-01-03 17:24:19 +00:00
Shu xian
31362bc331 prevent resource leak (#1080) 2020-01-03 13:31:47 +00:00
Asim Aslam
4e2339749c Merge branch 'master' of ssh://github.com/micro/go-micro 2020-01-02 21:19:50 +00:00
Asim Aslam
9cecf2e097 make grpc proxy streaming work 2020-01-02 21:11:25 +00:00
Lars Lehtonen
fe9c68238f runtime/kubernetes: remove unused name variable (#1078) 2020-01-02 20:42:46 +00:00
Asim Aslam
225b17559b fix log streaming 2020-01-02 18:23:43 +00:00
Asim Aslam
e697912ee5 don't panic on nil 2020-01-01 21:56:29 +00:00
Asim Aslam
6358b9277d don't write anything if theres no data 2019-12-31 22:58:14 +00:00
Asim Aslam
45c986c5f1 don't marshal frame values 2019-12-31 21:36:22 +00:00
Asim Aslam
fa01ff6604 update ctx test 2019-12-31 13:53:48 +00:00
Asim Aslam
fe1e018e8e update wrapper test 2019-12-31 13:45:49 +00:00
Asim Aslam
60ea537bbc upper case the metadata 2019-12-31 13:37:29 +00:00
Asim Aslam
488dc31743 log when starting the service 2019-12-31 12:07:52 +00:00
Asim Aslam
b6915f0898 set grpc by default (#1070) 2019-12-30 18:33:21 +00:00
Asim Aslam
04dfe4e867 fix breaking test 2019-12-30 17:39:02 +00:00
Asim Aslam
d8fe030a4b go fmt 2019-12-30 17:29:57 +00:00
Asim Aslam
f40d4578d5 go fmt 2019-12-30 17:29:45 +00:00
Asim Aslam
e0078bbcd5 Remove use of config/cmd in api 2019-12-30 17:29:20 +00:00
Asim Aslam
c145f355dd Moving to gRPC by default (#1069)
* Step 1

* Fix the test panics
2019-12-29 21:07:55 +00:00
943445270f fix registry check issue (#1067)
fix #1066

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-12-28 11:11:46 +00:00
Asim Aslam
61cde4a9f4 Kubernetes Registry (#1064)
* add teh k8s registry

* add k8s reg config/cmd

* go mod update
2019-12-27 20:08:46 +00:00
jamsonzan
22aa7d14b3 support streams pool for grpc (#1062)
* Update grpc_pool.go

* Update options.go

* Update grpc.go

* Update grpc_pool_test.go

* streams pool for grpc

* use busy list to speed up allocate while pool is very busy

* fix idle bug
2019-12-27 12:25:58 +00:00
Eagle Wu
a09b6729cc Fix missing recover while occur panic in handler (#1063) 2019-12-27 10:53:11 +00:00
Asim Aslam
2fe64001c0 Start runtime services inline 2019-12-24 17:51:30 +00:00
Asim Aslam
14c9c412cd Move and rename 2019-12-24 17:45:17 +00:00
Asim Aslam
361fdfba04 move around k8s api 2019-12-24 17:42:22 +00:00
Asim Aslam
5c8d1ae2b9 Update k8s log options 2019-12-24 17:33:05 +00:00
Asim Aslam
81e20160f5 reorder 2019-12-23 08:49:53 +00:00
Asim Aslam
ef95b28e3d add Write method to config source 2019-12-23 08:42:57 +00:00
shikbupt
1781542964 fix etcd LogConfig bug (#1056) 2019-12-23 07:29:13 +00:00
Asim Aslam
45208992b5 NewLog 2019-12-20 23:36:16 +00:00
Asim Aslam
847a01df82 Cleanup k8s logs 2019-12-20 23:34:08 +00:00
Jake Sanders
ce33e3b072 Kubernetes logging (#1054)
* wip

* Implementation of Kubernetes Logger

* Missing file

* Skip test in Travis
2019-12-20 23:16:05 +00:00
Asim Aslam
ae12fd1021 update crap logging 2019-12-19 18:25:22 +00:00
Asim Aslam
95ae2688a2 add formatters 2019-12-19 12:29:03 +00:00
Asim Aslam
2bcfb85613 Add log Format option 2019-12-19 12:20:33 +00:00
Asim Aslam
d52a111735 add requests/errors to stats 2019-12-18 18:36:42 +00:00
Asim Aslam
d4bec24eb7 Add os log buffer 2019-12-18 17:06:29 +00:00
Asim Aslam
2338e7c9d2 Cleanup go mod 2019-12-18 16:12:25 +00:00
Asim Aslam
f0e841595c move to structured logging 2019-12-18 16:02:11 +00:00
Asim Aslam
a82af19d43 strip newline 2019-12-18 15:19:20 +00:00
Asim Aslam
5d7254e79a fix the os logger 2019-12-18 15:06:25 +00:00
Asim Aslam
7c21a1b92a go fmt 2019-12-18 15:06:02 +00:00
Asim Aslam
587f64a87a Merge pull request #1051 from micro/event
publisher => event
2019-12-18 15:04:38 +00:00
Asim Aslam
cb9c4c3aef publisher => event 2019-12-17 23:05:46 +00:00
Asim Aslam
dda96cb87e Merge pull request #976 from micro/auth
First interface for auth
2019-12-17 21:38:11 +00:00
Asim Aslam
ebae497a72 use service rather than resource 2019-12-17 21:37:20 +00:00
Asim Aslam
515014fbeb update with resource 2019-12-17 21:27:05 +00:00
Asim Aslam
e9efcbe8dc strip logger 2019-12-17 18:34:21 +00:00
Asim Aslam
5a52593e66 go fmt 2019-12-17 18:24:00 +00:00
Asim Aslam
c61e12d5ee add event proto for runtiem 2019-12-17 18:17:32 +00:00
Asim Aslam
c2d59c1f4d Move logger 2019-12-17 18:16:45 +00:00
Jake Sanders
812fe9e640 Merge pull request #1049 from micro/kubernetes-logging
Kubernetes logging
2019-12-17 17:44:24 +00:00
Jake Sanders
f95bccce84 Use UTC in tests 2019-12-17 17:36:01 +00:00
Jake Sanders
81e7edd666 Adhere to new interfaces 2019-12-17 17:24:01 +00:00
Asim Aslam
46fd205eda rename files 2019-12-17 17:08:38 +00:00
Jake Sanders
c2b307e5bb Merge branch master of https://github.com/micro/go-micro into kubernetes-logging 2019-12-17 17:08:31 +00:00
Jake Sanders
b7ac62f7d2 Merge branch 'master' of https://github.com/micro/go-micro into kubernetes-logging 2019-12-17 17:06:07 +00:00
Asim Aslam
50d5c6402b Merge pull request #1048 from micro/logging
Move stream to interface
2019-12-17 17:05:13 +00:00
Asim Aslam
d2a3fd0b04 Move stream to interface 2019-12-17 16:56:55 +00:00
Jake Sanders
51f4bc6d56 Add logs to Interface 2019-12-17 16:30:09 +00:00
Jake Sanders
c3607c23e7 Fix after merge 2019-12-17 16:27:17 +00:00
Jake Sanders
34b1c403bb Merge branch 'master' of https://github.com/micro/go-micro into kubernetes-logging 2019-12-17 16:13:36 +00:00
Asim Aslam
91e057440d Merge pull request #1047 from micro/decruft-logger
Decruft logger
2019-12-17 16:13:20 +00:00
Jake Sanders
53ca742c66 Update the util/kubernetes client to retrieve logs 2019-12-17 16:09:51 +00:00
Asim Aslam
b35dfb1086 fix further breaks 2019-12-17 15:56:49 +00:00
Asim Aslam
d502e2f58a fix breaks 2019-12-17 15:46:09 +00:00
Asim Aslam
bc30efcf70 Decruft the debug logger interface 2019-12-17 15:38:03 +00:00
Jake Sanders
0415ead504 First commit for Kubernetes logger 2019-12-17 12:11:26 +00:00
Jake Sanders
e95f44d3f8 Move runtime/kubernetes/client to util/kubernetes/client 2019-12-17 11:32:38 +00:00
Asim Aslam
0489ae91e9 Merge pull request #1021 from unistack-org/registry_check
add RegisterCheck web server option for internal health checks
2019-12-17 08:49:00 +00:00
Asim Aslam
4e02f444fd Merge pull request #1024 from unistack-org/server
add server Context option to pass own context
2019-12-17 08:48:39 +00:00
Asim Aslam
6027a81f06 Update router comments 2019-12-17 08:28:45 +00:00
Asim Aslam
01f0e70213 add some commented out stuff 2019-12-16 17:37:11 +00:00
Asim Aslam
cb15fadcee go fmt 2019-12-16 17:36:47 +00:00
Asim Aslam
1c8d15fe4b Merge pull request #1046 from micro/storefix
Fix cockroachdb store implementation
2019-12-16 17:24:53 +00:00
Jake Sanders
55f5937c8b Remove debug 2019-12-16 17:16:10 +00:00
Jake Sanders
56619f2745 Fix cockroachdb store implemetation 2019-12-16 17:11:13 +00:00
Asim Aslam
0b59e2ce3d Merge pull request #1028 from Astone-Chou/fix
named return value for error defer modify.
2019-12-16 15:39:58 +00:00
Asim Aslam
1ea6390eae Add proxy string method 2019-12-16 15:18:20 +00:00
Asim Aslam
303adca500 rename postgres to cockroach 2019-12-16 15:09:59 +00:00
Asim Aslam
03700ae6c0 Replace proxy options 2019-12-16 14:55:47 +00:00
Asim Aslam
a1ddfa827e Merge pull request #1045 from micro/store-options
change store options
2019-12-16 14:46:15 +00:00
Asim Aslam
59751c02e6 change store options 2019-12-16 14:38:51 +00:00
Jake Sanders
e8e112144f Create database should take the name of the database 2019-12-16 14:15:30 +00:00
Asim Aslam
59246e0412 Merge pull request #1044 from micro/store-namespace
change use of store namespace/prefix in sql store
2019-12-16 12:20:57 +00:00
Asim Aslam
0131e9468f change use of store namespace/prefix in sql store 2019-12-16 12:13:18 +00:00
Asim Aslam
d5951f1d7c Merge pull request #1042 from ZGeomantic/feat-client-mock
support ctx as input params, error as output for MockClient.Call
2019-12-15 15:12:17 +00:00
Asim Aslam
f33b562c16 Merge pull request #1043 from jamsonzan/branch0
comment
2019-12-15 10:50:52 +00:00
jamsonzan
572fe58314 comment 2019-12-15 15:05:19 +08:00
gemantic
5602b93d7a support ctx as input params, error as output for MockClient.Call 2019-12-14 10:36:12 +08:00
Asim Aslam
64e438a8d4 Merge pull request #1038 from micro/tun
Next level tunnel optimisation
2019-12-13 15:34:03 +00:00
Asim Aslam
b0b6b8fce2 final updates 2019-12-13 15:27:47 +00:00
Asim Aslam
417a05db60 Merge pull request #1039 from xpunch/etcdLogConfig
Etcd log config
2019-12-13 09:23:54 +00:00
johnson
11e42aac69 etcd can set log config
default log level is info, which will log o log of unused logs
2019-12-13 11:22:05 +08:00
potato
885ba8f905 Merge pull request #6 from micro/master
Pull latest go-micro
2019-12-13 11:10:29 +08:00
Asim Aslam
caa74d1b5f fix build 2019-12-12 23:29:44 +00:00
Asim Aslam
f6b4a9da1c strip some code 2019-12-12 23:20:31 +00:00
Asim Aslam
74c5102e41 strip a couple things 2019-12-12 21:49:39 +00:00
Asim Aslam
7bd50cd251 fix more broken cruft 2019-12-12 17:10:32 +00:00
Asim Aslam
df728aaddd remove go routines from tunnel, fire network messages in go routines 2019-12-12 13:34:08 +00:00
Asim Aslam
ae934c19f1 fix tunnel test 2019-12-12 13:04:34 +00:00
Asim Aslam
e260cc4a24 save cruft 2019-12-12 12:27:46 +00:00
Asim Aslam
631faff7b8 Merge pull request #1035 from alrs/fix-grpc-test-goroutines
Fix service/grpc Test Goroutines
2019-12-11 16:39:22 +00:00
Lars Lehtonen
fd531349d7 service/grpc: t.Fatal out of TestGRPCTLSService() goroutine 2019-12-11 08:29:23 -08:00
Lars Lehtonen
27bab29e3c service/grpc: t.Fatal out of TestGRPCService() goroutine 2019-12-11 08:29:14 -08:00
Asim Aslam
27af221fd2 batch metric updates 2019-12-11 15:23:08 +00:00
Asim Aslam
6e28e7a86f Save current state of the world 2019-12-11 14:37:03 +00:00
Asim Aslam
ff69d46c98 strip some cruft from config/cmd 2019-12-10 18:12:29 +00:00
Asim Aslam
e966944ae5 sort resolved node list in network by lowest priority 2019-12-10 17:57:04 +00:00
Asim Aslam
8da77a3ddc use fixed port names for runtime k8s 2019-12-10 14:05:05 +00:00
Asim Aslam
8289dbabc4 Merge pull request #1034 from micro/strip-handlers
Strip handlers
2019-12-10 11:54:06 +00:00
Asim Aslam
d50f30d743 strip handlers 2019-12-10 11:46:13 +00:00
Asim Aslam
f8533551a4 move debug to trace for network peer refresh 2019-12-10 09:09:24 +00:00
Asim Aslam
7e46ff5d92 strip some more logging down 2019-12-10 09:08:03 +00:00
Asim Aslam
6b1eef5354 Merge branch 'master' of ssh://github.com/micro/go-micro 2019-12-09 22:56:33 +00:00
Asim Aslam
b29da80539 Move a few things around 2019-12-09 22:56:26 +00:00
Asim Aslam
a725ab2c94 Merge pull request #1032 from jankremlacek/patch-1
nlopes/slack dep update to fix broken unmarshal
2019-12-09 21:17:34 +00:00
Jan Kremlacek
1582aa1572 proper go.mod, go.sum 2019-12-09 22:08:57 +01:00
Jan Kremlacek
5352d53346 nlopes/slack dep update to fix broken unmarshal
About a month ago Slack introduced the updated structure of RTM messages which resulted in an inability to unmarshal received msg (original issue: https://github.com/nlopes/slack/issues/630). It's not an issue of micro itself, but of the github.com/nlopes/slack lib. The fix was already merged into master (https://github.com/nlopes/slack/pull/618), but the lib has not been released in any new version.

Thus so I propose to update directly to the commit:

go get github.com/nlopes/slack@d06c2a2b3249b44a9c5dee8485f5a87497beb9ea

The MicroBot does now work now with any Slack client newer than around a month old.
2019-12-09 21:52:28 +01:00
Asim Aslam
7d884eff9d Merge pull request #1031 from alrs/fix-web-test-goroutines
web: fix test goroutines
2019-12-09 18:03:07 +00:00
Lars Lehtonen
c9af88184b web: fix test goroutine in TestService() 2019-12-09 09:44:14 -08:00
Lars Lehtonen
f25ad35f0a web: fix test goroutine in TestTLS() 2019-12-09 09:28:42 -08:00
Asim Aslam
55ab44c8be Merge branch 'master' of ssh://github.com/micro/go-micro 2019-12-09 09:32:33 +00:00
Asim Aslam
59c1680594 Move Debug to Trace in router 2019-12-09 09:32:17 +00:00
Asim Aslam
b804303aa0 Merge pull request #1029 from Astone-Chou/lint_fix_fix
fix tiny error in linting fixes
2019-12-09 07:47:46 +00:00
Astone
55a15ecf12 fix tiny error in linting fixes 2019-12-09 11:18:45 +08:00
Astone
caa1bcf9fe named return value for error defer modify. 2019-12-09 11:08:21 +08:00
Asim Aslam
e2b2a30668 add net/http/pprof profiler 2019-12-08 20:31:16 +00:00
Asim Aslam
a9be1288d2 Merge pull request #1026 from micro/tun
Network & Tunnel refactor
2019-12-08 16:01:46 +00:00
Asim Aslam
679214e598 just return filtered routes 2019-12-08 15:39:45 +00:00
Asim Aslam
6064e1426c fix some potential go routine leaks 2019-12-08 15:02:54 +00:00
Asim Aslam
d28a868e46 Fix network startup connect 2019-12-08 14:37:17 +00:00
Asim Aslam
398acc67ca fix broken test 2019-12-08 13:45:24 +00:00
Asim Aslam
ce578800d0 n.initNodes 2019-12-08 12:17:31 +00:00
Asim Aslam
6307d6ba51 further cleanup of tunnel/network 2019-12-08 12:12:20 +00:00
Asim Aslam
283c85d256 done 2019-12-08 00:53:55 +00:00
Asim Aslam
9bd0fb9125 save changes 2019-12-07 23:28:39 +00:00
Asim Aslam
c445aed6b1 Cleanup and speedup network convergence along with direct messaging for connect and solicit 2019-12-07 19:54:29 +00:00
Asim Aslam
1d8c66780e save working solution 2019-12-06 00:18:40 +00:00
Asim Aslam
219efd27e9 Optimise the multicast to use one session in the tunnel 2019-12-05 23:11:42 +00:00
Asim Aslam
ac9001ac74 advert ttl inline with router table tick 2019-12-05 17:50:32 +00:00
Asim Aslam
2896614595 Merge pull request #1025 from milosgajdos83/router-knobs
Changed a few router knobs to avoid storms ⛈
2019-12-05 17:50:02 +00:00
Milos Gajdos
bca12a7003 Changed a few router knobs to avoid storms ⛈ 2019-12-05 17:11:17 +00:00
1c5a4c470f add server Context option to pass own context
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-12-05 19:37:03 +03:00
Asim Aslam
744b19d625 Merge pull request #1023 from milosgajdos83/dead-code
Fixing dead code and go vet moaning
2019-12-05 16:18:13 +00:00
Milos Gajdos
5865e89bed Fixing dead code and go vet moaning 2019-12-05 16:10:49 +00:00
Asim Aslam
3a10b1cdde Merge pull request #1022 from milosgajdos83/tunnel-races
This PR fixes various tunnel race conditions
2019-12-05 15:59:29 +00:00
Milos Gajdos
5db7514a91 This PR fixes various tunnel race conditions 2019-12-05 15:50:32 +00:00
a957e90ca8 add RegisterCheck web server option for internal health checks
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-12-05 17:28:11 +03:00
Asim Aslam
18cf025056 Merge pull request #1020 from Allenxuxu/master
Avoid excessive memory usage
2019-12-05 11:56:44 +00:00
Allenxuxu
b55b7d6b20 replace map with list 2019-12-05 19:29:48 +08:00
Asim Aslam
26b5d1994a Merge pull request #1019 from tboerger/no-newlin
No newlines at end of log messages
2019-12-05 10:08:16 +00:00
Thomas Boerger
30aec5b872 No newlines at end of log messages
Log messages should not end with a new line, this should be entirely
handled by the underlying log library.

Signed-off-by: Thomas Boerger <thomas@webhippie.de>
2019-12-05 10:56:45 +01:00
Asim Aslam
b0626089f3 Merge pull request #1016 from unistack-org/lint
lint fixes mostly for prealloc also remove deadcode from grpc client
2019-12-05 08:33:52 +00:00
Asim Aslam
af3d4e595f in memory stats buffer 2019-12-05 00:08:46 +00:00
Asim Aslam
40c09eed1c Logs to Log 2019-12-05 00:01:17 +00:00
Asim Aslam
fe46e7a9e9 Logs to Log 2019-12-04 23:58:29 +00:00
Asim Aslam
78647c7027 add timestamp to stats 2019-12-04 23:51:07 +00:00
Asim Aslam
26755f86b1 Merge pull request #1018 from micro/debug-service
Debug service
2019-12-04 14:40:58 +00:00
Asim Aslam
898848795b tab indent 2019-12-04 12:28:16 +00:00
Asim Aslam
24efbe6a41 Move debug handler to service dir 2019-12-04 12:27:30 +00:00
Asim Aslam
97433f716f add service name to debug proto 2019-12-04 12:02:44 +00:00
Asim Aslam
5200febaea add stats debug interface 2019-12-04 11:53:20 +00:00
a1eaf9cc20 linting fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-12-04 00:22:02 +03:00
Asim Aslam
3f8af8c1e0 Merge branch 'master' of ssh://github.com/micro/go-micro 2019-12-03 20:32:10 +00:00
Asim Aslam
becaeefcba Revert. DO NOT peer solicit until we know better 2019-12-03 20:32:02 +00:00
7d5bdcf993 fix pointer dereference in memory registry
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-12-03 23:05:03 +03:00
Asim Aslam
81a7cf458c Merge pull request #1013 from unistack-org/linting
add golanglint-ci
2019-12-03 16:57:00 +00:00
Asim Aslam
a4ea61334b Merge pull request #1015 from milosgajdos83/router-race
Fix router race condition
2019-12-03 16:55:56 +00:00
Asim Aslam
3fbba9f83d Merge pull request #1014 from milosgajdos83/memreg-race
Fix memory registry race
2019-12-03 16:51:36 +00:00
Milos Gajdos
4a11a4c546 Fix router race condition 2019-12-03 15:22:54 +00:00
Milos Gajdos
265233517e Fix memory registry race 2019-12-03 14:53:07 +00:00
c581ceb1dc add golanglint-ci
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-12-03 16:31:43 +03:00
Asim Aslam
bb1a1358b7 Merge pull request #1012 from Astone-Chou/lint
improve code quality
2019-12-03 13:10:04 +00:00
Astone
29fb58db39 improve code quality 2019-12-03 20:59:02 +08:00
Asim Aslam
b6f0164501 Merge branch 'master' of ssh://github.com/micro/go-micro 2019-12-03 08:11:46 +00:00
Asim Aslam
2c0801fc1c couple bug fixes in tunnel 2019-12-03 08:11:36 +00:00
Asim Aslam
34c92f4964 Merge pull request #1011 from Astone-Chou/master
optimize: a better way for return error
2019-12-03 07:48:21 +00:00
Astone
f9b900b2ca optimize: a better way for return error 2019-12-03 12:47:29 +08:00
Asim Aslam
b5d65305db Merge pull request #1009 from unistack-org/panics
recover panics
2019-12-02 23:30:17 +00:00
b8e96f45d4 add recovery in case of panics
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-12-03 02:25:40 +03:00
90f9b0d0c9 initialize client options context if not provided
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-12-03 01:35:59 +03:00
Asim Aslam
972c0989af Merge pull request #1008 from micro/connect-fast
Optimizations to connect fast
2019-12-02 19:09:56 +00:00
Asim Aslam
5f04fd58ab optimizations to connect fast 2019-12-02 18:48:19 +00:00
Asim Aslam
da04aa8ae8 Switch to AdvertiseLocal by default 2019-12-02 17:36:20 +00:00
Asim Aslam
62bf0df864 Merge pull request #1007 from micro/ultra-hacks
ultra hacks to make debug handler work in proxy
2019-12-02 15:11:56 +00:00
Asim Aslam
91e9c0cb62 ultra hacks to make debug handler work in proxy 2019-12-02 14:55:35 +00:00
Asim Aslam
3356b83f24 register debug handler 2019-12-01 21:04:09 +00:00
Asim Aslam
795ec509fd Merge branch 'master' of ssh://github.com/micro/go-micro 2019-12-01 19:43:45 +00:00
Asim Aslam
93d66afe8c fix that broken logic 2019-12-01 19:43:36 +00:00
Asim Aslam
c840cee404 A variety of fixes to try combat the multicast issue 2019-12-01 19:36:16 +00:00
Milos Gajdos
e48cce6485 Merge pull request #1005 from milosgajdos83/nano-seconds
Time resolution change to be in line with debug/handler.Handler
2019-12-01 18:39:59 +00:00
Milos Gajdos
b2ecd93404 Time resolution change to be in line with debug/handler.Handler 2019-12-01 18:33:08 +00:00
Asim Aslam
2928c66624 Merge pull request #1004 from milosgajdos83/debug-logs
[WIP] Debug logs
2019-12-01 17:40:40 +00:00
Milos Gajdos
4613a820ca Small refactoring og logs
* log.Write now accepts log.Record
* we stream last 10 records first
* regenerate proto because of the above
2019-12-01 13:16:44 +00:00
Milos Gajdos
ecdadef633 Added hack support for logs streaming cruft 2019-12-01 13:16:44 +00:00
Milos Gajdos
7f1dea72f2 Simplified Logs RPC. Cleaned up code. Added comments. 2019-12-01 13:16:44 +00:00
Milos Gajdos
612f872f76 Server should not close the stream when done 2019-12-01 13:16:44 +00:00
Milos Gajdos
13d2a9ec7a Register proto handler properly 2019-12-01 13:16:44 +00:00
Milos Gajdos
9fab47ecdb Added DefaultLog variables 2019-12-01 13:16:44 +00:00
Milos Gajdos
265e8ade05 Embed DebugHandler proto 2019-12-01 13:16:44 +00:00
Milos Gajdos
60e0e81523 Added debug/service to grab the logs from a service 2019-12-01 13:16:43 +00:00
Milos Gajdos
b01357058a Regenerate proto cruft 2019-12-01 13:16:43 +00:00
Milos Gajdos
7deafbc5ce Added ReadOptions; Changed proto; Reimplemented Log(er) 2019-12-01 13:16:43 +00:00
Milos Gajdos
9e177be560 Embed logger into debug.Handler 2019-12-01 13:16:43 +00:00
Milos Gajdos
ee9776e7b2 Added debug.Logger interface 2019-12-01 13:16:43 +00:00
Milos Gajdos
3f7f2afc7b Dont be stupid -- stream does not require pointer. 2019-12-01 13:16:43 +00:00
Milos Gajdos
4e965e4ce5 First commit. Modified proto. Added empty handler method. 2019-12-01 13:16:43 +00:00
Asim Aslam
6f1c30aef5 remove unused value in router 2019-11-30 21:39:03 +00:00
Asim Aslam
00bbb3ac61 revert some protocol changes for now 2019-11-30 21:20:33 +00:00
Asim Aslam
ce1942c578 why didn't we think of this before...single service name for http broker 2019-11-30 21:00:36 +00:00
Asim Aslam
df7169c9f2 Merge branch 'master' of ssh://github.com/micro/go-micro 2019-11-30 01:16:53 +00:00
Asim Aslam
5e59db4c6d revert mdns timeout 2019-11-30 01:16:32 +00:00
Asim Aslam
25b3fda25b Merge pull request #1002 from micro/tun-fix
fix bug in the tunnel which causes multicast connections to be closed
2019-11-30 01:16:12 +00:00
Asim Aslam
9678daeafa fix bug in the tunnel which causes multicast connections to be closed 2019-11-30 01:14:40 +00:00
Asim Aslam
9ed257f151 Merge pull request #1001 from micro/storm
Changes for the storm
2019-11-29 17:33:23 +00:00
Asim Aslam
107124e5dc Changes for the storm 2019-11-29 17:27:29 +00:00
Asim Aslam
ce4acfa892 fix type filtering for k8s runtime 2019-11-29 13:05:18 +00:00
Asim Aslam
6bf4828296 triple the mdns timeout 2019-11-29 13:05:07 +00:00
Asim Aslam
76b4e78a6a fix command to accept variadic args 2019-11-29 11:55:25 +00:00
Asim Aslam
8b6475b8d4 Merge pull request #1000 from micro/runtime-type
Support service types in runtime
2019-11-29 11:46:21 +00:00
Asim Aslam
c3ed83dfba Support service types in runtime 2019-11-29 11:35:00 +00:00
Asim Aslam
114bc1e18b Merge pull request #995 from micro/rpc-leak
Fix rpc go routine leak
2019-11-27 17:24:19 +00:00
Asim Aslam
af94899b54 Fix rpc go routine leak 2019-11-27 17:12:07 +00:00
Asim Aslam
266b6dbc64 add debug buffer time based access 2019-11-27 13:57:19 +00:00
Asim Aslam
7013e7467f Undefined time 2019-11-25 09:33:30 +00:00
Asim Aslam
dbc537007d First interface for auth 2019-11-25 09:30:26 +00:00
potato
f933457cc1 Merge pull request #5 from micro/master
pull request from go-micro
2019-09-30 17:22:51 +08:00
535 changed files with 27981 additions and 10665 deletions

9
.github/generate.sh vendored Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash -e
find . -type f -name '*.pb.*.go' -o -name '*.pb.go' -a ! -name 'message.pb.go' -delete
PROTOS=$(find . -type f -name '*.proto')
for PROTO in $PROTOS; do
echo $PROTO
protoc -I./ -I$(dirname $PROTO) --go_out=plugins=grpc,paths=source_relative:. --micro_out=paths=source_relative:. $PROTO
done

19
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Docker
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
name: Check out repository
- uses: elgohr/Publish-Docker-Github-Action@2.12
name: Build and Push Docker Image
with:
name: micro/go-micro
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

28
.github/workflows/pr.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: PR Sanity Check
on: pull_request
jobs:
prtest:
name: PR sanity check
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
- name: Run tests
id: tests
env:
IN_TRAVIS_CI: yes
run: go test -v ./...

51
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Run tests
on: [push]
jobs:
test:
name: Test repo
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
- name: Run tests
id: tests
env:
IN_TRAVIS_CI: yes
run: go test -v ./...
- name: Notify of test failure
if: failure()
uses: rtCamp/action-slack-notify@v2.0.0
env:
SLACK_CHANNEL: build
SLACK_COLOR: '#BF280A'
SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
SLACK_TITLE: Tests Failed
SLACK_USERNAME: GitHub Actions
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
- name: Notify of test success
if: success()
uses: rtCamp/action-slack-notify@v2.0.0
env:
SLACK_CHANNEL: build
SLACK_COLOR: '#1FAD2B'
SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
SLACK_TITLE: Tests Passed
SLACK_USERNAME: GitHub Actions
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}

26
.golangci.yml Normal file
View File

@@ -0,0 +1,26 @@
run:
deadline: 10m
linters:
disable-all: false
enable-all: false
enable:
- megacheck
- staticcheck
- deadcode
- varcheck
- gosimple
- unused
- prealloc
- scopelint
- gocritic
- goimports
- unconvert
- govet
- nakedret
- structcheck
- gosec
disable:
- maligned
- interfacer
- typecheck
- dupl

View File

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

View File

@@ -5,7 +5,7 @@ RUN mkdir /user && \
echo 'nobody:x:65534:' > /user/group
ENV GO111MODULE=on
RUN apk --no-cache add make git gcc libtool musl-dev ca-certificates && \
RUN apk --no-cache add make git gcc libtool musl-dev ca-certificates dumb-init && \
rm -rf /var/cache/apk/* /tmp/*
WORKDIR /

View File

@@ -1,4 +1,4 @@
# Go Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![GoDoc](https://godoc.org/github.com/micro/go-micro?status.svg)](https://godoc.org/github.com/micro/go-micro) [![Travis CI](https://api.travis-ci.org/micro/go-micro.svg?branch=master)](https://travis-ci.org/micro/go-micro) [![Go Report Card](https://goreportcard.com/badge/micro/go-micro)](https://goreportcard.com/report/github.com/micro/go-micro)
# Go Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Go.Dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/micro/go-micro?tab=doc) [![Travis CI](https://api.travis-ci.org/micro/go-micro.svg?branch=master)](https://travis-ci.org/micro/go-micro) [![Go Report Card](https://goreportcard.com/badge/micro/go-micro)](https://goreportcard.com/report/github.com/micro/go-micro)
Go Micro is a framework for microservice development.
@@ -12,7 +12,7 @@ but everything can be easily swapped out.
Plugins are available at [github.com/micro/go-plugins](https://github.com/micro/go-plugins).
Follow us on [Twitter](https://twitter.com/microhq) or join the [Slack](https://micro.mu/slack) community.
Follow us on [Twitter](https://twitter.com/microhq) or join the [Community](https://micro.mu/slack).
## Features
@@ -32,11 +32,11 @@ and server handle this by default. This includes protobuf and json by default.
- **Request/Response** - RPC based request/response with support for bidirectional streaming. We provide an abstraction for synchronous
communication. A request made to a service will be automatically resolved, load balanced, dialled and streamed. The default
transport is http/1.1 or http2 when tls is enabled.
transport is [gRPC](https://grpc.io/).
- **Async Messaging** - PubSub is built in as a first class citizen for asynchronous communication and event driven architectures.
Event notifications are a core pattern in micro service development. The default messaging is point-to-point http/1.1 or http2 when tls
is enabled.
Event notifications are a core pattern in micro service development. The default messaging system is an embedded [NATS](https://nats.io/)
server.
- **Pluggable Interfaces** - Go Micro makes use of Go interfaces for each distributed system abstraction. Because of this these interfaces
are pluggable and allows Go Micro to be runtime agnostic. You can plugin any underlying technology. Find plugins in

View File

@@ -1,4 +1,4 @@
# Go Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![GoDoc](https://godoc.org/github.com/micro/go-micro?status.svg)](https://godoc.org/github.com/micro/go-micro) [![Travis CI](https://api.travis-ci.org/micro/go-micro.svg?branch=master)](https://travis-ci.org/micro/go-micro) [![Go Report Card](https://goreportcard.com/badge/micro/go-micro)](https://goreportcard.com/report/github.com/micro/go-micro)
# Go Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Go.Dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/micro/go-micro?tab=doc) [![Travis CI](https://api.travis-ci.org/micro/go-micro.svg?branch=master)](https://travis-ci.org/micro/go-micro) [![Go Report Card](https://goreportcard.com/badge/micro/go-micro)](https://goreportcard.com/report/github.com/micro/go-micro)
Go Micro是基于Golang的微服务开发框架。

View File

@@ -6,8 +6,8 @@ import (
"sync"
"github.com/bwmarrin/discordgo"
"github.com/micro/go-micro/agent/input"
"github.com/micro/go-micro/util/log"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v2/logger"
)
type discordConn struct {
@@ -74,7 +74,9 @@ func (dc *discordConn) Send(e *input.Event) error {
fields := strings.Split(e.To, ":")
_, err := dc.master.session.ChannelMessageSend(fields[0], string(e.Data))
if err != nil {
log.Log("[bot][loop][send]", err)
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error("[bot][loop][send]", err)
}
}
return nil
}

View File

@@ -8,8 +8,8 @@ import (
"strings"
"github.com/bwmarrin/discordgo"
"github.com/micro/cli"
"github.com/micro/go-micro/agent/input"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
)
func init() {
@@ -36,21 +36,21 @@ type discordInput struct {
func (d *discordInput) Flags() []cli.Flag {
return []cli.Flag{
cli.StringFlag{
Name: "discord_token",
EnvVar: "MICRO_DISCORD_TOKEN",
Usage: "Discord token (prefix with Bot if it's for bot account)",
&cli.StringFlag{
Name: "discord_token",
EnvVars: []string{"MICRO_DISCORD_TOKEN"},
Usage: "Discord token (prefix with Bot if it's for bot account)",
},
cli.StringFlag{
Name: "discord_whitelist",
EnvVar: "MICRO_DISCORD_WHITELIST",
Usage: "Discord Whitelist (seperated by ,)",
&cli.StringFlag{
Name: "discord_whitelist",
EnvVars: []string{"MICRO_DISCORD_WHITELIST"},
Usage: "Discord Whitelist (seperated by ,)",
},
cli.StringFlag{
Name: "discord_prefix",
Usage: "Discord Prefix",
EnvVar: "MICRO_DISCORD_PREFIX",
Value: "Micro ",
&cli.StringFlag{
Name: "discord_prefix",
Usage: "Discord Prefix",
EnvVars: []string{"MICRO_DISCORD_PREFIX"},
Value: "Micro ",
},
}
}

View File

@@ -2,7 +2,7 @@
package input
import (
"github.com/micro/cli"
"github.com/micro/cli/v2"
)
type EventType string

View File

@@ -7,7 +7,7 @@ import (
"sync"
"time"
"github.com/micro/go-micro/agent/input"
"github.com/micro/go-micro/v2/agent/input"
"github.com/nlopes/slack"
)

View File

@@ -4,8 +4,8 @@ import (
"errors"
"sync"
"github.com/micro/cli"
"github.com/micro/go-micro/agent/input"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
"github.com/nlopes/slack"
)
@@ -26,15 +26,15 @@ func init() {
func (p *slackInput) Flags() []cli.Flag {
return []cli.Flag{
cli.BoolFlag{
Name: "slack_debug",
Usage: "Slack debug output",
EnvVar: "MICRO_SLACK_DEBUG",
&cli.BoolFlag{
Name: "slack_debug",
Usage: "Slack debug output",
EnvVars: []string{"MICRO_SLACK_DEBUG"},
},
cli.StringFlag{
Name: "slack_token",
Usage: "Slack token",
EnvVar: "MICRO_SLACK_TOKEN",
&cli.StringFlag{
Name: "slack_token",
Usage: "Slack token",
EnvVars: []string{"MICRO_SLACK_TOKEN"},
},
}
}

View File

@@ -6,9 +6,9 @@ import (
"sync"
"github.com/forestgiant/sliceutil"
"github.com/micro/go-micro/agent/input"
"github.com/micro/go-micro/util/log"
"gopkg.in/telegram-bot-api.v4"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v2/logger"
tgbotapi "gopkg.in/telegram-bot-api.v4"
)
type telegramConn struct {
@@ -44,11 +44,9 @@ func (tc *telegramConn) run() {
tc.recv = updates
tc.syncCond.Signal()
for {
select {
case <-tc.exit:
return
}
select {
case <-tc.exit:
return
}
}
@@ -106,7 +104,9 @@ func (tc *telegramConn) Send(event *input.Event) error {
if err != nil {
// probably it could be because of nested HTML tags -- telegram doesn't allow nested tags
log.Log("[telegram][Send] error:", err)
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error("[telegram][Send] error:", err)
}
msgConfig.Text = "This bot couldn't send the response (Internal error)"
tc.input.api.Send(msgConfig)
}

View File

@@ -5,9 +5,9 @@ import (
"strings"
"sync"
"github.com/micro/cli"
"github.com/micro/go-micro/agent/input"
"gopkg.in/telegram-bot-api.v4"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
tgbotapi "gopkg.in/telegram-bot-api.v4"
)
type telegramInput struct {
@@ -34,20 +34,20 @@ func init() {
func (ti *telegramInput) Flags() []cli.Flag {
return []cli.Flag{
cli.BoolFlag{
Name: "telegram_debug",
EnvVar: "MICRO_TELEGRAM_DEBUG",
Usage: "Telegram debug output",
&cli.BoolFlag{
Name: "telegram_debug",
EnvVars: []string{"MICRO_TELEGRAM_DEBUG"},
Usage: "Telegram debug output",
},
cli.StringFlag{
Name: "telegram_token",
EnvVar: "MICRO_TELEGRAM_TOKEN",
Usage: "Telegram token",
&cli.StringFlag{
Name: "telegram_token",
EnvVars: []string{"MICRO_TELEGRAM_TOKEN"},
Usage: "Telegram token",
},
cli.StringFlag{
Name: "telegram_whitelist",
EnvVar: "MICRO_TELEGRAM_WHITELIST",
Usage: "Telegram bot's users (comma-separated values)",
&cli.StringFlag{
Name: "telegram_whitelist",
EnvVars: []string{"MICRO_TELEGRAM_WHITELIST"},
Usage: "Telegram bot's users (comma-separated values)",
},
}
}

View File

@@ -1,11 +1,15 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: github.com/micro/go-micro/agent/proto/bot.proto
// source: agent/proto/bot.proto
package go_micro_bot
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
@@ -30,7 +34,7 @@ func (m *HelpRequest) Reset() { *m = HelpRequest{} }
func (m *HelpRequest) String() string { return proto.CompactTextString(m) }
func (*HelpRequest) ProtoMessage() {}
func (*HelpRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_018e8d5b14a89d12, []int{0}
return fileDescriptor_79b974b8c77805fa, []int{0}
}
func (m *HelpRequest) XXX_Unmarshal(b []byte) error {
@@ -63,7 +67,7 @@ func (m *HelpResponse) Reset() { *m = HelpResponse{} }
func (m *HelpResponse) String() string { return proto.CompactTextString(m) }
func (*HelpResponse) ProtoMessage() {}
func (*HelpResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_018e8d5b14a89d12, []int{1}
return fileDescriptor_79b974b8c77805fa, []int{1}
}
func (m *HelpResponse) XXX_Unmarshal(b []byte) error {
@@ -109,7 +113,7 @@ func (m *ExecRequest) Reset() { *m = ExecRequest{} }
func (m *ExecRequest) String() string { return proto.CompactTextString(m) }
func (*ExecRequest) ProtoMessage() {}
func (*ExecRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_018e8d5b14a89d12, []int{2}
return fileDescriptor_79b974b8c77805fa, []int{2}
}
func (m *ExecRequest) XXX_Unmarshal(b []byte) error {
@@ -149,7 +153,7 @@ func (m *ExecResponse) Reset() { *m = ExecResponse{} }
func (m *ExecResponse) String() string { return proto.CompactTextString(m) }
func (*ExecResponse) ProtoMessage() {}
func (*ExecResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_018e8d5b14a89d12, []int{3}
return fileDescriptor_79b974b8c77805fa, []int{3}
}
func (m *ExecResponse) XXX_Unmarshal(b []byte) error {
@@ -191,26 +195,139 @@ func init() {
proto.RegisterType((*ExecResponse)(nil), "go.micro.bot.ExecResponse")
}
func init() {
proto.RegisterFile("github.com/micro/go-micro/agent/proto/bot.proto", fileDescriptor_018e8d5b14a89d12)
func init() { proto.RegisterFile("agent/proto/bot.proto", fileDescriptor_79b974b8c77805fa) }
var fileDescriptor_79b974b8c77805fa = []byte{
// 234 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x3f, 0x4f, 0xc3, 0x30,
0x10, 0xc5, 0x1b, 0x28, 0x45, 0xbd, 0x84, 0xc5, 0x02, 0x14, 0x3a, 0x05, 0x4f, 0x9d, 0x5c, 0x09,
0x56, 0x24, 0x06, 0x04, 0x62, 0xce, 0x37, 0x48, 0xd2, 0x53, 0x14, 0xa9, 0xf1, 0x99, 0xb3, 0x23,
0xf1, 0x1d, 0xf8, 0xd2, 0xc8, 0x7f, 0x06, 0xab, 0xea, 0x76, 0xcf, 0x67, 0xbd, 0xf7, 0x7b, 0x07,
0x0f, 0xdd, 0x88, 0xda, 0x1d, 0x0c, 0x93, 0xa3, 0x43, 0x4f, 0x4e, 0x85, 0x49, 0x54, 0x23, 0xa9,
0x79, 0x1a, 0x98, 0x54, 0x4f, 0x4e, 0xde, 0x41, 0xf9, 0x8d, 0x27, 0xd3, 0xe2, 0xcf, 0x82, 0xd6,
0xc9, 0x2f, 0xa8, 0xa2, 0xb4, 0x86, 0xb4, 0x45, 0x71, 0x0f, 0x37, 0x8b, 0xed, 0x46, 0xac, 0x8b,
0xa6, 0xd8, 0x6f, 0xdb, 0x28, 0x44, 0x03, 0xe5, 0x11, 0xed, 0xc0, 0x93, 0x71, 0x13, 0xe9, 0xfa,
0x2a, 0xec, 0xf2, 0x27, 0xf9, 0x0c, 0xe5, 0xe7, 0x2f, 0x0e, 0xc9, 0x56, 0x08, 0x58, 0x77, 0x3c,
0xda, 0xba, 0x68, 0xae, 0xf7, 0xdb, 0x36, 0xcc, 0xf2, 0x0d, 0xaa, 0xf8, 0x25, 0x45, 0x3d, 0xc2,
0x86, 0xd1, 0x2e, 0x27, 0x17, 0xb2, 0xaa, 0x36, 0x29, 0x8f, 0x80, 0xcc, 0xc4, 0x29, 0x26, 0x8a,
0x97, 0xbf, 0x02, 0x6e, 0x3f, 0x68, 0x9e, 0x3b, 0x7d, 0x14, 0xef, 0xb0, 0xf6, 0xd0, 0xe2, 0x49,
0xe5, 0xd5, 0x54, 0xd6, 0x6b, 0xb7, 0xbb, 0xb4, 0x8a, 0xc1, 0x72, 0xe5, 0x0d, 0x3c, 0xca, 0xb9,
0x41, 0xd6, 0xe0, 0xdc, 0x20, 0x27, 0x97, 0xab, 0x7e, 0x13, 0x4e, 0xfb, 0xfa, 0x1f, 0x00, 0x00,
0xff, 0xff, 0xe8, 0x08, 0x5e, 0xad, 0x73, 0x01, 0x00, 0x00,
}
var fileDescriptor_018e8d5b14a89d12 = []byte{
// 246 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x50, 0x4d, 0x4b, 0xc4, 0x30,
0x10, 0xdd, 0xea, 0xba, 0xb2, 0xd3, 0x7a, 0x09, 0x22, 0x75, 0x4f, 0x35, 0xa7, 0xbd, 0x98, 0x80,
0x5e, 0x05, 0x0f, 0xa2, 0x78, 0xee, 0x3f, 0x68, 0xbb, 0x43, 0x2c, 0x6c, 0x3b, 0x35, 0x99, 0x82,
0xff, 0xc1, 0x3f, 0x2d, 0x4d, 0x72, 0x08, 0xc5, 0xdb, 0x7b, 0x79, 0xe1, 0x7d, 0x0c, 0x68, 0xd3,
0xf3, 0xd7, 0xdc, 0xaa, 0x8e, 0x06, 0x3d, 0xf4, 0x9d, 0x25, 0x6d, 0xe8, 0x31, 0x80, 0xc6, 0xe0,
0xc8, 0x7a, 0xb2, 0xc4, 0xa4, 0x5b, 0x62, 0xe5, 0x91, 0x28, 0x0c, 0x29, 0xaf, 0xab, 0x96, 0x58,
0xde, 0x40, 0xfe, 0x89, 0xe7, 0xa9, 0xc6, 0xef, 0x19, 0x1d, 0xcb, 0x0f, 0x28, 0x02, 0x75, 0x13,
0x8d, 0x0e, 0xc5, 0x2d, 0x5c, 0xcd, 0xae, 0x31, 0x58, 0x66, 0x55, 0x76, 0xdc, 0xd7, 0x81, 0x88,
0x0a, 0xf2, 0x13, 0xba, 0xce, 0xf6, 0x13, 0xf7, 0x34, 0x96, 0x17, 0x5e, 0x4b, 0x9f, 0xe4, 0x03,
0xe4, 0xef, 0x3f, 0xd8, 0x45, 0x5b, 0x21, 0x60, 0xdb, 0x58, 0xe3, 0xca, 0xac, 0xba, 0x3c, 0xee,
0x6b, 0x8f, 0xe5, 0x0b, 0x14, 0xe1, 0x4b, 0x8c, 0xba, 0x83, 0x9d, 0x45, 0x37, 0x9f, 0xd9, 0x67,
0x15, 0x75, 0x64, 0x4b, 0x05, 0xb4, 0x96, 0x6c, 0x8c, 0x09, 0xe4, 0xe9, 0x37, 0x83, 0xeb, 0x37,
0x1a, 0x86, 0x66, 0x3c, 0x89, 0x57, 0xd8, 0x2e, 0xa5, 0xc5, 0xbd, 0x4a, 0xa7, 0xa9, 0x64, 0xd7,
0xe1, 0xf0, 0x9f, 0x14, 0x82, 0xe5, 0x66, 0x31, 0x58, 0xaa, 0xac, 0x0d, 0x92, 0x05, 0x6b, 0x83,
0xb4, 0xb9, 0xdc, 0xb4, 0x3b, 0x7f, 0xda, 0xe7, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0xbd,
0x39, 0x29, 0x8d, 0x01, 0x00, 0x00,
// 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
// CommandClient is the client API for Command service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type CommandClient interface {
Help(ctx context.Context, in *HelpRequest, opts ...grpc.CallOption) (*HelpResponse, error)
Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (*ExecResponse, error)
}
type commandClient struct {
cc *grpc.ClientConn
}
func NewCommandClient(cc *grpc.ClientConn) CommandClient {
return &commandClient{cc}
}
func (c *commandClient) Help(ctx context.Context, in *HelpRequest, opts ...grpc.CallOption) (*HelpResponse, error) {
out := new(HelpResponse)
err := c.cc.Invoke(ctx, "/go.micro.bot.Command/Help", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *commandClient) Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (*ExecResponse, error) {
out := new(ExecResponse)
err := c.cc.Invoke(ctx, "/go.micro.bot.Command/Exec", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// CommandServer is the server API for Command service.
type CommandServer interface {
Help(context.Context, *HelpRequest) (*HelpResponse, error)
Exec(context.Context, *ExecRequest) (*ExecResponse, error)
}
// UnimplementedCommandServer can be embedded to have forward compatible implementations.
type UnimplementedCommandServer struct {
}
func (*UnimplementedCommandServer) Help(ctx context.Context, req *HelpRequest) (*HelpResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Help not implemented")
}
func (*UnimplementedCommandServer) Exec(ctx context.Context, req *ExecRequest) (*ExecResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Exec not implemented")
}
func RegisterCommandServer(s *grpc.Server, srv CommandServer) {
s.RegisterService(&_Command_serviceDesc, srv)
}
func _Command_Help_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelpRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CommandServer).Help(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.bot.Command/Help",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CommandServer).Help(ctx, req.(*HelpRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Command_Exec_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ExecRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CommandServer).Exec(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.bot.Command/Exec",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CommandServer).Exec(ctx, req.(*ExecRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Command_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.bot.Command",
HandlerType: (*CommandServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Help",
Handler: _Command_Help_Handler,
},
{
MethodName: "Exec",
Handler: _Command_Exec_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "agent/proto/bot.proto",
}

View File

@@ -1,5 +1,5 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/go-micro/agent/proto/bot.proto
// source: agent/proto/bot.proto
package go_micro_bot
@@ -11,8 +11,8 @@ import (
import (
context "context"
client "github.com/micro/go-micro/client"
server "github.com/micro/go-micro/server"
client "github.com/micro/go-micro/v2/client"
server "github.com/micro/go-micro/v2/server"
)
// Reference imports to suppress errors if they are not otherwise used.
@@ -44,12 +44,6 @@ type commandService struct {
}
func NewCommandService(name string, c client.Client) CommandService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.bot"
}
return &commandService{
c: c,
name: name,

View File

@@ -5,10 +5,24 @@ import (
"regexp"
"strings"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/server"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/server"
)
type Api interface {
// Register a http handler
Register(*Endpoint) error
// Register a route
Deregister(*Endpoint) error
// Init initialises the command line.
// It also parses further options.
//Init(...Option) error
// Options
//Options() Options
// String
String() string
}
// Endpoint is a mapping between an RPC method and HTTP endpoint
type Endpoint struct {
// RPC Method e.g. Greeter.Hello
@@ -23,6 +37,8 @@ type Endpoint struct {
Method []string
// HTTP Path e.g /greeter. Expect POSIX regex
Path []string
// Stream flag
Stream bool
}
// Service represents an API service
@@ -57,14 +73,25 @@ func Encode(e *Endpoint) map[string]string {
return nil
}
return map[string]string{
"endpoint": e.Name,
"description": e.Description,
"method": strings.Join(e.Method, ","),
"path": strings.Join(e.Path, ","),
"host": strings.Join(e.Host, ","),
"handler": e.Handler,
// endpoint map
ep := make(map[string]string)
// set vals only if they exist
set := func(k, v string) {
if len(v) == 0 {
return
}
ep[k] = v
}
set("endpoint", e.Name)
set("description", e.Description)
set("handler", e.Handler)
set("method", strings.Join(e.Method, ","))
set("path", strings.Join(e.Path, ","))
set("host", strings.Join(e.Host, ","))
return ep
}
// Decode decodes endpoint metadata into an endpoint

132
api/grpc_test.go Normal file
View File

@@ -0,0 +1,132 @@
package api_test
import (
"context"
"fmt"
"io/ioutil"
"log"
"net/http"
"testing"
"time"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/api"
ahandler "github.com/micro/go-micro/v2/api/handler"
apirpc "github.com/micro/go-micro/v2/api/handler/rpc"
"github.com/micro/go-micro/v2/api/router"
rstatic "github.com/micro/go-micro/v2/api/router/static"
bmemory "github.com/micro/go-micro/v2/broker/memory"
"github.com/micro/go-micro/v2/client"
gcli "github.com/micro/go-micro/v2/client/grpc"
rmemory "github.com/micro/go-micro/v2/registry/memory"
"github.com/micro/go-micro/v2/server"
gsrv "github.com/micro/go-micro/v2/server/grpc"
tgrpc "github.com/micro/go-micro/v2/transport/grpc"
pb "github.com/micro/go-micro/v2/server/grpc/proto"
)
// server is used to implement helloworld.GreeterServer.
type testServer struct {
msgCount int
}
// TestHello implements helloworld.GreeterServer
func (s *testServer) Call(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
rsp.Msg = "Hello " + req.Name
return nil
}
func TestApiAndGRPC(t *testing.T) {
r := rmemory.NewRegistry()
b := bmemory.NewBroker()
tr := tgrpc.NewTransport()
s := gsrv.NewServer(
server.Broker(b),
server.Name("foo"),
server.Registry(r),
server.Transport(tr),
)
c := gcli.NewClient(
client.Registry(r),
client.Broker(b),
client.Transport(tr),
)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
svc := micro.NewService(
micro.Server(s),
micro.Client(c),
micro.Broker(b),
micro.Registry(r),
micro.Transport(tr),
micro.Context(ctx))
h := &testServer{}
pb.RegisterTestHandler(s, h)
go func() {
if err := svc.Run(); err != nil {
t.Fatalf("failed to start: %v", err)
}
}()
time.Sleep(1 * time.Second)
// check registration
services, err := r.GetService("foo")
if err != nil || len(services) == 0 {
t.Fatalf("failed to get service: %v # %d", err, len(services))
}
router := rstatic.NewRouter(
router.WithHandler(apirpc.Handler),
router.WithRegistry(svc.Server().Options().Registry),
)
err = router.Register(&api.Endpoint{
Name: "foo.Test.Call",
Method: []string{"GET"},
Path: []string{"/api/v0/test/call/{name}"},
Handler: "rpc",
})
if err != nil {
t.Fatal(err)
}
hrpc := apirpc.NewHandler(
ahandler.WithService(svc),
ahandler.WithRouter(router),
)
hsrv := &http.Server{
Handler: hrpc,
Addr: "127.0.0.1:6543",
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
IdleTimeout: 20 * time.Second,
MaxHeaderBytes: 1024 * 1024 * 1, // 1Mb
}
go func() {
log.Println(hsrv.ListenAndServe())
}()
time.Sleep(1 * time.Second)
rsp, err := http.Get(fmt.Sprintf("http://%s/api/v0/test/call/TEST", hsrv.Addr))
if err != nil {
t.Fatalf("Failed to created http.Request: %v", err)
}
defer rsp.Body.Close()
buf, err := ioutil.ReadAll(rsp.Body)
if err != nil {
t.Fatal(err)
}
jsonMsg := `{"msg":"Hello TEST"}`
if string(buf) != jsonMsg {
t.Fatalf("invalid message received, parsing error %s != %s", buf, jsonMsg)
}
select {
case <-ctx.Done():
return
}
}

View File

@@ -4,13 +4,13 @@ package api
import (
"net/http"
goapi "github.com/micro/go-micro/api"
"github.com/micro/go-micro/api/handler"
api "github.com/micro/go-micro/api/proto"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/util/ctx"
goapi "github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
api "github.com/micro/go-micro/v2/api/proto"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/util/ctx"
)
type apiHandler struct {
@@ -24,6 +24,12 @@ const (
// API handler is the default handler which takes api.Request and returns api.Response
func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if a.opts.MaxRecvSize > 0 {
bsize = a.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
request, err := requestToProto(r)
if err != nil {
er := errors.InternalServerError("go.micro.api", err.Error())

View File

@@ -2,15 +2,20 @@ package api
import (
"fmt"
"io/ioutil"
"mime"
"net"
"net/http"
"strings"
api "github.com/micro/go-micro/api/proto"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/registry"
api "github.com/micro/go-micro/v2/api/proto"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v2/registry"
"github.com/oxtoacart/bpool"
)
var (
// need to calculate later to specify useful defaults
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
func requestToProto(r *http.Request) (*api.Request, error) {
@@ -39,9 +44,12 @@ func requestToProto(r *http.Request) (*api.Request, error) {
case "application/x-www-form-urlencoded":
// expect form vals in Post data
default:
data, _ := ioutil.ReadAll(r.Body)
req.Body = string(data)
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err = buf.ReadFrom(r.Body); err != nil {
return nil, err
}
req.Body = buf.String()
}
}

View File

@@ -3,17 +3,22 @@ package broker
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/gorilla/websocket"
"github.com/micro/go-micro/api/handler"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/util/log"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/logger"
"github.com/oxtoacart/bpool"
)
var (
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
const (
@@ -26,6 +31,7 @@ const (
)
type brokerHandler struct {
once atomic.Value
opts handler.Options
u websocket.Upgrader
}
@@ -42,7 +48,6 @@ type conn struct {
}
var (
once sync.Once
contentType = "text/plain"
)
@@ -135,7 +140,9 @@ func (c *conn) writeLoop() {
}()
if err != nil {
log.Log(err.Error())
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err.Error())
}
return
}
@@ -152,13 +159,25 @@ func (c *conn) writeLoop() {
}
func (b *brokerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if b.opts.MaxRecvSize > 0 {
bsize = b.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
br := b.opts.Service.Client().Options().Broker
// Setup the broker
once.Do(func() {
br.Init()
br.Connect()
})
if !b.once.Load().(bool) {
if err := br.Init(); err != nil {
http.Error(w, err.Error(), 500)
}
if err := br.Connect(); err != nil {
http.Error(w, err.Error(), 500)
}
b.once.Store(true)
}
// Parse
r.ParseForm()
@@ -183,14 +202,15 @@ func (b *brokerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// Read body
b, err := ioutil.ReadAll(r.Body)
if err != nil {
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
http.Error(w, err.Error(), 500)
return
}
// Set body
msg.Body = b
msg.Body = buf.Bytes()
// Set body
// Publish
br.Publish(topic, msg)
@@ -208,7 +228,9 @@ func (b *brokerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ws, err := b.u.Upgrade(w, r, nil)
if err != nil {
log.Log(err.Error())
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err.Error())
}
return
}
@@ -235,7 +257,7 @@ func (b *brokerHandler) String() string {
}
func NewHandler(opts ...handler.Option) handler.Handler {
return &brokerHandler{
h := &brokerHandler{
u: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
@@ -245,6 +267,8 @@ func NewHandler(opts ...handler.Option) handler.Handler {
},
opts: handler.NewOptions(opts...),
}
h.once.Store(true)
return h
}
func WithCors(cors map[string]bool, opts ...handler.Option) handler.Handler {

View File

@@ -7,12 +7,12 @@ import (
"regexp"
"strings"
"github.com/micro/go-micro/api/handler"
"github.com/micro/go-micro/util/ctx"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/util/ctx"
)
type event struct {
options handler.Options
opts handler.Options
}
var (
@@ -58,10 +58,17 @@ func evRoute(ns, p string) (string, string) {
}
func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if e.opts.MaxRecvSize > 0 {
bsize = e.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
// request to topic:event
// create event
// publish to topic
topic, _ := evRoute(e.options.Namespace, r.URL.Path)
topic, _ := evRoute(e.opts.Namespace, r.URL.Path)
// create event
ev, err := FromRequest(r)
@@ -71,7 +78,7 @@ func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// get client
c := e.options.Service.Client()
c := e.opts.Service.Client()
// create publication
p := c.NewMessage(topic, ev)
@@ -89,6 +96,6 @@ func (e *event) String() string {
func NewHandler(opts ...handler.Option) handler.Handler {
return &event{
options: handler.NewOptions(opts...),
opts: handler.NewOptions(opts...),
}
}

View File

@@ -24,7 +24,6 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"mime"
"net/http"
"strings"
@@ -32,7 +31,12 @@ import (
"unicode"
"github.com/google/uuid"
"gopkg.in/go-playground/validator.v9"
"github.com/oxtoacart/bpool"
validator "gopkg.in/go-playground/validator.v9"
)
var (
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
const (
@@ -97,10 +101,12 @@ func FromRequest(r *http.Request) (*Event, error) {
// Read request body
body := []byte{}
if r.Body != nil {
body, err = ioutil.ReadAll(r.Body)
if err != nil {
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
return nil, err
}
body = buf.Bytes()
}
var event *Event

View File

@@ -4,7 +4,6 @@ package event
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"path"
"regexp"
@@ -12,13 +11,18 @@ import (
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/api/handler"
proto "github.com/micro/go-micro/api/proto"
"github.com/micro/go-micro/util/ctx"
"github.com/micro/go-micro/v2/api/handler"
proto "github.com/micro/go-micro/v2/api/proto"
"github.com/micro/go-micro/v2/util/ctx"
"github.com/oxtoacart/bpool"
)
var (
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
type event struct {
options handler.Options
opts handler.Options
}
var (
@@ -64,11 +68,18 @@ func evRoute(ns, p string) (string, string) {
}
func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if e.opts.MaxRecvSize > 0 {
bsize = e.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
// request to topic:event
// create event
// publish to topic
topic, action := evRoute(e.options.Namespace, r.URL.Path)
topic, action := evRoute(e.opts.Namespace, r.URL.Path)
// create event
ev := &proto.Event{
@@ -96,16 +107,18 @@ func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bytes, _ := json.Marshal(r.URL.Query())
ev.Data = string(bytes)
} else {
b, err := ioutil.ReadAll(r.Body)
if err != nil {
// Read body
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
http.Error(w, err.Error(), 500)
return
}
ev.Data = string(b)
ev.Data = buf.String()
}
// get client
c := e.options.Service.Client()
c := e.opts.Service.Client()
// create publication
p := c.NewMessage(topic, ev)
@@ -123,6 +136,6 @@ func (e *event) String() string {
func NewHandler(opts ...handler.Option) handler.Handler {
return &event{
options: handler.NewOptions(opts...),
opts: handler.NewOptions(opts...),
}
}

View File

@@ -8,9 +8,9 @@ import (
"net/http/httputil"
"net/url"
"github.com/micro/go-micro/api"
"github.com/micro/go-micro/api/handler"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/client/selector"
)
const (

View File

@@ -6,17 +6,15 @@ import (
"net/http/httptest"
"testing"
"github.com/micro/go-micro/api/handler"
"github.com/micro/go-micro/api/router"
regRouter "github.com/micro/go-micro/api/router/registry"
"github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/registry/memory"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/api/router"
regRouter "github.com/micro/go-micro/v2/api/router/registry"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/memory"
)
func testHttp(t *testing.T, path, service, ns string) {
r := memory.NewRegistry()
cmd.DefaultCmd = cmd.NewCmd(cmd.Registry(&r))
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
@@ -57,6 +55,7 @@ func testHttp(t *testing.T, path, service, ns string) {
rt := regRouter.NewRouter(
router.WithHandler("http"),
router.WithNamespace(ns),
router.WithRegistry(r),
)
p := NewHandler(handler.WithRouter(rt))

View File

@@ -1,14 +1,19 @@
package handler
import (
"github.com/micro/go-micro"
"github.com/micro/go-micro/api/router"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/api/router"
)
var (
DefaultMaxRecvSize int64 = 1024 * 1024 * 100 // 10Mb
)
type Options struct {
Namespace string
Router router.Router
Service micro.Service
MaxRecvSize int64
Namespace string
Router router.Router
Service micro.Service
}
type Option func(o *Options)
@@ -30,6 +35,10 @@ func NewOptions(opts ...Option) Options {
WithNamespace("go.micro.api")(&options)
}
if options.MaxRecvSize == 0 {
options.MaxRecvSize = DefaultMaxRecvSize
}
return options
}
@@ -53,3 +62,10 @@ func WithService(s micro.Service) Option {
o.Service = s
}
}
// WithmaxRecvSize specifies max body size
func WithMaxRecvSize(size int64) Option {
return func(o *Options) {
o.MaxRecvSize = size
}
}

View File

@@ -3,14 +3,18 @@ package registry
import (
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"time"
"github.com/gorilla/websocket"
"github.com/micro/go-micro/api/handler"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/registry"
"github.com/oxtoacart/bpool"
)
var (
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
const (
@@ -29,12 +33,15 @@ type registryHandler struct {
func (rh *registryHandler) add(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
b, err := ioutil.ReadAll(r.Body)
if err != nil {
defer r.Body.Close()
// Read body
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
http.Error(w, err.Error(), 500)
return
}
defer r.Body.Close()
var opts []registry.RegisterOption
@@ -47,13 +54,11 @@ func (rh *registryHandler) add(w http.ResponseWriter, r *http.Request) {
}
var service *registry.Service
err = json.Unmarshal(b, &service)
if err != nil {
if err := json.NewDecoder(buf).Decode(&service); err != nil {
http.Error(w, err.Error(), 500)
return
}
err = rh.reg.Register(service, opts...)
if err != nil {
if err := rh.reg.Register(service, opts...); err != nil {
http.Error(w, err.Error(), 500)
return
}
@@ -61,21 +66,22 @@ func (rh *registryHandler) add(w http.ResponseWriter, r *http.Request) {
func (rh *registryHandler) del(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
b, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer r.Body.Close()
var service *registry.Service
err = json.Unmarshal(b, &service)
if err != nil {
// Read body
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
http.Error(w, err.Error(), 500)
return
}
err = rh.reg.Deregister(service)
if err != nil {
var service *registry.Service
if err := json.NewDecoder(buf).Decode(&service); err != nil {
http.Error(w, err.Error(), 500)
return
}
if err := rh.reg.Deregister(service); err != nil {
http.Error(w, err.Error(), 500)
return
}
@@ -187,6 +193,13 @@ func watch(rw registry.Watcher, w http.ResponseWriter, r *http.Request) {
}
func (rh *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if rh.opts.MaxRecvSize > 0 {
bsize = rh.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
switch r.Method {
case "GET":
rh.get(w, r)

View File

@@ -4,23 +4,26 @@ package rpc
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"strconv"
"strings"
jsonpatch "github.com/evanphx/json-patch/v5"
"github.com/joncalhoun/qson"
"github.com/micro/go-micro/api"
"github.com/micro/go-micro/api/handler"
proto "github.com/micro/go-micro/api/internal/proto"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/codec"
"github.com/micro/go-micro/codec/jsonrpc"
"github.com/micro/go-micro/codec/protorpc"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/util/ctx"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/api/internal/proto"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/codec/jsonrpc"
"github.com/micro/go-micro/v2/codec/protorpc"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/util/ctx"
"github.com/oxtoacart/bpool"
)
const (
@@ -44,6 +47,8 @@ var (
"application/proto-rpc",
"application/octet-stream",
}
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
type rpcHandler struct {
@@ -68,6 +73,13 @@ func strategy(services []*registry.Service) selector.Strategy {
}
func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if h.opts.MaxRecvSize > 0 {
bsize = h.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
defer r.Body.Close()
var service *api.Service
@@ -90,7 +102,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// only allow post when we have the router
if r.Method != "GET" && (h.opts.Router != nil && r.Method != "POST") {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
writeError(w, r, errors.MethodNotAllowed("go.micro.api", "method not allowed"))
return
}
@@ -104,9 +116,19 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// micro client
c := h.opts.Service.Client()
// create context
cx := ctx.FromRequest(r)
// if stream we currently only support json
if isStream(r, service) {
serveWebsocket(cx, w, r, service, c)
return
}
// create strategy
so := selector.WithStrategy(strategy(service.Services))
// walk the standard call path
// get payload
br, err := requestPayload(r)
if err != nil {
@@ -114,9 +136,6 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
// create context
cx := ctx.FromRequest(r)
var rsp []byte
switch {
@@ -145,7 +164,12 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// marshall response
rsp, _ = response.Marshal()
rsp, err = response.Marshal()
if err != nil {
writeError(w, r, err)
return
}
default:
// if json codec is not present set to json
if !hasCodec(ct, jsonCodecs) {
@@ -176,7 +200,11 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// marshall response
rsp, _ = response.MarshalJSON()
rsp, err = response.MarshalJSON()
if err != nil {
writeError(w, r, err)
return
}
}
// write the response
@@ -200,49 +228,140 @@ func hasCodec(ct string, codecs []string) bool {
// If the request is a GET the query string parameters are extracted and marshaled to JSON and the raw bytes are returned.
// If the request method is a POST the request body is read and returned
func requestPayload(r *http.Request) ([]byte, error) {
var err error
// we have to decode json-rpc and proto-rpc because we suck
// well actually because there's no proxy codec right now
switch r.Header.Get("Content-Type") {
case "application/json-rpc":
ct := r.Header.Get("Content-Type")
switch {
case strings.Contains(ct, "application/json-rpc"):
msg := codec.Message{
Type: codec.Request,
Header: make(map[string]string),
}
c := jsonrpc.NewCodec(&buffer{r.Body})
if err := c.ReadHeader(&msg, codec.Request); err != nil {
if err = c.ReadHeader(&msg, codec.Request); err != nil {
return nil, err
}
var raw json.RawMessage
if err := c.ReadBody(&raw); err != nil {
if err = c.ReadBody(&raw); err != nil {
return nil, err
}
return ([]byte)(raw), nil
case "application/proto-rpc", "application/octet-stream":
case strings.Contains(ct, "application/proto-rpc"), strings.Contains(ct, "application/octet-stream"):
msg := codec.Message{
Type: codec.Request,
Header: make(map[string]string),
}
c := protorpc.NewCodec(&buffer{r.Body})
if err := c.ReadHeader(&msg, codec.Request); err != nil {
if err = c.ReadHeader(&msg, codec.Request); err != nil {
return nil, err
}
var raw proto.Message
if err := c.ReadBody(&raw); err != nil {
if err = c.ReadBody(&raw); err != nil {
return nil, err
}
b, _ := raw.Marshal()
return b, nil
return raw.Marshal()
case strings.Contains(ct, "application/www-x-form-urlencoded"):
r.ParseForm()
// generate a new set of values from the form
vals := make(map[string]string)
for k, v := range r.Form {
vals[k] = strings.Join(v, ",")
}
// marshal
return json.Marshal(vals)
// TODO: application/grpc
}
// otherwise as per usual
ctx := r.Context()
// dont user meadata.FromContext as it mangles names
md, ok := ctx.Value(metadata.MetadataKey{}).(metadata.Metadata)
if !ok {
md = make(map[string]string)
}
// allocate maximum
matches := make(map[string]string, len(md))
for k, v := range md {
if strings.HasPrefix(k, "x-api-field-") {
matches[strings.TrimPrefix(k, "x-api-field-")] = v
}
delete(md, k)
}
// restore context without fields
ctx = metadata.NewContext(ctx, md)
*r = *r.WithContext(ctx)
req := make(map[string]interface{}, len(md))
for k, v := range matches {
ps := strings.Split(k, ".")
if len(ps) == 1 {
req[k] = v
continue
}
em := make(map[string]interface{})
em[ps[len(ps)-1]] = v
for i := len(ps) - 2; i > 0; i-- {
nm := make(map[string]interface{})
nm[ps[i]] = em
em = nm
}
req[ps[0]] = em
}
pathbuf := []byte("{}")
if len(req) > 0 {
pathbuf, err = json.Marshal(req)
if err != nil {
return nil, err
}
}
urlbuf := []byte("{}")
if len(r.URL.RawQuery) > 0 {
urlbuf, err = qson.ToJSON(r.URL.RawQuery)
if err != nil {
return nil, err
}
}
out, err := jsonpatch.MergeMergePatches(urlbuf, pathbuf)
if err != nil {
return nil, err
}
switch r.Method {
case "GET":
if len(r.URL.RawQuery) > 0 {
return qson.ToJSON(r.URL.RawQuery)
// empty response
if strings.Contains(ct, "application/json") && string(out) == "{}" {
return out, nil
} else if string(out) == "{}" && !strings.Contains(ct, "application/json") {
return []byte{}, nil
}
case "PATCH", "POST":
return ioutil.ReadAll(r.Body)
return out, nil
case "PATCH", "POST", "PUT", "DELETE":
bodybuf := []byte("{}")
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
return nil, err
}
if b := buf.Bytes(); len(b) > 0 {
bodybuf = b
} else {
return []byte{}, nil
}
if out, err = jsonpatch.MergeMergePatches(out, bodybuf); err == nil {
return out, nil
}
//fallback to previous unknown behaviour
return bodybuf, nil
}
return []byte{}, nil
@@ -274,7 +393,12 @@ func writeError(w http.ResponseWriter, r *http.Request, err error) {
w.Header().Set("grpc-message", ce.Detail)
}
w.Write([]byte(ce.Error()))
_, werr := w.Write([]byte(ce.Error()))
if werr != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(werr)
}
}
}
func writeResponse(w http.ResponseWriter, r *http.Request, rsp []byte) {
@@ -289,8 +413,19 @@ func writeResponse(w http.ResponseWriter, r *http.Request, rsp []byte) {
w.Header().Set("grpc-message", "")
}
// write 204 status if rsp is nil
if len(rsp) == 0 {
w.WriteHeader(http.StatusNoContent)
}
// write response
w.Write(rsp)
_, err := w.Write(rsp)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
}
}
func NewHandler(opts ...handler.Option) handler.Handler {

View File

@@ -7,7 +7,7 @@ import (
"testing"
"github.com/golang/protobuf/proto"
"github.com/micro/go-micro/api/proto"
go_api "github.com/micro/go-micro/v2/api/proto"
)
func TestRequestPayloadFromRequest(t *testing.T) {
@@ -27,6 +27,23 @@ func TestRequestPayloadFromRequest(t *testing.T) {
t.Fatal("Failed to marshal proto to JSON ", err)
}
jsonUrlBytes := []byte(`{"key1":"val1","key2":"val2","name":"Test"}`)
t.Run("extracting a json from a POST request with url params", func(t *testing.T) {
r, err := http.NewRequest("POST", "http://localhost/my/path?key1=val1&key2=val2", bytes.NewReader(jsonBytes))
if err != nil {
t.Fatalf("Failed to created http.Request: %v", err)
}
extByte, err := requestPayload(r)
if err != nil {
t.Fatalf("Failed to extract payload from request: %v", err)
}
if string(extByte) != string(jsonUrlBytes) {
t.Fatalf("Expected %v and %v to match", string(extByte), jsonUrlBytes)
}
})
t.Run("extracting a proto from a POST request", func(t *testing.T) {
r, err := http.NewRequest("POST", "http://localhost/my/path", bytes.NewReader(protoBytes))
if err != nil {

150
api/handler/rpc/stream.go Normal file
View File

@@ -0,0 +1,150 @@
package rpc
import (
"context"
"encoding/json"
"net/http"
"strings"
"github.com/gorilla/websocket"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
// serveWebsocket will stream rpc back over websockets assuming json
func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, service *api.Service, c client.Client) {
// upgrade the connection
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
// close on exit
defer conn.Close()
// wait for the first request so we know
_, p, err := conn.ReadMessage()
if err != nil {
return
}
// send to backend
// default to trying json
var request json.RawMessage
// if the extracted payload isn't empty lets use it
if len(p) > 0 {
request = json.RawMessage(p)
}
// create a request to the backend
req := c.NewRequest(
service.Name,
service.Endpoint.Name,
&request,
client.WithContentType("application/json"),
)
so := selector.WithStrategy(strategy(service.Services))
// create a new stream
stream, err := c.Stream(ctx, req, client.WithSelectOption(so))
if err != nil {
return
}
// send the first request for the client
// since
if err := stream.Send(request); err != nil {
return
}
go writeLoop(conn, stream)
resp := stream.Response()
// receive from stream and send to client
for {
// read backend response body
body, err := resp.Read()
if err != nil {
return
}
// write the response
if err := conn.WriteMessage(websocket.TextMessage, body); err != nil {
return
}
}
}
// writeLoop
func writeLoop(conn *websocket.Conn, stream client.Stream) {
// close stream when done
defer stream.Close()
for {
_, p, err := conn.ReadMessage()
if err != nil {
return
}
// send to backend
// default to trying json
var request json.RawMessage
// if the extracted payload isn't empty lets use it
if len(p) > 0 {
request = json.RawMessage(p)
}
if err := stream.Send(request); err != nil {
return
}
}
}
func isStream(r *http.Request, srv *api.Service) bool {
// check if it's a web socket
if !isWebSocket(r) {
return false
}
// check if the endpoint supports streaming
for _, service := range srv.Services {
for _, ep := range service.Endpoints {
// skip if it doesn't match the name
if ep.Name != srv.Endpoint.Name {
continue
}
// matched if the name
if v := ep.Metadata["stream"]; v == "true" {
return true
}
}
}
return false
}
func isWebSocket(r *http.Request) bool {
contains := func(key, val string) bool {
vv := strings.Split(r.Header.Get(key), ",")
for _, v := range vv {
if val == strings.ToLower(strings.TrimSpace(v)) {
return true
}
}
return false
}
if contains("Connection", "upgrade") && contains("Upgrade", "websocket") {
return true
}
return false
}

View File

@@ -11,9 +11,9 @@ import (
"net/url"
"strings"
"github.com/micro/go-micro/api"
"github.com/micro/go-micro/api/handler"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/client/selector"
)
const (

View File

@@ -1,5 +1,5 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: github.com/micro/go-micro/api/proto/api.proto
// source: api/proto/api.proto
package go_api
@@ -32,7 +32,7 @@ func (m *Pair) Reset() { *m = Pair{} }
func (m *Pair) String() string { return proto.CompactTextString(m) }
func (*Pair) ProtoMessage() {}
func (*Pair) Descriptor() ([]byte, []int) {
return fileDescriptor_7b6696ef87ec1943, []int{0}
return fileDescriptor_2df576b66d12087a, []int{0}
}
func (m *Pair) XXX_Unmarshal(b []byte) error {
@@ -86,7 +86,7 @@ func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) {
return fileDescriptor_7b6696ef87ec1943, []int{1}
return fileDescriptor_2df576b66d12087a, []int{1}
}
func (m *Request) XXX_Unmarshal(b []byte) error {
@@ -171,7 +171,7 @@ func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) {
return fileDescriptor_7b6696ef87ec1943, []int{2}
return fileDescriptor_2df576b66d12087a, []int{2}
}
func (m *Response) XXX_Unmarshal(b []byte) error {
@@ -235,7 +235,7 @@ func (m *Event) Reset() { *m = Event{} }
func (m *Event) String() string { return proto.CompactTextString(m) }
func (*Event) ProtoMessage() {}
func (*Event) Descriptor() ([]byte, []int) {
return fileDescriptor_7b6696ef87ec1943, []int{3}
return fileDescriptor_2df576b66d12087a, []int{3}
}
func (m *Event) XXX_Unmarshal(b []byte) error {
@@ -303,36 +303,33 @@ func init() {
proto.RegisterMapType((map[string]*Pair)(nil), "go.api.Event.HeaderEntry")
}
func init() {
proto.RegisterFile("github.com/micro/go-micro/api/proto/api.proto", fileDescriptor_7b6696ef87ec1943)
}
func init() { proto.RegisterFile("api/proto/api.proto", fileDescriptor_2df576b66d12087a) }
var fileDescriptor_7b6696ef87ec1943 = []byte{
// 408 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0x4d, 0x8f, 0xd3, 0x30,
0x10, 0x55, 0xe2, 0x24, 0xbb, 0x99, 0x22, 0x84, 0x7c, 0x40, 0x66, 0x59, 0xa1, 0x2a, 0xa7, 0x0a,
0xa9, 0x29, 0xec, 0x72, 0x40, 0x5c, 0xa1, 0x5a, 0x8e, 0x2b, 0xff, 0x03, 0x77, 0x63, 0x25, 0x16,
0x4d, 0x1c, 0x62, 0xa7, 0x52, 0x7f, 0x1c, 0x07, 0x7e, 0x06, 0xff, 0x06, 0x79, 0xec, 0x7e, 0x50,
0x95, 0x0b, 0xf4, 0xf6, 0x62, 0xbf, 0x79, 0xf3, 0xe6, 0x8d, 0x03, 0xf3, 0x5a, 0xd9, 0x66, 0x5c,
0x95, 0x4f, 0xba, 0x5d, 0xb4, 0xea, 0x69, 0xd0, 0x8b, 0x5a, 0xcf, 0x3d, 0x10, 0xbd, 0x5a, 0xf4,
0x83, 0xb6, 0x88, 0x4a, 0x44, 0x34, 0xab, 0x75, 0x29, 0x7a, 0x55, 0xbc, 0x83, 0xe4, 0x51, 0xa8,
0x81, 0xbe, 0x00, 0xf2, 0x4d, 0x6e, 0x59, 0x34, 0x8d, 0x66, 0x39, 0x77, 0x90, 0xbe, 0x84, 0x6c,
0x23, 0xd6, 0xa3, 0x34, 0x2c, 0x9e, 0x92, 0x59, 0xce, 0xc3, 0x57, 0xf1, 0x93, 0xc0, 0x15, 0x97,
0xdf, 0x47, 0x69, 0xac, 0xe3, 0xb4, 0xd2, 0x36, 0xba, 0x0a, 0x85, 0xe1, 0x8b, 0x52, 0x48, 0x7a,
0x61, 0x1b, 0x16, 0xe3, 0x29, 0x62, 0x7a, 0x0f, 0x59, 0x23, 0x45, 0x25, 0x07, 0x46, 0xa6, 0x64,
0x36, 0xb9, 0x7b, 0x5d, 0x7a, 0x0b, 0x65, 0x10, 0x2b, 0xbf, 0xe2, 0xed, 0xb2, 0xb3, 0xc3, 0x96,
0x07, 0x2a, 0x7d, 0x0b, 0xa4, 0x96, 0x96, 0x25, 0x58, 0xc1, 0x4e, 0x2b, 0x1e, 0xa4, 0xf5, 0x74,
0x47, 0xa2, 0x73, 0x48, 0x7a, 0x6d, 0x2c, 0x4b, 0x91, 0xfc, 0xea, 0x94, 0xfc, 0xa8, 0x4d, 0x60,
0x23, 0xcd, 0x79, 0x5c, 0xe9, 0x6a, 0xcb, 0x32, 0xef, 0xd1, 0x61, 0x97, 0xc2, 0x38, 0xac, 0xd9,
0x95, 0x4f, 0x61, 0x1c, 0xd6, 0x37, 0x0f, 0x30, 0x39, 0xf2, 0x75, 0x26, 0xa6, 0x02, 0x52, 0x0c,
0x06, 0x67, 0x9d, 0xdc, 0x3d, 0xdb, 0xb5, 0x75, 0xa9, 0x72, 0x7f, 0xf5, 0x29, 0xfe, 0x18, 0xdd,
0x7c, 0x81, 0xeb, 0x9d, 0xdd, 0xff, 0x50, 0x59, 0x42, 0xbe, 0x9f, 0xe3, 0xdf, 0x65, 0x8a, 0x1f,
0x11, 0x5c, 0x73, 0x69, 0x7a, 0xdd, 0x19, 0x49, 0xdf, 0x00, 0x18, 0x2b, 0xec, 0x68, 0x3e, 0xeb,
0x4a, 0xa2, 0x5a, 0xca, 0x8f, 0x4e, 0xe8, 0x87, 0xfd, 0xe2, 0x62, 0x4c, 0xf6, 0xf6, 0x90, 0xac,
0x57, 0x38, 0xbb, 0xb9, 0x5d, 0xbc, 0xe4, 0x10, 0xef, 0xc5, 0xc2, 0x2c, 0x7e, 0x45, 0x90, 0x2e,
0x37, 0xb2, 0xc3, 0x2d, 0x76, 0xa2, 0x95, 0x41, 0x04, 0x31, 0x7d, 0x0e, 0xb1, 0xaa, 0xc2, 0xdb,
0x8b, 0x55, 0x45, 0x6f, 0x21, 0xb7, 0xaa, 0x95, 0xc6, 0x8a, 0xb6, 0x47, 0x3f, 0x84, 0x1f, 0x0e,
0xe8, 0xfb, 0xfd, 0x78, 0xc9, 0x9f, 0x0f, 0x07, 0x1b, 0xfc, 0x6d, 0xb6, 0x4a, 0x58, 0xc1, 0x52,
0xdf, 0xd4, 0xe1, 0x8b, 0xcd, 0xb6, 0xca, 0xf0, 0x07, 0xbd, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff,
0x97, 0xf3, 0x59, 0x6e, 0xd1, 0x03, 0x00, 0x00,
var fileDescriptor_2df576b66d12087a = []byte{
// 393 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0xcd, 0xce, 0xd3, 0x30,
0x10, 0x54, 0xe2, 0x24, 0x6d, 0xb6, 0x08, 0x21, 0x23, 0x21, 0x53, 0x2a, 0x54, 0xe5, 0x54, 0x21,
0x91, 0x42, 0xcb, 0x01, 0x71, 0x85, 0xaa, 0x1c, 0x2b, 0xbf, 0x81, 0xab, 0x58, 0x6d, 0x44, 0x13,
0x9b, 0xd8, 0xa9, 0xd4, 0x87, 0xe3, 0xc0, 0x63, 0xf0, 0x36, 0xc8, 0x1b, 0xf7, 0xe7, 0xab, 0xfa,
0x5d, 0xbe, 0xaf, 0xb7, 0x89, 0x3d, 0x3b, 0x3b, 0x3b, 0xeb, 0xc0, 0x6b, 0xa1, 0xcb, 0xa9, 0x6e,
0x94, 0x55, 0x53, 0xa1, 0xcb, 0x1c, 0x11, 0x4d, 0x36, 0x2a, 0x17, 0xba, 0xcc, 0x3e, 0x41, 0xb4,
0x12, 0x65, 0x43, 0x5f, 0x01, 0xf9, 0x25, 0x0f, 0x2c, 0x18, 0x07, 0x93, 0x94, 0x3b, 0x48, 0xdf,
0x40, 0xb2, 0x17, 0xbb, 0x56, 0x1a, 0x16, 0x8e, 0xc9, 0x24, 0xe5, 0xfe, 0x2b, 0xfb, 0x4b, 0xa0,
0xc7, 0xe5, 0xef, 0x56, 0x1a, 0xeb, 0x38, 0x95, 0xb4, 0x5b, 0x55, 0xf8, 0x42, 0xff, 0x45, 0x29,
0x44, 0x5a, 0xd8, 0x2d, 0x0b, 0xf1, 0x14, 0x31, 0x9d, 0x43, 0xb2, 0x95, 0xa2, 0x90, 0x0d, 0x23,
0x63, 0x32, 0x19, 0xcc, 0xde, 0xe5, 0x9d, 0x85, 0xdc, 0x8b, 0xe5, 0x3f, 0xf1, 0x76, 0x51, 0xdb,
0xe6, 0xc0, 0x3d, 0x95, 0x7e, 0x00, 0xb2, 0x91, 0x96, 0x45, 0x58, 0xc1, 0xae, 0x2b, 0x96, 0xd2,
0x76, 0x74, 0x47, 0xa2, 0x1f, 0x21, 0xd2, 0xca, 0x58, 0x16, 0x23, 0xf9, 0xed, 0x35, 0x79, 0xa5,
0x8c, 0x67, 0x23, 0xcd, 0x79, 0x5c, 0xab, 0xe2, 0xc0, 0x92, 0xce, 0xa3, 0xc3, 0x2e, 0x85, 0xb6,
0xd9, 0xb1, 0x5e, 0x97, 0x42, 0xdb, 0xec, 0x86, 0x4b, 0x18, 0x5c, 0xf8, 0xba, 0x11, 0x53, 0x06,
0x31, 0x06, 0x83, 0xb3, 0x0e, 0x66, 0x2f, 0x8e, 0x6d, 0x5d, 0xaa, 0xbc, 0xbb, 0xfa, 0x16, 0x7e,
0x0d, 0x86, 0x3f, 0xa0, 0x7f, 0xb4, 0xfb, 0x0c, 0x95, 0x05, 0xa4, 0xa7, 0x39, 0x9e, 0x2e, 0x93,
0xfd, 0x09, 0xa0, 0xcf, 0xa5, 0xd1, 0xaa, 0x36, 0x92, 0xbe, 0x07, 0x30, 0x56, 0xd8, 0xd6, 0x7c,
0x57, 0x85, 0x44, 0xb5, 0x98, 0x5f, 0x9c, 0xd0, 0x2f, 0xa7, 0xc5, 0x85, 0x98, 0xec, 0xe8, 0x9c,
0x6c, 0xa7, 0x70, 0x73, 0x73, 0xc7, 0x78, 0xc9, 0x39, 0xde, 0xbb, 0x85, 0x99, 0xfd, 0x0b, 0x20,
0x5e, 0xec, 0x65, 0x8d, 0x5b, 0xac, 0x45, 0x25, 0xbd, 0x08, 0x62, 0xfa, 0x12, 0xc2, 0xb2, 0xf0,
0x6f, 0x2f, 0x2c, 0x0b, 0x3a, 0x82, 0xd4, 0x96, 0x95, 0x34, 0x56, 0x54, 0x1a, 0xfd, 0x10, 0x7e,
0x3e, 0xa0, 0x9f, 0x4f, 0xe3, 0x45, 0x0f, 0x1f, 0x0e, 0x36, 0x78, 0x6c, 0xb6, 0x42, 0x58, 0xc1,
0xe2, 0xae, 0xa9, 0xc3, 0x77, 0x9b, 0x6d, 0x9d, 0xe0, 0x0f, 0x3a, 0xff, 0x1f, 0x00, 0x00, 0xff,
0xff, 0xd4, 0x6d, 0x70, 0x51, 0xb7, 0x03, 0x00, 0x00,
}

View File

@@ -1,5 +1,5 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/go-micro/api/proto/api.proto
// source: api/proto/api.proto
package go_api

View File

@@ -6,7 +6,7 @@ import (
"net/http"
"strings"
"github.com/micro/go-micro/api/resolver"
"github.com/micro/go-micro/v2/api/resolver"
)
type Resolver struct{}

View File

@@ -4,7 +4,7 @@ package host
import (
"net/http"
"github.com/micro/go-micro/api/resolver"
"github.com/micro/go-micro/v2/api/resolver"
)
type Resolver struct{}

View File

@@ -4,7 +4,7 @@ package micro
import (
"net/http"
"github.com/micro/go-micro/api/resolver"
"github.com/micro/go-micro/v2/api/resolver"
)
// default resolver for legacy purposes

View File

@@ -18,6 +18,11 @@ func apiRoute(p string) (string, string) {
p = strings.TrimPrefix(p, "/")
parts := strings.Split(p, "/")
// if we have 1 part assume name Name.Call
if len(parts) == 1 && len(parts[0]) > 0 {
return parts[0], methodName(append(parts, "Call"))
}
// If we've got two or less parts
// Use first part as service
// Use all parts as method

View File

@@ -6,7 +6,7 @@ import (
"net/http"
"strings"
"github.com/micro/go-micro/api/resolver"
"github.com/micro/go-micro/v2/api/resolver"
)
type Resolver struct{}

View File

@@ -7,7 +7,7 @@ import (
"regexp"
"strings"
"github.com/micro/go-micro/api/resolver"
"github.com/micro/go-micro/v2/api/resolver"
)
type Resolver struct{}

View File

@@ -1,10 +1,9 @@
package router
import (
"github.com/micro/go-micro/api/resolver"
"github.com/micro/go-micro/api/resolver/micro"
"github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v2/api/resolver/micro"
"github.com/micro/go-micro/v2/registry"
)
type Options struct {
@@ -19,7 +18,7 @@ type Option func(o *Options)
func NewOptions(opts ...Option) Options {
options := Options{
Handler: "meta",
Registry: *cmd.DefaultOptions().Registry,
Registry: registry.DefaultRegistry,
}
for _, o := range opts {

View File

@@ -4,17 +4,17 @@ package registry
import (
"errors"
"fmt"
"log"
"net/http"
"regexp"
"strings"
"sync"
"time"
"github.com/micro/go-micro/api"
"github.com/micro/go-micro/api/router"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/registry/cache"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/router"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/cache"
)
// router is the default router
@@ -68,7 +68,9 @@ func (r *registryRouter) refresh() {
services, err := r.opts.Registry.ListServices()
if err != nil {
attempts++
log.Println("Error listing endpoints", err)
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("unable to list services: %v", err)
}
time.Sleep(time.Duration(attempts) * time.Second)
continue
}
@@ -83,6 +85,9 @@ func (r *registryRouter) refresh() {
}
service, err := r.rc.GetService(s.Name)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("unable to get service: %v", err)
}
continue
}
r.store(service)
@@ -107,6 +112,9 @@ func (r *registryRouter) process(res *registry.Result) {
// get entry from cache
service, err := r.rc.GetService(res.Service.Name)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("unable to get service: %v", err)
}
return
}
@@ -136,6 +144,9 @@ func (r *registryRouter) store(services []*registry.Service) {
// if we got nothing skip
if err := api.Validate(end); err != nil {
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
logger.Tracef("endpoint validation failed: %v", err)
}
continue
}
@@ -188,7 +199,9 @@ func (r *registryRouter) watch() {
w, err := r.opts.Registry.Watch()
if err != nil {
attempts++
log.Println("Error watching endpoints", err)
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("error watching endpoints: %v", err)
}
time.Sleep(time.Duration(attempts) * time.Second)
continue
}
@@ -211,7 +224,9 @@ func (r *registryRouter) watch() {
// process next event
res, err := w.Next()
if err != nil {
log.Println("Error getting next endpoint", err)
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("error getting next endoint: %v", err)
}
close(ch)
break
}
@@ -235,6 +250,14 @@ func (r *registryRouter) Close() error {
return nil
}
func (r *registryRouter) Register(ep *api.Endpoint) error {
return nil
}
func (r *registryRouter) Deregister(ep *api.Endpoint) error {
return nil
}
func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
if r.isClosed() {
return nil, errors.New("router closed")

View File

@@ -6,7 +6,7 @@ import (
"net/url"
"testing"
"github.com/micro/go-micro/api"
"github.com/micro/go-micro/v2/api"
)
func TestSetNamespace(t *testing.T) {

View File

@@ -4,7 +4,7 @@ package router
import (
"net/http"
"github.com/micro/go-micro/api"
"github.com/micro/go-micro/v2/api"
)
// Router is used to determine an endpoint for a request
@@ -15,6 +15,10 @@ type Router interface {
Close() error
// Endpoint returns an api.Service endpoint or an error if it does not exist
Endpoint(r *http.Request) (*api.Service, error)
// Register endpoint in router
Register(ep *api.Endpoint) error
// Deregister endpoint from router
Deregister(ep *api.Endpoint) error
// Route returns an api.Service route
Route(r *http.Request) (*api.Service, error)
}

304
api/router/static/static.go Normal file
View File

@@ -0,0 +1,304 @@
package static
import (
"context"
"errors"
"fmt"
"net/http"
"regexp"
"strings"
"sync"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/router"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/metadata"
)
type endpoint struct {
apiep *api.Endpoint
hostregs []*regexp.Regexp
pathregs []runtime.Pattern
}
// router is the default router
type staticRouter struct {
exit chan bool
opts router.Options
sync.RWMutex
eps map[string]*endpoint
}
func (r *staticRouter) isClosed() bool {
select {
case <-r.exit:
return true
default:
return false
}
}
/*
// watch for endpoint changes
func (r *staticRouter) watch() {
var attempts int
for {
if r.isClosed() {
return
}
// watch for changes
w, err := r.opts.Registry.Watch()
if err != nil {
attempts++
log.Println("Error watching endpoints", err)
time.Sleep(time.Duration(attempts) * time.Second)
continue
}
ch := make(chan bool)
go func() {
select {
case <-ch:
w.Stop()
case <-r.exit:
w.Stop()
}
}()
// reset if we get here
attempts = 0
for {
// process next event
res, err := w.Next()
if err != nil {
log.Println("Error getting next endpoint", err)
close(ch)
break
}
r.process(res)
}
}
}
*/
func (r *staticRouter) Register(ep *api.Endpoint) error {
if err := api.Validate(ep); err != nil {
return err
}
var pathregs []runtime.Pattern
var hostregs []*regexp.Regexp
for _, h := range ep.Host {
if h == "" || h == "*" {
continue
}
hostreg, err := regexp.CompilePOSIX(h)
if err != nil {
return err
}
hostregs = append(hostregs, hostreg)
}
for _, p := range ep.Path {
rule, err := httprule.Parse(p)
if err != nil {
return err
}
tpl := rule.Compile()
pathreg, err := runtime.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "")
if err != nil {
return err
}
pathregs = append(pathregs, pathreg)
}
r.Lock()
r.eps[ep.Name] = &endpoint{apiep: ep, pathregs: pathregs, hostregs: hostregs}
r.Unlock()
return nil
}
func (r *staticRouter) Deregister(ep *api.Endpoint) error {
if err := api.Validate(ep); err != nil {
return err
}
r.Lock()
delete(r.eps, ep.Name)
r.Unlock()
return nil
}
func (r *staticRouter) Options() router.Options {
return r.opts
}
func (r *staticRouter) Close() error {
select {
case <-r.exit:
return nil
default:
close(r.exit)
}
return nil
}
func (r *staticRouter) Endpoint(req *http.Request) (*api.Service, error) {
ep, err := r.endpoint(req)
if err != nil {
return nil, err
}
epf := strings.Split(ep.apiep.Name, ".")
services, err := r.opts.Registry.GetService(epf[0])
if err != nil {
return nil, err
}
// hack for stream endpoint
if ep.apiep.Stream {
for _, svc := range services {
for _, e := range svc.Endpoints {
e.Name = strings.Join(epf[1:], ".")
e.Metadata = make(map[string]string)
e.Metadata["stream"] = "true"
}
}
}
svc := &api.Service{
Name: epf[0],
Endpoint: &api.Endpoint{
Name: strings.Join(epf[1:], "."),
Handler: "rpc",
Host: ep.apiep.Host,
Method: ep.apiep.Method,
Path: ep.apiep.Path,
},
Services: services,
}
return svc, nil
}
func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) {
if r.isClosed() {
return nil, errors.New("router closed")
}
r.RLock()
defer r.RUnlock()
var idx int
if len(req.URL.Path) > 0 && req.URL.Path != "/" {
idx = 1
}
path := strings.Split(req.URL.Path[idx:], "/")
// use the first match
// TODO: weighted matching
for _, ep := range r.eps {
var mMatch, hMatch, pMatch bool
// 1. try method
methodLoop:
for _, m := range ep.apiep.Method {
if m == req.Method {
mMatch = true
break methodLoop
}
}
if !mMatch {
continue
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api method match %s", req.Method)
}
// 2. try host
if len(ep.apiep.Host) == 0 {
hMatch = true
} else {
hostLoop:
for idx, h := range ep.apiep.Host {
if h == "" || h == "*" {
hMatch = true
break hostLoop
} else {
if ep.hostregs[idx].MatchString(req.URL.Host) {
hMatch = true
break hostLoop
}
}
}
}
if !hMatch {
continue
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api host match %s", req.URL.Host)
}
// 3. try path
pathLoop:
for _, pathreg := range ep.pathregs {
matches, err := pathreg.Match(path, "")
if err != nil {
// TODO: log error
continue
}
pMatch = true
ctx := req.Context()
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(metadata.Metadata)
}
for k, v := range matches {
md[fmt.Sprintf("x-api-field-%s", k)] = v
}
*req = *req.WithContext(context.WithValue(ctx, metadata.MetadataKey{}, md))
break pathLoop
}
if !pMatch {
continue
}
// TODO: Percentage traffic
// we got here, so its a match
return ep, nil
}
// no match
return nil, fmt.Errorf("endpoint not found for %v", req)
}
func (r *staticRouter) Route(req *http.Request) (*api.Service, error) {
if r.isClosed() {
return nil, errors.New("router closed")
}
// try get an endpoint
ep, err := r.Endpoint(req)
if err != nil {
return nil, err
}
return ep, nil
}
func NewRouter(opts ...router.Option) *staticRouter {
options := router.NewOptions(opts...)
r := &staticRouter{
exit: make(chan bool),
opts: options,
eps: make(map[string]*endpoint),
}
//go r.watch()
//go r.refresh()
return r
}

View File

@@ -2,6 +2,7 @@
package acme
import (
"crypto/tls"
"errors"
"net"
)
@@ -14,7 +15,10 @@ var (
// Provider is a ACME provider interface
type Provider interface {
NewListener(...string) (net.Listener, error)
// Listen returns a new listener
Listen(...string) (net.Listener, error)
// TLSConfig returns a tls config
TLSConfig(...string) (*tls.Config, error)
}
// The Let's Encrypt ACME endpoints

View File

@@ -3,21 +3,44 @@
package autocert
import (
"crypto/tls"
"net"
"os"
"github.com/micro/go-micro/api/server/acme"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/micro/go-micro/v2/logger"
"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
// Listen implements acme.Provider
func (a *autocertProvider) Listen(hosts ...string) (net.Listener, error) {
return autocert.NewListener(hosts...), nil
}
// TLSConfig returns a new tls config
func (a *autocertProvider) TLSConfig(hosts ...string) (*tls.Config, error) {
// create a new manager
m := &autocert.Manager{
Prompt: autocert.AcceptTOS,
}
if len(hosts) > 0 {
m.HostPolicy = autocert.HostWhitelist(hosts...)
}
dir := cacheDir()
if err := os.MkdirAll(dir, 0700); err != nil {
if logger.V(logger.InfoLevel, logger.DefaultLogger) {
logger.Infof("warning: autocert not using a cache: %v", err)
}
} else {
m.Cache = autocert.DirCache(dir)
}
return m.TLSConfig(), nil
}
// New returns an autocert acme.Provider
func New() acme.Provider {
func NewProvider() acme.Provider {
return &autocertProvider{}
}

View File

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

View File

@@ -0,0 +1,37 @@
package autocert
import (
"os"
"path/filepath"
"runtime"
)
func homeDir() string {
if runtime.GOOS == "windows" {
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
}
if h := os.Getenv("HOME"); h != "" {
return h
}
return "/"
}
func cacheDir() string {
const base = "golang-autocert"
switch runtime.GOOS {
case "darwin":
return filepath.Join(homeDir(), "Library", "Caches", base)
case "windows":
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
if v := os.Getenv(ev); v != "" {
return filepath.Join(v, base)
}
}
// Worst case:
return filepath.Join(homeDir(), base)
}
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
return filepath.Join(xdg, base)
}
return filepath.Join(homeDir(), ".cache", base)
}

View File

@@ -2,21 +2,22 @@
package certmagic
import (
"log"
"crypto/tls"
"math/rand"
"net"
"time"
"github.com/mholt/certmagic"
"github.com/micro/go-micro/api/server/acme"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/micro/go-micro/v2/logger"
)
type certmagicProvider struct {
opts acme.Options
}
func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
// TODO: set self-contained options
func (c *certmagicProvider) setup() {
certmagic.Default.CA = c.opts.CA
if c.opts.ChallengeProvider != nil {
// Enabling DNS Challenge disables the other challenges
@@ -34,12 +35,20 @@ func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, erro
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 {
func (c *certmagicProvider) Listen(hosts ...string) (net.Listener, error) {
c.setup()
return certmagic.Listen(hosts)
}
func (c *certmagicProvider) TLSConfig(hosts ...string) (*tls.Config, error) {
c.setup()
return certmagic.TLS(hosts)
}
// NewProvider returns a certmagic provider
func NewProvider(options ...acme.Option) acme.Provider {
opts := acme.DefaultOptions()
for _, o := range options {
@@ -48,7 +57,7 @@ func New(options ...acme.Option) acme.Provider {
if opts.Cache != nil {
if _, ok := opts.Cache.(certmagic.Storage); !ok {
log.Fatal("ACME: cache provided doesn't implement certmagic's Storage interface")
logger.Fatal("ACME: cache provided doesn't implement certmagic's Storage interface")
}
}

View File

@@ -1,6 +1,7 @@
package certmagic
import (
"net"
"net/http"
"os"
"reflect"
@@ -10,17 +11,20 @@ import (
"github.com/go-acme/lego/v3/providers/dns/cloudflare"
"github.com/mholt/certmagic"
"github.com/micro/go-micro/api/server/acme"
cfstore "github.com/micro/go-micro/store/cloudflare"
"github.com/micro/go-micro/sync/lock/memory"
"github.com/micro/go-micro/v2/api/server/acme"
cfstore "github.com/micro/go-micro/v2/store/cloudflare"
"github.com/micro/go-micro/v2/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()
l, err := NewProvider().Listen()
if err != nil {
if _, ok := err.(*net.OpError); ok {
t.Skip("Run under non privileged user")
}
t.Fatal(err.Error())
}
l.Close()
@@ -36,10 +40,10 @@ func TestCertMagic(t *testing.T) {
t.Fatal(err.Error())
}
l, err = New(acme.AcceptToS(true),
l, err = NewProvider(acme.AcceptToS(true),
acme.CA(acme.LetsEncryptStagingCA),
acme.ChallengeProvider(p),
).NewListener()
).Listen()
if err != nil {
t.Fatal(err.Error())
@@ -180,7 +184,7 @@ func TestStorageImplementation(t *testing.T) {
// New interface doesn't return an error, so call it in case any log.Fatal
// happens
New(acme.Cache(s))
NewProvider(acme.Cache(s))
}
// Full test with a real zone, with against LE staging
@@ -207,7 +211,7 @@ func TestE2e(t *testing.T) {
t.Fatal(err.Error())
}
testProvider := New(
testProvider := NewProvider(
acme.AcceptToS(true),
acme.Cache(testStorage),
acme.CA(acme.LetsEncryptStagingCA),
@@ -215,7 +219,7 @@ func TestE2e(t *testing.T) {
acme.OnDemand(false),
)
listener, err := testProvider.NewListener("*.micro.mu", "micro.mu")
listener, err := testProvider.Listen("*.micro.mu", "micro.mu")
if err != nil {
t.Fatal(err.Error())
}

View File

@@ -10,8 +10,8 @@ import (
"time"
"github.com/mholt/certmagic"
"github.com/micro/go-micro/store"
"github.com/micro/go-micro/sync/lock"
"github.com/micro/go-micro/v2/store"
"github.com/micro/go-micro/v2/sync/lock"
)
// File represents a "File" that will be stored in store.Store - the contents and last modified time
@@ -81,22 +81,23 @@ func (s *storage) Delete(key string) error {
}
func (s *storage) Exists(key string) bool {
_, err := s.store.Read(key)
if err != nil {
if _, err := s.store.Read(key); err != nil {
return false
}
return true
}
func (s *storage) List(prefix string, recursive bool) ([]string, error) {
records, err := s.store.List()
keys, err := s.store.List()
if err != nil {
return nil, err
}
//nolint:prealloc
var results []string
for _, r := range records {
if strings.HasPrefix(r.Key, prefix) {
results = append(results, r.Key)
for _, k := range keys {
if strings.HasPrefix(k, prefix) {
results = append(results, k)
}
}
if recursive {

79
api/server/auth/auth.go Normal file
View File

@@ -0,0 +1,79 @@
package auth
import (
"fmt"
"net/http"
"net/url"
"strings"
"github.com/micro/go-micro/v2/auth"
)
// CombinedAuthHandler wraps a server and authenticates requests
func CombinedAuthHandler(h http.Handler) http.Handler {
return authHandler{
handler: h,
auth: auth.DefaultAuth,
}
}
type authHandler struct {
handler http.Handler
auth auth.Auth
}
func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Extract the token from the request
var token string
if header := req.Header.Get("Authorization"); len(header) > 0 {
// Extract the auth token from the request
if strings.HasPrefix(header, auth.BearerScheme) {
token = header[len(auth.BearerScheme):]
}
} else {
// Get the token out the cookies if not provided in headers
if c, err := req.Cookie("micro-token"); err == nil && c != nil {
token = strings.TrimPrefix(c.Value, auth.TokenCookieName+"=")
req.Header.Set("Authorization", auth.BearerScheme+token)
}
}
// Get the account using the token, fallback to a blank account
// since some endpoints can be unauthenticated, so the lack of an
// account doesn't necesserially mean a forbidden request
acc, err := h.auth.Inspect(token)
if err != nil {
acc = &auth.Account{}
}
err = h.auth.Verify(acc, &auth.Resource{
Type: "service",
Name: "go.micro.web",
Endpoint: req.URL.Path,
})
// The account has the necessary permissions to access the
// resource
if err == nil {
h.handler.ServeHTTP(w, req)
return
}
// The account is set, but they don't have enough permissions,
// hence we 403.
if len(acc.ID) > 0 {
w.WriteHeader(http.StatusForbidden)
return
}
// If there is no auth login url set, 401
loginURL := h.auth.Options().LoginURL
if loginURL == "" {
w.WriteHeader(http.StatusUnauthorized)
return
}
// Redirect to the login path
params := url.Values{"redirect_to": {req.URL.Path}}
loginWithRedirect := fmt.Sprintf("%v?%v", loginURL, params.Encode())
http.Redirect(w, req, loginWithRedirect, http.StatusTemporaryRedirect)
}

44
api/server/cors/cors.go Normal file
View File

@@ -0,0 +1,44 @@
package cors
import (
"net/http"
)
// CombinedCORSHandler wraps a server and provides CORS headers
func CombinedCORSHandler(h http.Handler) http.Handler {
return corsHandler{h}
}
type corsHandler struct {
handler http.Handler
}
func (c corsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
SetHeaders(w, r)
if r.Method == "OPTIONS" {
return
}
c.handler.ServeHTTP(w, r)
}
// SetHeaders sets the CORS headers
func SetHeaders(w http.ResponseWriter, r *http.Request) {
set := func(w http.ResponseWriter, k, v string) {
if v := w.Header().Get(k); len(v) > 0 {
return
}
w.Header().Set(k, v)
}
if origin := r.Header.Get("Origin"); len(origin) > 0 {
set(w, "Access-Control-Allow-Origin", origin)
} else {
set(w, "Access-Control-Allow-Origin", "*")
}
set(w, "Access-Control-Allow-Credentials", "true")
set(w, "Access-Control-Allow-Methods", "POST, PATCH, GET, OPTIONS, PUT, DELETE")
set(w, "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}

View File

@@ -8,9 +8,12 @@ import (
"os"
"sync"
"github.com/micro/go-micro/v2/api/server/auth"
"github.com/gorilla/handlers"
"github.com/micro/go-micro/api/server"
"github.com/micro/go-micro/util/log"
"github.com/micro/go-micro/v2/api/server"
"github.com/micro/go-micro/v2/api/server/cors"
"github.com/micro/go-micro/v2/logger"
)
type httpServer struct {
@@ -45,7 +48,14 @@ func (s *httpServer) Init(opts ...server.Option) error {
}
func (s *httpServer) Handle(path string, handler http.Handler) {
s.mux.Handle(path, handlers.CombinedLoggingHandler(os.Stdout, handler))
h := handlers.CombinedLoggingHandler(os.Stdout, handler)
h = auth.CombinedAuthHandler(handler)
if s.opts.EnableCORS {
h = cors.CombinedCORSHandler(h)
}
s.mux.Handle(path, h)
}
func (s *httpServer) Start() error {
@@ -54,7 +64,7 @@ func (s *httpServer) Start() error {
if s.opts.EnableACME && s.opts.ACMEProvider != nil {
// should we check the address to make sure its using :443?
l, err = s.opts.ACMEProvider.NewListener(s.opts.ACMEHosts...)
l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...)
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig)
} else {
@@ -65,7 +75,9 @@ func (s *httpServer) Start() error {
return err
}
log.Logf("HTTP API Listening on %s", l.Addr().String())
if logger.V(logger.InfoLevel, logger.DefaultLogger) {
logger.Infof("HTTP API Listening on %s", l.Addr().String())
}
s.mtx.Lock()
s.address = l.Addr().String()
@@ -74,7 +86,7 @@ func (s *httpServer) Start() error {
go func() {
if err := http.Serve(l, s.mux); err != nil {
// temporary fix
//log.Fatal(err)
//logger.Fatal(err)
}
}()

View File

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

268
api/service/proto/api.pb.go Normal file
View File

@@ -0,0 +1,268 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: api/service/proto/api.proto
package go_micro_api
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
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 Endpoint struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Host []string `protobuf:"bytes,2,rep,name=host,proto3" json:"host,omitempty"`
Path []string `protobuf:"bytes,3,rep,name=path,proto3" json:"path,omitempty"`
Method []string `protobuf:"bytes,4,rep,name=method,proto3" json:"method,omitempty"`
Stream bool `protobuf:"varint,5,opt,name=stream,proto3" json:"stream,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Endpoint) Reset() { *m = Endpoint{} }
func (m *Endpoint) String() string { return proto.CompactTextString(m) }
func (*Endpoint) ProtoMessage() {}
func (*Endpoint) Descriptor() ([]byte, []int) {
return fileDescriptor_c4a48b6b680b5c31, []int{0}
}
func (m *Endpoint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Endpoint.Unmarshal(m, b)
}
func (m *Endpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Endpoint.Marshal(b, m, deterministic)
}
func (m *Endpoint) XXX_Merge(src proto.Message) {
xxx_messageInfo_Endpoint.Merge(m, src)
}
func (m *Endpoint) XXX_Size() int {
return xxx_messageInfo_Endpoint.Size(m)
}
func (m *Endpoint) XXX_DiscardUnknown() {
xxx_messageInfo_Endpoint.DiscardUnknown(m)
}
var xxx_messageInfo_Endpoint proto.InternalMessageInfo
func (m *Endpoint) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Endpoint) GetHost() []string {
if m != nil {
return m.Host
}
return nil
}
func (m *Endpoint) GetPath() []string {
if m != nil {
return m.Path
}
return nil
}
func (m *Endpoint) GetMethod() []string {
if m != nil {
return m.Method
}
return nil
}
func (m *Endpoint) GetStream() bool {
if m != nil {
return m.Stream
}
return false
}
type EmptyResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EmptyResponse) Reset() { *m = EmptyResponse{} }
func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
func (*EmptyResponse) ProtoMessage() {}
func (*EmptyResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_c4a48b6b680b5c31, []int{1}
}
func (m *EmptyResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EmptyResponse.Unmarshal(m, b)
}
func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic)
}
func (m *EmptyResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_EmptyResponse.Merge(m, src)
}
func (m *EmptyResponse) XXX_Size() int {
return xxx_messageInfo_EmptyResponse.Size(m)
}
func (m *EmptyResponse) XXX_DiscardUnknown() {
xxx_messageInfo_EmptyResponse.DiscardUnknown(m)
}
var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*Endpoint)(nil), "go.micro.api.Endpoint")
proto.RegisterType((*EmptyResponse)(nil), "go.micro.api.EmptyResponse")
}
func init() { proto.RegisterFile("api/service/proto/api.proto", fileDescriptor_c4a48b6b680b5c31) }
var fileDescriptor_c4a48b6b680b5c31 = []byte{
// 212 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0xd0, 0xc1, 0x4a, 0x03, 0x31,
0x10, 0x80, 0x61, 0xd7, 0xad, 0x65, 0x1d, 0x14, 0x21, 0x87, 0x12, 0xec, 0x65, 0xd9, 0x53, 0x4f,
0x59, 0xd0, 0x27, 0x28, 0xda, 0x17, 0xd8, 0x37, 0x88, 0xed, 0xd0, 0x9d, 0x43, 0x32, 0x43, 0x32,
0x14, 0x7c, 0x08, 0xdf, 0x59, 0x12, 0x2b, 0x2c, 0x5e, 0xbd, 0xfd, 0xf3, 0x1d, 0x86, 0x61, 0x60,
0xeb, 0x85, 0xc6, 0x8c, 0xe9, 0x42, 0x47, 0x1c, 0x25, 0xb1, 0xf2, 0xe8, 0x85, 0x5c, 0x2d, 0xf3,
0x70, 0x66, 0x17, 0xe8, 0x98, 0xd8, 0x79, 0xa1, 0xe1, 0x02, 0xdd, 0x21, 0x9e, 0x84, 0x29, 0xaa,
0x31, 0xb0, 0x8a, 0x3e, 0xa0, 0x6d, 0xfa, 0x66, 0x77, 0x3f, 0xd5, 0x2e, 0x36, 0x73, 0x56, 0x7b,
0xdb, 0xb7, 0xc5, 0x4a, 0x17, 0x13, 0xaf, 0xb3, 0x6d, 0x7f, 0xac, 0xb4, 0xd9, 0xc0, 0x3a, 0xa0,
0xce, 0x7c, 0xb2, 0xab, 0xaa, 0xd7, 0xa9, 0x78, 0xd6, 0x84, 0x3e, 0xd8, 0xbb, 0xbe, 0xd9, 0x75,
0xd3, 0x75, 0x1a, 0x9e, 0xe0, 0xf1, 0x10, 0x44, 0x3f, 0x27, 0xcc, 0xc2, 0x31, 0xe3, 0xcb, 0x57,
0x03, 0xed, 0x5e, 0xc8, 0xec, 0xa1, 0x9b, 0xf0, 0x4c, 0x59, 0x31, 0x99, 0x8d, 0x5b, 0xde, 0xea,
0x7e, 0x0f, 0x7d, 0xde, 0xfe, 0xf1, 0xe5, 0xa2, 0xe1, 0xc6, 0xbc, 0x01, 0xbc, 0x63, 0xfa, 0xdf,
0x92, 0x8f, 0x75, 0xfd, 0xd6, 0xeb, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x46, 0x62, 0x67, 0x30,
0x4c, 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
// ApiClient is the client API for Api service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type ApiClient interface {
Register(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error)
Deregister(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error)
}
type apiClient struct {
cc *grpc.ClientConn
}
func NewApiClient(cc *grpc.ClientConn) ApiClient {
return &apiClient{cc}
}
func (c *apiClient) Register(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error) {
out := new(EmptyResponse)
err := c.cc.Invoke(ctx, "/go.micro.api.Api/Register", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) Deregister(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error) {
out := new(EmptyResponse)
err := c.cc.Invoke(ctx, "/go.micro.api.Api/Deregister", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ApiServer is the server API for Api service.
type ApiServer interface {
Register(context.Context, *Endpoint) (*EmptyResponse, error)
Deregister(context.Context, *Endpoint) (*EmptyResponse, error)
}
// UnimplementedApiServer can be embedded to have forward compatible implementations.
type UnimplementedApiServer struct {
}
func (*UnimplementedApiServer) Register(ctx context.Context, req *Endpoint) (*EmptyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Register not implemented")
}
func (*UnimplementedApiServer) Deregister(ctx context.Context, req *Endpoint) (*EmptyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Deregister not implemented")
}
func RegisterApiServer(s *grpc.Server, srv ApiServer) {
s.RegisterService(&_Api_serviceDesc, srv)
}
func _Api_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Endpoint)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).Register(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.api.Api/Register",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).Register(ctx, req.(*Endpoint))
}
return interceptor(ctx, in, info, handler)
}
func _Api_Deregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Endpoint)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).Deregister(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.api.Api/Deregister",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).Deregister(ctx, req.(*Endpoint))
}
return interceptor(ctx, in, info, handler)
}
var _Api_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.api.Api",
HandlerType: (*ApiServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Register",
Handler: _Api_Register_Handler,
},
{
MethodName: "Deregister",
Handler: _Api_Deregister_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/service/proto/api.proto",
}

View File

@@ -0,0 +1,102 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: api/service/proto/api.proto
package go_micro_api
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
client "github.com/micro/go-micro/v2/client"
server "github.com/micro/go-micro/v2/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 Api service
type ApiService interface {
Register(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error)
Deregister(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error)
}
type apiService struct {
c client.Client
name string
}
func NewApiService(name string, c client.Client) ApiService {
return &apiService{
c: c,
name: name,
}
}
func (c *apiService) Register(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error) {
req := c.c.NewRequest(c.name, "Api.Register", in)
out := new(EmptyResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiService) Deregister(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error) {
req := c.c.NewRequest(c.name, "Api.Deregister", in)
out := new(EmptyResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Api service
type ApiHandler interface {
Register(context.Context, *Endpoint, *EmptyResponse) error
Deregister(context.Context, *Endpoint, *EmptyResponse) error
}
func RegisterApiHandler(s server.Server, hdlr ApiHandler, opts ...server.HandlerOption) error {
type api interface {
Register(ctx context.Context, in *Endpoint, out *EmptyResponse) error
Deregister(ctx context.Context, in *Endpoint, out *EmptyResponse) error
}
type Api struct {
api
}
h := &apiHandler{hdlr}
return s.Handle(s.NewHandler(&Api{h}, opts...))
}
type apiHandler struct {
ApiHandler
}
func (h *apiHandler) Register(ctx context.Context, in *Endpoint, out *EmptyResponse) error {
return h.ApiHandler.Register(ctx, in, out)
}
func (h *apiHandler) Deregister(ctx context.Context, in *Endpoint, out *EmptyResponse) error {
return h.ApiHandler.Deregister(ctx, in, out)
}

View File

@@ -0,0 +1,18 @@
syntax = "proto3";
package go.micro.api;
service Api {
rpc Register(Endpoint) returns (EmptyResponse) {};
rpc Deregister(Endpoint) returns (EmptyResponse) {};
}
message Endpoint {
string name = 1;
repeated string host = 2;
repeated string path = 3;
repeated string method = 4;
bool stream = 5;
}
message EmptyResponse {}

139
auth/auth.go Normal file
View File

@@ -0,0 +1,139 @@
// Package auth provides authentication and authorization capability
package auth
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/micro/go-micro/v2/metadata"
)
var (
// ErrNotFound is returned when a resouce cannot be found
ErrNotFound = errors.New("not found")
// ErrEncodingToken is returned when the service encounters an error during encoding
ErrEncodingToken = errors.New("error encoding the token")
// ErrInvalidToken is returned when the token provided is not valid
ErrInvalidToken = errors.New("invalid token provided")
// ErrInvalidRole is returned when the role provided was invalid
ErrInvalidRole = errors.New("invalid role")
// ErrForbidden is returned when a user does not have the necessary roles to access a resource
ErrForbidden = errors.New("resource forbidden")
// BearerScheme used for Authorization header
BearerScheme = "Bearer "
)
// Auth providers authentication and authorization
type Auth interface {
// Init the auth
Init(opts ...Option)
// Options set for auth
Options() Options
// Generate a new account
Generate(id string, opts ...GenerateOption) (*Account, error)
// Grant access to a resource
Grant(role string, res *Resource) error
// Revoke access to a resource
Revoke(role string, res *Resource) error
// Verify an account has access to a resource
Verify(acc *Account, res *Resource) error
// Inspect a token
Inspect(token string) (*Account, error)
// Token generated using an account ID and secret
Token(id, secret string, opts ...TokenOption) (*Token, error)
// String returns the name of the implementation
String() string
}
// Resource is an entity such as a user or
type Resource struct {
// Name of the resource
Name string
// Type of resource, e.g.
Type string
// Endpoint resource e.g NotesService.Create
Endpoint string
}
// Account provided by an auth provider
type Account struct {
// ID of the account (UUIDV4, email or username)
ID string `json:"id"`
// Secret used to renew the account
Secret string `json:"secret"`
// Roles associated with the Account
Roles []string `json:"roles"`
// Any other associated metadata
Metadata map[string]string `json:"metadata"`
// Namespace the account belongs to, default blank
Namespace string `json:"namespace"`
}
// Token can be short or long lived
type Token struct {
// The token itself
Token string `json:"token"`
// Type of token, e.g. JWT
Type string `json:"type"`
// Time of token creation
Created time.Time `json:"created"`
// Time of token expiry
Expiry time.Time `json:"expiry"`
// Subject of the token, e.g. the account ID
Subject string `json:"subject"`
// Roles granted to the token
Roles []string `json:"roles"`
// Metadata embedded in the token
Metadata map[string]string `json:"metadata"`
// Namespace the token belongs to
Namespace string `json:"namespace"`
}
const (
// MetadataKey is the key used when storing the account in metadata
MetadataKey = "auth-account"
// TokenCookieName is the name of the cookie which stores the auth token
TokenCookieName = "micro-token"
// SecretCookieName is the name of the cookie which stores the auth secret
SecretCookieName = "micro-secret"
)
// AccountFromContext gets the account from the context, which
// is set by the auth wrapper at the start of a call. If the account
// is not set, a nil account will be returned. The error is only returned
// when there was a problem retrieving an account
func AccountFromContext(ctx context.Context) (*Account, error) {
str, ok := metadata.Get(ctx, MetadataKey)
// there was no account set
if !ok {
return nil, nil
}
var acc *Account
// metadata is stored as a string, so unmarshal to an account
if err := json.Unmarshal([]byte(str), &acc); err != nil {
return nil, err
}
return acc, nil
}
// ContextWithAccount sets the account in the context
func ContextWithAccount(ctx context.Context, account *Account) (context.Context, error) {
// metadata is stored as a string, so marshal to bytes
bytes, err := json.Marshal(account)
if err != nil {
return ctx, err
}
// generate a new context with the MetadataKey set
return metadata.Set(ctx, MetadataKey, string(bytes)), nil
}
// ContextWithToken sets the auth token in the context
func ContextWithToken(ctx context.Context, token string) context.Context {
return metadata.Set(ctx, "Authorization", fmt.Sprintf("%v%v", BearerScheme, token))
}

73
auth/default.go Normal file
View File

@@ -0,0 +1,73 @@
package auth
import (
"github.com/google/uuid"
)
var (
DefaultAuth = NewAuth()
)
func NewAuth(opts ...Option) Auth {
return &noop{}
}
type noop struct {
opts Options
}
// String returns the name of the implementation
func (n *noop) String() string {
return "noop"
}
// Init the auth
func (n *noop) Init(opts ...Option) {
for _, o := range opts {
o(&n.opts)
}
}
// Options set for auth
func (n *noop) Options() Options {
return n.opts
}
// Generate a new account
func (n *noop) Generate(id string, opts ...GenerateOption) (*Account, error) {
options := NewGenerateOptions(opts...)
return &Account{
ID: id,
Roles: options.Roles,
Metadata: options.Metadata,
Secret: uuid.New().String(),
}, nil
}
// Grant access to a resource
func (n *noop) Grant(role string, res *Resource) error {
return nil
}
// Revoke access to a resource
func (n *noop) Revoke(role string, res *Resource) error {
return nil
}
// Verify an account has access to a resource
func (n *noop) Verify(acc *Account, res *Resource) error {
return nil
}
// Inspect a token
func (n *noop) Inspect(token string) (*Account, error) {
return &Account{
ID: uuid.New().String(),
}, nil
}
// Token generation using an account id and secret
func (n *noop) Token(id, secret string, opts ...TokenOption) (*Token, error) {
return &Token{}, nil
}

137
auth/options.go Normal file
View File

@@ -0,0 +1,137 @@
package auth
import (
"time"
"github.com/micro/go-micro/v2/auth/provider"
"github.com/micro/go-micro/v2/store"
)
type Options struct {
// Token is an auth token
Token string
// Public key base64 encoded
PublicKey string
// Private key base64 encoded
PrivateKey string
// Provider is an auth provider
Provider provider.Provider
// LoginURL is the relative url path where a user can login
LoginURL string
// Store to back auth
Store store.Store
}
type Option func(o *Options)
// Store to back auth
func Store(s store.Store) Option {
return func(o *Options) {
o.Store = s
}
}
// PublicKey is the JWT public key
func PublicKey(key string) Option {
return func(o *Options) {
o.PublicKey = key
}
}
// PrivateKey is the JWT private key
func PrivateKey(key string) Option {
return func(o *Options) {
o.PrivateKey = key
}
}
// ServiceToken sets an auth token
func ServiceToken(t string) Option {
return func(o *Options) {
o.Token = t
}
}
// Provider set the auth provider
func Provider(p provider.Provider) Option {
return func(o *Options) {
o.Provider = p
}
}
// LoginURL sets the auth LoginURL
func LoginURL(url string) Option {
return func(o *Options) {
o.LoginURL = url
}
}
type GenerateOptions struct {
// Metadata associated with the account
Metadata map[string]string
// Roles/scopes associated with the account
Roles []string
// Namespace the account belongs too
Namespace string
}
type GenerateOption func(o *GenerateOptions)
// WithMetadata for the generated account
func WithMetadata(md map[string]string) GenerateOption {
return func(o *GenerateOptions) {
o.Metadata = md
}
}
// WithRoles for the generated account
func WithRoles(rs ...string) GenerateOption {
return func(o *GenerateOptions) {
o.Roles = rs
}
}
// WithNamespace for the generated account
func WithNamespace(n string) GenerateOption {
return func(o *GenerateOptions) {
o.Namespace = n
}
}
// NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions
for _, o := range opts {
o(&options)
}
return options
}
type TokenOptions struct {
// TokenExpiry is the time the token should live for
TokenExpiry time.Duration
}
type TokenOption func(o *TokenOptions)
// WithTokenExpiry for the token
func WithTokenExpiry(ex time.Duration) TokenOption {
return func(o *TokenOptions) {
o.TokenExpiry = ex
}
}
// NewTokenOptions from a slice of options
func NewTokenOptions(opts ...TokenOption) TokenOptions {
var options TokenOptions
for _, o := range opts {
o(&options)
}
// set defualt expiry of token
if options.TokenExpiry == 0 {
options.TokenExpiry = time.Minute
}
return options
}

View File

@@ -0,0 +1,34 @@
package basic
import (
"github.com/micro/go-micro/v2/auth/provider"
)
// NewProvider returns an initialised basic provider
func NewProvider(opts ...provider.Option) provider.Provider {
var options provider.Options
for _, o := range opts {
o(&options)
}
return &basic{options}
}
type basic struct {
opts provider.Options
}
func (b *basic) String() string {
return "basic"
}
func (b *basic) Options() provider.Options {
return b.opts
}
func (b *basic) Endpoint() string {
return ""
}
func (b *basic) Redirect() string {
return ""
}

View File

@@ -0,0 +1,54 @@
package oauth
import (
"fmt"
"net/url"
"strings"
"github.com/micro/go-micro/v2/auth/provider"
)
// NewProvider returns an initialised oauth provider
func NewProvider(opts ...provider.Option) provider.Provider {
var options provider.Options
for _, o := range opts {
o(&options)
}
return &oauth{options}
}
type oauth struct {
opts provider.Options
}
func (o *oauth) String() string {
return "oauth"
}
func (o *oauth) Options() provider.Options {
return o.opts
}
func (o *oauth) Endpoint() string {
params := make(url.Values)
params.Add("response_type", "code")
if clientID := o.opts.ClientID; len(clientID) > 0 {
params.Add("client_id", clientID)
}
if scope := o.opts.Scope; len(scope) > 0 {
// spaces are url encoded since this cannot be passed in env vars
params.Add("scope", strings.ReplaceAll(scope, "%20", " "))
}
if redir := o.Redirect(); len(redir) > 0 {
params.Add("redirect_uri", redir)
}
return fmt.Sprintf("%v?%v", o.opts.Endpoint, params.Encode())
}
func (o *oauth) Redirect() string {
return o.opts.Redirect
}

47
auth/provider/options.go Normal file
View File

@@ -0,0 +1,47 @@
package provider
// Option returns a function which sets an option
type Option func(*Options)
// Options a provider can have
type Options struct {
// ClientID is the application's ID.
ClientID string
// ClientSecret is the application's secret.
ClientSecret string
// Endpoint for the provider
Endpoint string
// Redirect url incase of UI
Redirect string
// Scope of the oauth request
Scope string
}
// Credentials is an option which sets the client id and secret
func Credentials(id, secret string) Option {
return func(o *Options) {
o.ClientID = id
o.ClientSecret = secret
}
}
// Endpoint sets the endpoint option
func Endpoint(e string) Option {
return func(o *Options) {
o.Endpoint = e
}
}
// Redirect sets the Redirect option
func Redirect(r string) Option {
return func(o *Options) {
o.Redirect = r
}
}
// Scope sets the oauth scope
func Scope(s string) Option {
return func(o *Options) {
o.Scope = s
}
}

28
auth/provider/provider.go Normal file
View File

@@ -0,0 +1,28 @@
// Package provider is an external auth provider e.g oauth
package provider
import (
"time"
)
// Provider is an auth provider
type Provider interface {
// String returns the name of the provider
String() string
// Options returns the options of a provider
Options() Options
// Endpoint for the provider
Endpoint() string
// Redirect url incase of UI
Redirect() string
}
// Grant is a granted authorisation
type Grant struct {
// token for reuse
Token string
// Expiry of the token
Expiry time.Time
// Scopes associated with grant
Scopes []string
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,259 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/go-micro/auth/service/proto/auth.proto
package go_micro_auth
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
client "github.com/micro/go-micro/v2/client"
server "github.com/micro/go-micro/v2/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 Auth service
type AuthService interface {
Generate(ctx context.Context, in *GenerateRequest, opts ...client.CallOption) (*GenerateResponse, error)
Inspect(ctx context.Context, in *InspectRequest, opts ...client.CallOption) (*InspectResponse, error)
Token(ctx context.Context, in *TokenRequest, opts ...client.CallOption) (*TokenResponse, error)
}
type authService struct {
c client.Client
name string
}
func NewAuthService(name string, c client.Client) AuthService {
return &authService{
c: c,
name: name,
}
}
func (c *authService) Generate(ctx context.Context, in *GenerateRequest, opts ...client.CallOption) (*GenerateResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Generate", in)
out := new(GenerateResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authService) Inspect(ctx context.Context, in *InspectRequest, opts ...client.CallOption) (*InspectResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Inspect", in)
out := new(InspectResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authService) Token(ctx context.Context, in *TokenRequest, opts ...client.CallOption) (*TokenResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Token", in)
out := new(TokenResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Auth service
type AuthHandler interface {
Generate(context.Context, *GenerateRequest, *GenerateResponse) error
Inspect(context.Context, *InspectRequest, *InspectResponse) error
Token(context.Context, *TokenRequest, *TokenResponse) error
}
func RegisterAuthHandler(s server.Server, hdlr AuthHandler, opts ...server.HandlerOption) error {
type auth interface {
Generate(ctx context.Context, in *GenerateRequest, out *GenerateResponse) error
Inspect(ctx context.Context, in *InspectRequest, out *InspectResponse) error
Token(ctx context.Context, in *TokenRequest, out *TokenResponse) error
}
type Auth struct {
auth
}
h := &authHandler{hdlr}
return s.Handle(s.NewHandler(&Auth{h}, opts...))
}
type authHandler struct {
AuthHandler
}
func (h *authHandler) Generate(ctx context.Context, in *GenerateRequest, out *GenerateResponse) error {
return h.AuthHandler.Generate(ctx, in, out)
}
func (h *authHandler) Inspect(ctx context.Context, in *InspectRequest, out *InspectResponse) error {
return h.AuthHandler.Inspect(ctx, in, out)
}
func (h *authHandler) Token(ctx context.Context, in *TokenRequest, out *TokenResponse) error {
return h.AuthHandler.Token(ctx, in, out)
}
// Client API for Accounts service
type AccountsService interface {
List(ctx context.Context, in *ListAccountsRequest, opts ...client.CallOption) (*ListAccountsResponse, error)
}
type accountsService struct {
c client.Client
name string
}
func NewAccountsService(name string, c client.Client) AccountsService {
return &accountsService{
c: c,
name: name,
}
}
func (c *accountsService) List(ctx context.Context, in *ListAccountsRequest, opts ...client.CallOption) (*ListAccountsResponse, error) {
req := c.c.NewRequest(c.name, "Accounts.List", in)
out := new(ListAccountsResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Accounts service
type AccountsHandler interface {
List(context.Context, *ListAccountsRequest, *ListAccountsResponse) error
}
func RegisterAccountsHandler(s server.Server, hdlr AccountsHandler, opts ...server.HandlerOption) error {
type accounts interface {
List(ctx context.Context, in *ListAccountsRequest, out *ListAccountsResponse) error
}
type Accounts struct {
accounts
}
h := &accountsHandler{hdlr}
return s.Handle(s.NewHandler(&Accounts{h}, opts...))
}
type accountsHandler struct {
AccountsHandler
}
func (h *accountsHandler) List(ctx context.Context, in *ListAccountsRequest, out *ListAccountsResponse) error {
return h.AccountsHandler.List(ctx, in, out)
}
// Client API for Rules service
type RulesService interface {
Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error)
Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error)
List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
}
type rulesService struct {
c client.Client
name string
}
func NewRulesService(name string, c client.Client) RulesService {
return &rulesService{
c: c,
name: name,
}
}
func (c *rulesService) Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error) {
req := c.c.NewRequest(c.name, "Rules.Create", in)
out := new(CreateResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *rulesService) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) {
req := c.c.NewRequest(c.name, "Rules.Delete", in)
out := new(DeleteResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *rulesService) List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
req := c.c.NewRequest(c.name, "Rules.List", in)
out := new(ListResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Rules service
type RulesHandler interface {
Create(context.Context, *CreateRequest, *CreateResponse) error
Delete(context.Context, *DeleteRequest, *DeleteResponse) error
List(context.Context, *ListRequest, *ListResponse) error
}
func RegisterRulesHandler(s server.Server, hdlr RulesHandler, opts ...server.HandlerOption) error {
type rules interface {
Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error
Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error
List(ctx context.Context, in *ListRequest, out *ListResponse) error
}
type Rules struct {
rules
}
h := &rulesHandler{hdlr}
return s.Handle(s.NewHandler(&Rules{h}, opts...))
}
type rulesHandler struct {
RulesHandler
}
func (h *rulesHandler) Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error {
return h.RulesHandler.Create(ctx, in, out)
}
func (h *rulesHandler) Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error {
return h.RulesHandler.Delete(ctx, in, out)
}
func (h *rulesHandler) List(ctx context.Context, in *ListRequest, out *ListResponse) error {
return h.RulesHandler.List(ctx, in, out)
}

View File

@@ -0,0 +1,130 @@
syntax = "proto3";
package go.micro.auth;
service Auth {
rpc Generate(GenerateRequest) returns (GenerateResponse) {};
rpc Inspect(InspectRequest) returns (InspectResponse) {};
rpc Token(TokenRequest) returns (TokenResponse) {};
}
service Accounts {
rpc List(ListAccountsRequest) returns (ListAccountsResponse) {};
}
service Rules {
rpc Create(CreateRequest) returns (CreateResponse) {};
rpc Delete(DeleteRequest) returns (DeleteResponse) {};
rpc List(ListRequest) returns (ListResponse) {};
}
message ListAccountsRequest {
}
message ListAccountsResponse {
repeated Account accounts = 1;
}
message Token {
string token = 1;
string type = 2;
int64 created = 3;
int64 expiry = 4;
string subject = 5;
repeated string roles = 6;
map<string, string> metadata = 7;
string namespace = 8;
}
message Account {
string id = 1;
string secret = 2;
repeated string roles = 3;
map<string, string> metadata = 4;
string namespace = 5;
}
message Resource{
string name = 1;
string type = 2;
string endpoint = 3;
}
message GenerateRequest {
string id = 1;
repeated string roles = 2;
map<string, string> metadata = 3;
string namespace = 4;
}
message GenerateResponse {
Account account = 1;
}
message GrantRequest {
string role = 1;
Resource resource = 2;
}
message GrantResponse {}
message RevokeRequest {
string role = 1;
Resource resource = 2;
}
message RevokeResponse {}
message InspectRequest {
string token = 1;
}
message InspectResponse {
Account account = 1;
}
message TokenRequest {
string id = 1;
string secret = 2;
int64 token_expiry = 3;
}
message TokenResponse {
Token token = 1;
}
enum Access {
UNKNOWN = 0;
GRANTED = 1;
DENIED = 2;
}
message Rule {
string id = 1;
string role = 2;
Resource resource = 3;
Access access = 4;
}
message CreateRequest {
string role = 1;
Resource resource = 2;
Access access = 3;
}
message CreateResponse {}
message DeleteRequest {
string role = 1;
Resource resource = 2;
Access access = 3;
}
message DeleteResponse {}
message ListRequest {
}
message ListResponse {
repeated Rule rules = 1;
}

279
auth/service/service.go Normal file
View File

@@ -0,0 +1,279 @@
package service
import (
"context"
"fmt"
"strings"
"sync"
"time"
"github.com/micro/go-micro/v2/auth"
pb "github.com/micro/go-micro/v2/auth/service/proto"
"github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/auth/token/jwt"
"github.com/micro/go-micro/v2/client"
log "github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/util/jitter"
)
// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
svc := new(svc)
svc.Init(opts...)
return svc
}
// svc is the service implementation of the Auth interface
type svc struct {
options auth.Options
auth pb.AuthService
rule pb.RulesService
jwt token.Provider
rules []*pb.Rule
sync.Mutex
}
func (s *svc) String() string {
return "service"
}
func (s *svc) Init(opts ...auth.Option) {
for _, o := range opts {
o(&s.options)
}
dc := client.DefaultClient
s.auth = pb.NewAuthService("go.micro.auth", dc)
s.rule = pb.NewRulesService("go.micro.auth", dc)
// if we have a JWT public key passed as an option,
// we can decode tokens with the type "JWT" locally
// and not have to make an RPC call
if key := s.options.PublicKey; len(key) > 0 {
s.jwt = jwt.NewTokenProvider(token.WithPublicKey(key))
}
// load rules periodically from the auth service
timer := time.NewTicker(time.Second * 30)
go func() {
// load rules immediately on startup
s.loadRules()
for {
<-timer.C
// jitter for up to 5 seconds, this stops
// all the services calling the auth service
// at the exact same time
time.Sleep(jitter.Do(time.Second * 5))
s.loadRules()
}
}()
}
func (s *svc) Options() auth.Options {
return s.options
}
// Generate a new account
func (s *svc) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
options := auth.NewGenerateOptions(opts...)
rsp, err := s.auth.Generate(context.TODO(), &pb.GenerateRequest{
Id: id,
Roles: options.Roles,
Metadata: options.Metadata,
Namespace: options.Namespace,
})
if err != nil {
return nil, err
}
return serializeAccount(rsp.Account), nil
}
// Grant access to a resource
func (s *svc) Grant(role string, res *auth.Resource) error {
_, err := s.rule.Create(context.TODO(), &pb.CreateRequest{
Role: role,
Access: pb.Access_GRANTED,
Resource: &pb.Resource{
Type: res.Type,
Name: res.Name,
Endpoint: res.Endpoint,
},
})
return err
}
// Revoke access to a resource
func (s *svc) Revoke(role string, res *auth.Resource) error {
_, err := s.rule.Delete(context.TODO(), &pb.DeleteRequest{
Role: role,
Access: pb.Access_GRANTED,
Resource: &pb.Resource{
Type: res.Type,
Name: res.Name,
Endpoint: res.Endpoint,
},
})
return err
}
// Verify an account has access to a resource
func (s *svc) Verify(acc *auth.Account, res *auth.Resource) error {
queries := [][]string{
{res.Type, res.Name, res.Endpoint}, // check for specific role, e.g. service.foo.ListFoo:admin (role is checked in accessForRule)
{res.Type, res.Name, "*"}, // check for wildcard endpoint, e.g. service.foo*
{res.Type, "*"}, // check for wildcard name, e.g. service.*
{"*"}, // check for wildcard type, e.g. *
}
// endpoint is a url which can have wildcard excludes, e.g.
// "/foo/*" will allow "/foo/bar"
if comps := strings.Split(res.Endpoint, "/"); len(comps) > 1 {
for i := 1; i < len(comps); i++ {
wildcard := fmt.Sprintf("%v/*", strings.Join(comps[0:i], "/"))
queries = append(queries, []string{res.Type, res.Name, wildcard})
}
}
for _, q := range queries {
for _, rule := range s.listRules(q...) {
switch accessForRule(rule, acc, res) {
case pb.Access_UNKNOWN:
continue // rule did not specify access, check the next rule
case pb.Access_GRANTED:
log.Infof("%v granted access to %v:%v:%v by rule %v", acc.ID, res.Type, res.Name, res.Endpoint, rule.Id)
return nil // rule grants the account access to the resource
case pb.Access_DENIED:
log.Infof("%v denied access to %v:%v:%v by rule %v", acc.ID, res.Type, res.Name, res.Endpoint, rule.Id)
return auth.ErrForbidden // rule denies access to the resource
}
}
}
// no rules were found for the resource, default to denying access
log.Infof("%v denied access to %v:%v:%v by lack of rule (%v rules found)", acc.ID, res.Type, res.Name, res.Endpoint, len(s.rules))
return auth.ErrForbidden
}
// Inspect a token
func (s *svc) Inspect(token string) (*auth.Account, error) {
// try to decode JWT locally and fall back to srv if an error
// occurs, TODO: find a better way of determining if the token
// is a JWT, possibly update the interface to take an auth.Token
// and not just the string
if len(strings.Split(token, ".")) == 3 && s.jwt != nil {
if tok, err := s.jwt.Inspect(token); err == nil {
return &auth.Account{
ID: tok.Subject,
Roles: tok.Roles,
Metadata: tok.Metadata,
}, nil
}
}
rsp, err := s.auth.Inspect(context.TODO(), &pb.InspectRequest{
Token: token,
})
if err != nil {
return nil, err
}
return serializeAccount(rsp.Account), nil
}
// Token generation using an account ID and secret
func (s *svc) Token(id, secret string, opts ...auth.TokenOption) (*auth.Token, error) {
options := auth.NewTokenOptions(opts...)
rsp, err := s.auth.Token(context.Background(), &pb.TokenRequest{
Id: id,
Secret: secret,
TokenExpiry: int64(options.TokenExpiry.Seconds()),
})
if err != nil {
return nil, err
}
return serializeToken(rsp.Token), nil
}
var ruleJoinKey = ":"
// accessForRule returns a rule status, indicating if a rule permits access to a
// resource for a given account
func accessForRule(rule *pb.Rule, acc *auth.Account, res *auth.Resource) pb.Access {
if rule.Role == "*" {
return rule.Access
}
for _, role := range acc.Roles {
if rule.Role == role {
return rule.Access
}
// allow user.anything if role is user.*
if strings.HasSuffix(rule.Role, ".*") && strings.HasPrefix(rule.Role, role+".") {
return rule.Access
}
}
return pb.Access_UNKNOWN
}
// listRules gets all the rules from the store which have an id
// prefix matching the filters
func (s *svc) listRules(filters ...string) []*pb.Rule {
s.Lock()
defer s.Unlock()
prefix := strings.Join(filters, ruleJoinKey)
var rules []*pb.Rule
for _, r := range s.rules {
if strings.HasPrefix(r.Id, prefix) {
rules = append(rules, r)
}
}
return rules
}
// loadRules retrieves the rules from the auth service
func (s *svc) loadRules() {
rsp, err := s.rule.List(context.TODO(), &pb.ListRequest{})
s.Lock()
defer s.Unlock()
if err != nil {
log.Errorf("Error listing rules: %v", err)
return
}
s.rules = rsp.Rules
}
func serializeToken(t *pb.Token) *auth.Token {
return &auth.Token{
Token: t.Token,
Type: t.Type,
Created: time.Unix(t.Created, 0),
Expiry: time.Unix(t.Expiry, 0),
Subject: t.Subject,
Roles: t.Roles,
Metadata: t.Metadata,
}
}
func serializeAccount(a *pb.Account) *auth.Account {
return &auth.Account{
ID: a.Id,
Roles: a.Roles,
Metadata: a.Metadata,
Namespace: a.Namespace,
Secret: a.Secret,
}
}

102
auth/token/basic/basic.go Normal file
View File

@@ -0,0 +1,102 @@
package basic
import (
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/store"
)
// Basic implementation of token provider, backed by the store
type Basic struct {
store store.Store
}
var (
// StorePrefix to isolate tokens
StorePrefix = "tokens/"
)
// NewTokenProvider returns an initialized basic provider
func NewTokenProvider(opts ...token.Option) token.Provider {
options := token.NewOptions(opts...)
if options.Store == nil {
options.Store = store.DefaultStore
}
return &Basic{
store: options.Store,
}
}
// Generate a token for an account
func (b *Basic) Generate(subject string, opts ...token.GenerateOption) (*auth.Token, error) {
options := token.NewGenerateOptions(opts...)
// construct the token
token := auth.Token{
Subject: subject,
Type: b.String(),
Token: uuid.New().String(),
Created: time.Now(),
Expiry: time.Now().Add(options.Expiry),
Metadata: options.Metadata,
Roles: options.Roles,
Namespace: options.Namespace,
}
// marshal the account to bytes
bytes, err := json.Marshal(token)
if err != nil {
return nil, err
}
// write to the store
err = b.store.Write(&store.Record{
Key: fmt.Sprintf("%v%v", StorePrefix, token.Token),
Value: bytes,
Expiry: options.Expiry,
})
if err != nil {
return nil, err
}
// return the token
return &token, nil
}
// Inspect a token
func (b *Basic) Inspect(t string) (*auth.Token, error) {
// lookup the token in the store
recs, err := b.store.Read(StorePrefix + t)
if err == store.ErrNotFound {
return nil, token.ErrInvalidToken
} else if err != nil {
return nil, err
}
bytes := recs[0].Value
// unmarshal the bytes
var tok *auth.Token
if err := json.Unmarshal(bytes, &tok); err != nil {
return nil, err
}
// ensure the token hasn't expired, the store should
// expire the token but we're checking again
if tok.Expiry.Unix() < time.Now().Unix() {
return nil, token.ErrInvalidToken
}
return tok, err
}
// String returns basic
func (b *Basic) String() string {
return "basic"
}

View File

@@ -0,0 +1,80 @@
package basic
import (
"testing"
"time"
"github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/store/memory"
)
func TestGenerate(t *testing.T) {
store := memory.NewStore()
b := NewTokenProvider(token.WithStore(store))
_, err := b.Generate("test")
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
recs, err := store.List()
if err != nil {
t.Fatalf("Unable to read from store: %v", err)
}
if len(recs) != 1 {
t.Errorf("Generate didn't write to the store, expected 1 record, got %v", len(recs))
}
}
func TestInspect(t *testing.T) {
store := memory.NewStore()
b := NewTokenProvider(token.WithStore(store))
t.Run("Valid token", func(t *testing.T) {
md := map[string]string{"foo": "bar"}
roles := []string{"admin"}
subject := "test"
opts := []token.GenerateOption{
token.WithMetadata(md),
token.WithRoles(roles...),
}
tok, err := b.Generate(subject, opts...)
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
tok2, err := b.Inspect(tok.Token)
if err != nil {
t.Fatalf("Inspect returned %v error, expected nil", err)
}
if tok2.Subject != subject {
t.Errorf("Inspect returned %v as the token subject, expected %v", tok2.Subject, subject)
}
if len(tok2.Roles) != len(roles) {
t.Errorf("Inspect returned %v roles, expected %v", len(tok2.Roles), len(roles))
}
if len(tok2.Metadata) != len(md) {
t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md)
}
})
t.Run("Expired token", func(t *testing.T) {
tok, err := b.Generate("foo", token.WithExpiry(-10*time.Second))
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
if _, err = b.Inspect(tok.Token); err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
t.Run("Invalid token", func(t *testing.T) {
_, err := b.Inspect("Invalid token")
if err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
}

114
auth/token/jwt/jwt.go Normal file
View File

@@ -0,0 +1,114 @@
package jwt
import (
"encoding/base64"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/token"
)
// authClaims to be encoded in the JWT
type authClaims struct {
Roles []string `json:"roles"`
Metadata map[string]string `json:"metadata"`
Namespace string `json:"namespace"`
jwt.StandardClaims
}
// JWT implementation of token provider
type JWT struct {
opts token.Options
}
// NewTokenProvider returns an initialized basic provider
func NewTokenProvider(opts ...token.Option) token.Provider {
return &JWT{
opts: token.NewOptions(opts...),
}
}
// Generate a new JWT
func (j *JWT) Generate(subject string, opts ...token.GenerateOption) (*auth.Token, error) {
// decode the private key
priv, err := base64.StdEncoding.DecodeString(j.opts.PrivateKey)
if err != nil {
return nil, err
}
// parse the private key
key, err := jwt.ParseRSAPrivateKeyFromPEM(priv)
if err != nil {
return nil, token.ErrEncodingToken
}
// parse the options
options := token.NewGenerateOptions(opts...)
// generate the JWT
expiry := time.Now().Add(options.Expiry)
t := jwt.NewWithClaims(jwt.SigningMethodRS256, authClaims{
options.Roles, options.Metadata, options.Namespace, jwt.StandardClaims{
Subject: subject,
ExpiresAt: expiry.Unix(),
},
})
tok, err := t.SignedString(key)
if err != nil {
return nil, err
}
// return the token
return &auth.Token{
Subject: subject,
Token: tok,
Type: j.String(),
Created: time.Now(),
Expiry: expiry,
Roles: options.Roles,
Metadata: options.Metadata,
Namespace: options.Namespace,
}, nil
}
// Inspect a JWT
func (j *JWT) Inspect(t string) (*auth.Token, error) {
// decode the public key
pub, err := base64.StdEncoding.DecodeString(j.opts.PublicKey)
if err != nil {
return nil, err
}
// parse the public key
res, err := jwt.ParseWithClaims(t, &authClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwt.ParseRSAPublicKeyFromPEM(pub)
})
if err != nil {
return nil, token.ErrInvalidToken
}
// validate the token
if !res.Valid {
return nil, token.ErrInvalidToken
}
claims, ok := res.Claims.(*authClaims)
if !ok {
return nil, token.ErrInvalidToken
}
// return the token
return &auth.Token{
Token: t,
Subject: claims.Subject,
Metadata: claims.Metadata,
Roles: claims.Roles,
Namespace: claims.Namespace,
}, nil
}
// String returns JWT
func (j *JWT) String() string {
return "jwt"
}

View File

@@ -0,0 +1,90 @@
package jwt
import (
"io/ioutil"
"testing"
"time"
"github.com/micro/go-micro/v2/auth/token"
)
func TestGenerate(t *testing.T) {
privKey, err := ioutil.ReadFile("test/sample_key")
if err != nil {
t.Fatalf("Unable to read private key: %v", err)
}
j := NewTokenProvider(
token.WithPrivateKey(string(privKey)),
)
_, err = j.Generate("test")
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
}
func TestInspect(t *testing.T) {
pubKey, err := ioutil.ReadFile("test/sample_key.pub")
if err != nil {
t.Fatalf("Unable to read public key: %v", err)
}
privKey, err := ioutil.ReadFile("test/sample_key")
if err != nil {
t.Fatalf("Unable to read private key: %v", err)
}
j := NewTokenProvider(
token.WithPublicKey(string(pubKey)),
token.WithPrivateKey(string(privKey)),
)
t.Run("Valid token", func(t *testing.T) {
md := map[string]string{"foo": "bar"}
roles := []string{"admin"}
subject := "test"
opts := []token.GenerateOption{
token.WithMetadata(md),
token.WithRoles(roles...),
}
tok, err := j.Generate(subject, opts...)
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
tok2, err := j.Inspect(tok.Token)
if err != nil {
t.Fatalf("Inspect returned %v error, expected nil", err)
}
if tok2.Subject != subject {
t.Errorf("Inspect returned %v as the token subject, expected %v", tok2.Subject, subject)
}
if len(tok2.Roles) != len(roles) {
t.Errorf("Inspect returned %v roles, expected %v", len(tok2.Roles), len(roles))
}
if len(tok2.Metadata) != len(md) {
t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md)
}
})
t.Run("Expired token", func(t *testing.T) {
tok, err := j.Generate("foo", token.WithExpiry(-10*time.Second))
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
if _, err = j.Inspect(tok.Token); err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
t.Run("Invalid token", func(t *testing.T) {
_, err := j.Inspect("Invalid token")
if err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
}

View File

@@ -0,0 +1 @@
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS3dJQkFBS0NBZ0VBOFNiSlA1WGJFaWRSbTViMnNOcExHbzJlV2ZVNU9KZTBpemdySHdEOEg3RjZQa1BkCi9SbDkvMXBNVjdNaU8zTEh3dGhIQzJCUllxcisxd0Zkb1pDR0JZckxhWHVYRnFLMHZ1WmhQcUUzYXpqdUlIUXUKMEJIL2xYUU1xeUVxRjVNSTJ6ZWpDNHpNenIxNU9OK2dFNEpuaXBqcC9DZGpPUEFEbUpHK0JKOXFlRS9RUGVtLwptVWRJVC9MYUY3a1F4eVlLNVZLbitOZ09Xek1sektBQXBDbjdUVEtCVWU4RlpHNldTWDdMVjBlTEdIc29pYnhsCm85akRqbFk1b0JPY3pmcWVOV0hLNUdYQjdRd3BMTmg5NDZQelpucW9hcFdVZStZL1JPaUhpekpUY3I1Wk1TTDUKd2xFcThoTmhtaG01Tk5lL08rR2dqQkROU2ZVaDA2K3E0bmdtYm1OWDVoODM4QmJqUmN5YzM2ZHd6NkpVK2R1bwpSdFFoZ2lZOTEwcFBmOWJhdVhXcXdVQ1VhNHFzSHpqS1IwTC9OMVhYQXlsQ0RqeWVnWnp6Y093MkNIOFNrZkZVCnJnTHJQYkVCOWVnY0drMzgrYnBLczNaNlJyNSt0bkQxQklQSUZHTGVJMFVPQzAreGlCdjBvenhJRE9GbldhOVUKVEdEeFV4OG9qOFZJZVJuV0RxNk1jMWlKcDhVeWNpQklUUnR3NGRabzcweG1mbmVJV3pyM0tTTmFoU29nSmRSMApsYVF6QXVQM2FpV1hJTXAyc2M4U2MrQmwrTGpYbUJveEJyYUJIaDlLa0pKRWNnQUZ3czJib2pDbEpPWXhvRi9YCmdGS1NzSW5IRHJIVk95V1BCZTNmYWRFYzc3YituYi9leE96cjFFcnhoR2c5akZtcmtPK3M0eEdodjZNQ0F3RUEKQVFLQ0FnRUFqUzc1Q2VvUlRRcUtBNzZaaFNiNGEzNVlKRENtcEpSazFsRTNKYnFzNFYxRnhXaDBjZmJYeG9VMgpSdTRRYjUrZWhsdWJGSFQ2a1BxdG9uRWhRVExjMUNmVE9WbHJOb3hocDVZM2ZyUmlQcnNnNXcwK1R3RUtrcFJUCnltanJQTXdQbGxCM2U0NmVaYmVXWGc3R3FFVmptMGcxVFRRK0tocVM4R0w3VGJlTFhRN1ZTem9ydTNCNVRKMVEKeEN6TVB0dnQ2eDYrU3JrcmhvZG1iT3VNRkpDam1TbWxmck9pZzQ4Zkc3NUpERHRObXpLWHBEUVJpYUNodFJhVQpQRHpmUTlTamhYdFFqdkZvWFFFT3BqdkZVRjR2WldNUWNQNUw1VklDM3JRSWp4MFNzQTN6S0FwakVUbjJHNjN2CktZby8zVWttbzhkUCtGRHA3NCs5a3pLNHFFaFJycEl3bEtiN0VOZWtDUXZqUFl1K3pyKzMyUXdQNTJ2L2FveWQKdjJJaUY3M2laTU1vZDhhYjJuQStyVEI2T0cvOVlSYk5kV21tay9VTi9jUHYrN214TmZ6Y1d1ZU1XcThxMXh4eAptNTNpR0NSQ29PQ1lDQk4zcUFkb1JwYW5xd3lCOUxrLzFCQjBHUld3MjgxK3VhNXNYRnZBVDBKeTVURnduMncvClU1MlJKWFlNOXVhMFBvd214b0RDUWRuNFZYVkdNZGdXaHN4aXhHRlYwOUZObWJJQWJaN0xaWGtkS1gzc1ZVbTcKWU1WYWIzVVo2bEhtdXYzT1NzcHNVUlRqN1hiRzZpaVVlaDU1aW91OENWbnRndWtFcnEzQTQwT05FVzhjNDBzOQphVTBGaSs4eWZpQTViaVZHLzF0bWlucUVERkhuQStnWk1xNEhlSkZxcWZxaEZKa1JwRGtDZ2dFQkFQeGR1NGNKCm5Da1duZDdPWFlHMVM3UDdkVWhRUzgwSDlteW9uZFc5bGFCQm84RWRPeTVTZzNOUmsxQ2pNZFZ1a3FMcjhJSnkKeStLWk15SVpvSlJvbllaMEtIUUVMR3ZLbzFOS2NLQ1FJbnYvWHVCdFJpRzBVb1pQNVkwN0RpRFBRQWpYUjlXUwpBc0EzMmQ1eEtFOC91Y3h0MjVQVzJFakNBUmtVeHQ5d0tKazN3bC9JdXVYRlExTDdDWjJsOVlFUjlHeWxUbzhNCmxXUEY3YndtUFV4UVNKaTNVS0FjTzZweTVUU1lkdWQ2aGpQeXJwSXByNU42VGpmTlRFWkVBeU9LbXVpOHVkUkoKMUg3T3RQVEhGZElKQjNrNEJnRDZtRE1HbjB2SXBLaDhZN3NtRUZBbFkvaXlCZjMvOHk5VHVMb1BycEdqR3RHbgp4Y2RpMHFud2p0SGFNbFVDZ2dFQkFQU2Z0dVFCQ2dTU2JLUSswUEFSR2VVeEQyTmlvZk1teENNTmdHUzJ5Ull3CjRGaGV4ZWkwMVJoaFk1NjE3UjduR1dzb0czd1RQa3dvRTJtbE1aQkoxeWEvUU9RRnQ3WG02OVl0RGh0T2FWbDgKL0o4dlVuSTBtWmxtT2pjTlRoYnVPZDlNSDlRdGxIRUMxMlhYdHJNb3Fsb0U2a05TT0pJalNxYm9wcDRXc1BqcApvZTZ0Nkdyd1RhOHBHeUJWWS90Mi85Ym5ORHVPVlpjODBaODdtY2gzcDNQclBqU3h5di9saGxYMFMwYUdHTkhTCk1XVjdUa25OaGo1TWlIRXFnZ1pZemtBWTkyd1JoVENnU1A2M0VNcitUWXFudXVuMXJHbndPYm95TDR2aFRpV0UKcU42UDNCTFlCZ1FpMllDTDludEJrOEl6RHZyd096dW5GVnhhZ0g5SVVoY0NnZ0VCQUwzQXlLa1BlOENWUmR6cQpzL284VkJDZmFSOFhhUGRnSGxTek1BSXZpNXEwNENqckRyMlV3MHZwTVdnM1hOZ0xUT3g5bFJpd3NrYk9SRmxHCmhhd3hRUWlBdkk0SE9WTlBTU0R1WHVNTG5USTQ0S0RFNlMrY2cxU0VMS2pWbDVqcDNFOEpkL1RJMVpLc0xBQUsKZTNHakM5UC9ZbE8xL21ndW4xNjVkWk01cFAwWHBPb2FaeFV2RHFFTktyekR0V1g0RngyOTZlUzdaSFJodFpCNwovQ2t1VUhlcmxrN2RDNnZzdWhTaTh2eTM3c0tPbmQ0K3c4cVM4czhZYVZxSDl3ZzVScUxxakp0bmJBUnc3alVDCm9KQ053M1hNdnc3clhaYzRTbnhVQUNMRGJNV2lLQy9xL1ZGWW9oTEs2WkpUVkJscWd5cjBSYzBRWmpDMlNJb0kKMjRwRWt3VUNnZ0VCQUpqb0FJVVNsVFY0WlVwaExXN3g4WkxPa01UWjBVdFFyd2NPR0hSYndPUUxGeUNGMVFWNQppejNiR2s4SmZyZHpVdk1sTmREZm9uQXVHTHhQa3VTVEUxWlg4L0xVRkJveXhyV3dvZ0cxaUtwME11QTV6em90CjROai9DbUtCQVkvWnh2anA5M2RFS21aZGxWQkdmeUFMeWpmTW5MWUovZXh5L09YSnhPUktZTUttSHg4M08zRWsKMWhvb0FwbTZabTIzMjRGME1iVU1ham5Idld2ZjhHZGJTNk5zcHd4L0dkbk1tYVMrdUJMVUhVMkNLbmc1bEIwVAp4OWJITmY0dXlPbTR0dXRmNzhCd1R5V3UreEdrVW0zZ2VZMnkvR1hqdDZyY2l1ajFGNzFDenZzcXFmZThTcDdJCnd6SHdxcTNzVHR5S2lCYTZuYUdEYWpNR1pKYSt4MVZJV204Q2dnRUJBT001ajFZR25Ba0pxR0czQWJSVDIvNUMKaVVxN0loYkswOGZsSGs5a2YwUlVjZWc0ZVlKY3dIRXJVaE4rdWQyLzE3MC81dDYra0JUdTVZOUg3bkpLREtESQpoeEg5SStyamNlVkR0RVNTRkluSXdDQ1lrOHhOUzZ0cHZMV1U5b0pibGFKMlZsalV2NGRFWGVQb0hkREh1Zk9ZClVLa0lsV2E3Uit1QzNEOHF5U1JrQnFLa3ZXZ1RxcFNmTVNkc1ZTeFIzU2Q4SVhFSHFjTDNUNEtMWGtYNEdEamYKMmZOSTFpZkx6ekhJMTN3Tk5IUTVRNU9SUC9pell2QzVzZkx4U2ZIUXJiMXJZVkpKWkI5ZjVBUjRmWFpHSVFsbApjMG8xd0JmZFlqMnZxVDlpR09IQnNSSTlSL2M2RzJQcUt3aFRpSzJVR2lmVFNEUVFuUkF6b2tpQVkrbE8vUjQ9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

View File

@@ -0,0 +1 @@
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE4U2JKUDVYYkVpZFJtNWIyc05wTApHbzJlV2ZVNU9KZTBpemdySHdEOEg3RjZQa1BkL1JsOS8xcE1WN01pTzNMSHd0aEhDMkJSWXFyKzF3RmRvWkNHCkJZckxhWHVYRnFLMHZ1WmhQcUUzYXpqdUlIUXUwQkgvbFhRTXF5RXFGNU1JMnplakM0ek16cjE1T04rZ0U0Sm4KaXBqcC9DZGpPUEFEbUpHK0JKOXFlRS9RUGVtL21VZElUL0xhRjdrUXh5WUs1VktuK05nT1d6TWx6S0FBcENuNwpUVEtCVWU4RlpHNldTWDdMVjBlTEdIc29pYnhsbzlqRGpsWTVvQk9jemZxZU5XSEs1R1hCN1F3cExOaDk0NlB6ClpucW9hcFdVZStZL1JPaUhpekpUY3I1Wk1TTDV3bEVxOGhOaG1obTVOTmUvTytHZ2pCRE5TZlVoMDYrcTRuZ20KYm1OWDVoODM4QmJqUmN5YzM2ZHd6NkpVK2R1b1J0UWhnaVk5MTBwUGY5YmF1WFdxd1VDVWE0cXNIempLUjBMLwpOMVhYQXlsQ0RqeWVnWnp6Y093MkNIOFNrZkZVcmdMclBiRUI5ZWdjR2szOCticEtzM1o2UnI1K3RuRDFCSVBJCkZHTGVJMFVPQzAreGlCdjBvenhJRE9GbldhOVVUR0R4VXg4b2o4VkllUm5XRHE2TWMxaUpwOFV5Y2lCSVRSdHcKNGRabzcweG1mbmVJV3pyM0tTTmFoU29nSmRSMGxhUXpBdVAzYWlXWElNcDJzYzhTYytCbCtMalhtQm94QnJhQgpIaDlLa0pKRWNnQUZ3czJib2pDbEpPWXhvRi9YZ0ZLU3NJbkhEckhWT3lXUEJlM2ZhZEVjNzdiK25iL2V4T3pyCjFFcnhoR2c5akZtcmtPK3M0eEdodjZNQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=

105
auth/token/options.go Normal file
View File

@@ -0,0 +1,105 @@
package token
import (
"time"
"github.com/micro/go-micro/v2/store"
)
type Options struct {
// Store to persist the tokens
Store store.Store
// PublicKey base64 encoded, used by JWT
PublicKey string
// PrivateKey base64 encoded, used by JWT
PrivateKey string
}
type Option func(o *Options)
// WithStore sets the token providers store
func WithStore(s store.Store) Option {
return func(o *Options) {
o.Store = s
}
}
// WithPublicKey sets the JWT public key
func WithPublicKey(key string) Option {
return func(o *Options) {
o.PublicKey = key
}
}
// WithPrivateKey sets the JWT private key
func WithPrivateKey(key string) Option {
return func(o *Options) {
o.PrivateKey = key
}
}
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
//set default store
if options.Store == nil {
options.Store = store.DefaultStore
}
return options
}
type GenerateOptions struct {
// Expiry for the token
Expiry time.Duration
// Metadata associated with the account
Metadata map[string]string
// Roles/scopes associated with the account
Roles []string
// Namespace the account belongs too
Namespace string
}
type GenerateOption func(o *GenerateOptions)
// WithExpiry for the generated account's token expires
func WithExpiry(d time.Duration) GenerateOption {
return func(o *GenerateOptions) {
o.Expiry = d
}
}
// WithMetadata for the token
func WithMetadata(md map[string]string) func(o *GenerateOptions) {
return func(o *GenerateOptions) {
o.Metadata = md
}
}
// WithRoles for the token
func WithRoles(rs ...string) func(o *GenerateOptions) {
return func(o *GenerateOptions) {
o.Roles = rs
}
}
// WithNamespace for the token
func WithNamespace(n string) func(o *GenerateOptions) {
return func(o *GenerateOptions) {
o.Namespace = n
}
}
// NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions
for _, o := range opts {
o(&options)
}
//set default Expiry of token
if options.Expiry == 0 {
options.Expiry = time.Minute * 15
}
return options
}

23
auth/token/token.go Normal file
View File

@@ -0,0 +1,23 @@
package token
import (
"errors"
"github.com/micro/go-micro/v2/auth"
)
var (
// ErrNotFound is returned when a token cannot be found
ErrNotFound = errors.New("token not found")
// ErrEncodingToken is returned when the service encounters an error during encoding
ErrEncodingToken = errors.New("error encoding the token")
// ErrInvalidToken is returned when the token provided is not valid
ErrInvalidToken = errors.New("invalid token provided")
)
// Provider generates and inspects tokens
type Provider interface {
Generate(subject string, opts ...GenerateOption) (*auth.Token, error)
Inspect(token string) (*auth.Token, error)
String() string
}

View File

@@ -28,6 +28,7 @@ type Event interface {
Topic() string
Message() *Message
Ack() error
Error() error
}
// Subscriber is a convenience return type for the Subscribe method
@@ -38,13 +39,9 @@ type Subscriber interface {
}
var (
DefaultBroker Broker = newHttpBroker()
DefaultBroker Broker = NewBroker()
)
func NewBroker(opts ...Option) Broker {
return newHttpBroker(opts...)
}
func Init(opts ...Option) error {
return DefaultBroker.Init(opts...)
}

View File

@@ -1,47 +0,0 @@
package broker
import (
"github.com/micro/go-micro/registry"
)
var (
// mock data
testData = map[string][]*registry.Service{
"foo": {
{
Name: "foo",
Version: "1.0.0",
Nodes: []*registry.Node{
{
Id: "foo-1.0.0-123",
Address: "localhost:9999",
},
{
Id: "foo-1.0.0-321",
Address: "localhost:9999",
},
},
},
{
Name: "foo",
Version: "1.0.1",
Nodes: []*registry.Node{
{
Id: "foo-1.0.1-321",
Address: "localhost:6666",
},
},
},
{
Name: "foo",
Version: "1.0.3",
Nodes: []*registry.Node{
{
Id: "foo-1.0.3-345",
Address: "localhost:8888",
},
},
},
},
}
)

504
broker/default.go Normal file
View File

@@ -0,0 +1,504 @@
package broker
import (
"context"
"errors"
"net"
"net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/micro/go-micro/v2/codec/json"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/util/addr"
"github.com/nats-io/nats-server/v2/server"
nats "github.com/nats-io/nats.go"
)
type natsBroker struct {
sync.Once
sync.RWMutex
// indicate if we're connected
connected bool
// address to bind routes to
addrs []string
// servers for the client
servers []string
// client connection and nats opts
conn *nats.Conn
opts Options
nopts nats.Options
// should we drain the connection
drain bool
closeCh chan (error)
// embedded server
server *server.Server
// configure to use local server
local bool
// server exit channel
exit chan bool
}
type subscriber struct {
s *nats.Subscription
opts SubscribeOptions
}
type publication struct {
t string
err error
m *Message
}
func (p *publication) Topic() string {
return p.t
}
func (p *publication) Message() *Message {
return p.m
}
func (p *publication) Ack() error {
// nats does not support acking
return nil
}
func (p *publication) Error() error {
return p.err
}
func (s *subscriber) Options() SubscribeOptions {
return s.opts
}
func (s *subscriber) Topic() string {
return s.s.Subject
}
func (s *subscriber) Unsubscribe() error {
return s.s.Unsubscribe()
}
func (n *natsBroker) Address() string {
n.RLock()
defer n.RUnlock()
if n.server != nil {
return n.server.ClusterAddr().String()
}
if n.conn != nil && n.conn.IsConnected() {
return n.conn.ConnectedUrl()
}
if len(n.addrs) > 0 {
return n.addrs[0]
}
return "127.0.0.1:-1"
}
func (n *natsBroker) setAddrs(addrs []string) []string {
//nolint:prealloc
var cAddrs []string
for _, addr := range addrs {
if len(addr) == 0 {
continue
}
if !strings.HasPrefix(addr, "nats://") {
addr = "nats://" + addr
}
cAddrs = append(cAddrs, addr)
}
// if there's no address and we weren't told to
// embed a local server then use the default url
if len(cAddrs) == 0 && !n.local {
cAddrs = []string{nats.DefaultURL}
}
return cAddrs
}
// serve stats a local nats server if needed
func (n *natsBroker) serve(exit chan bool) error {
// local server address
host := "127.0.0.1"
port := -1
// cluster address
caddr := "0.0.0.0"
cport := -1
// with no address we just default it
// this is a local client address
if len(n.addrs) > 0 {
address := n.addrs[0]
if strings.HasPrefix(address, "nats://") {
address = strings.TrimPrefix(address, "nats://")
}
// parse out the address
h, p, err := net.SplitHostPort(address)
if err == nil {
caddr = h
cport, _ = strconv.Atoi(p)
}
}
// 1. create new server
// 2. register the server
// 3. connect to other servers
// set cluster opts
cOpts := server.ClusterOpts{
Host: caddr,
Port: cport,
}
// get the routes for other nodes
var routes []*url.URL
// get existing nats servers to connect to
services, err := n.opts.Registry.GetService("go.micro.nats.broker")
if err == nil {
for _, service := range services {
for _, node := range service.Nodes {
u, err := url.Parse("nats://" + node.Address)
if err != nil {
if logger.V(logger.InfoLevel, logger.DefaultLogger) {
logger.Info(err)
}
continue
}
// append to the cluster routes
routes = append(routes, u)
}
}
}
// try get existing server
s := n.server
if s != nil {
// stop the existing server
s.Shutdown()
}
s, err = server.NewServer(&server.Options{
// Specify the host
Host: host,
// Use a random port
Port: port,
// Set the cluster ops
Cluster: cOpts,
// Set the routes
Routes: routes,
NoLog: true,
NoSigs: true,
MaxControlLine: 2048,
TLSConfig: n.opts.TLSConfig,
})
if err != nil {
return err
}
// save the server
n.server = s
// start the server
go s.Start()
var ready bool
// wait till its ready for connections
for i := 0; i < 3; i++ {
if s.ReadyForConnections(time.Second) {
ready = true
break
}
}
if !ready {
return errors.New("server not ready")
}
// set the client address
n.servers = []string{s.ClientURL()}
go func() {
var advertise string
// parse out the address
_, port, err := net.SplitHostPort(s.ClusterAddr().String())
if err == nil {
addr, _ := addr.Extract("")
advertise = net.JoinHostPort(addr, port)
} else {
s.ClusterAddr().String()
}
// register the cluster address
for {
select {
case err := <-n.closeCh:
if err != nil {
if logger.V(logger.InfoLevel, logger.DefaultLogger) {
logger.Info(err)
}
}
case <-exit:
// deregister on exit
n.opts.Registry.Deregister(&registry.Service{
Name: "go.micro.nats.broker",
Version: "v2",
Nodes: []*registry.Node{
{Id: s.ID(), Address: advertise},
},
})
s.Shutdown()
return
default:
// register the broker
n.opts.Registry.Register(&registry.Service{
Name: "go.micro.nats.broker",
Version: "v2",
Nodes: []*registry.Node{
{Id: s.ID(), Address: advertise},
},
}, registry.RegisterTTL(time.Minute))
time.Sleep(time.Minute)
}
}
}()
return nil
}
func (n *natsBroker) Connect() error {
n.Lock()
defer n.Unlock()
if !n.connected {
// create exit chan
n.exit = make(chan bool)
// start the local server
if err := n.serve(n.exit); err != nil {
return err
}
// set to connected
}
status := nats.CLOSED
if n.conn != nil {
status = n.conn.Status()
}
switch status {
case nats.CONNECTED, nats.RECONNECTING, nats.CONNECTING:
return nil
default: // DISCONNECTED or CLOSED or DRAINING
opts := n.nopts
opts.DrainTimeout = 1 * time.Second
opts.AsyncErrorCB = n.onAsyncError
opts.DisconnectedErrCB = n.onDisconnectedError
opts.ClosedCB = n.onClose
opts.Servers = n.servers
opts.Secure = n.opts.Secure
opts.TLSConfig = n.opts.TLSConfig
// secure might not be set
if n.opts.TLSConfig != nil {
opts.Secure = true
}
c, err := opts.Connect()
if err != nil {
return err
}
n.conn = c
n.connected = true
return nil
}
}
func (n *natsBroker) Disconnect() error {
n.RLock()
defer n.RUnlock()
if !n.connected {
return nil
}
// drain the connection if specified
if n.drain {
n.conn.Drain()
}
// close the client connection
n.conn.Close()
// shutdown the local server
// and deregister
if n.server != nil {
select {
case <-n.exit:
default:
close(n.exit)
}
}
// set not connected
n.connected = false
return nil
}
func (n *natsBroker) Init(opts ...Option) error {
n.setOption(opts...)
return nil
}
func (n *natsBroker) Options() Options {
return n.opts
}
func (n *natsBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
b, err := n.opts.Codec.Marshal(msg)
if err != nil {
return err
}
n.RLock()
defer n.RUnlock()
return n.conn.Publish(topic, b)
}
func (n *natsBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
if n.conn == nil {
return nil, errors.New("not connected")
}
opt := SubscribeOptions{
AutoAck: true,
Context: context.Background(),
}
for _, o := range opts {
o(&opt)
}
fn := func(msg *nats.Msg) {
var m Message
pub := &publication{t: msg.Subject}
eh := n.opts.ErrorHandler
err := n.opts.Codec.Unmarshal(msg.Data, &m)
pub.err = err
pub.m = &m
if err != nil {
m.Body = msg.Data
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
if eh != nil {
eh(pub)
}
return
}
if err := handler(pub); err != nil {
pub.err = err
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
if eh != nil {
eh(pub)
}
}
}
var sub *nats.Subscription
var err error
n.RLock()
if len(opt.Queue) > 0 {
sub, err = n.conn.QueueSubscribe(topic, opt.Queue, fn)
} else {
sub, err = n.conn.Subscribe(topic, fn)
}
n.RUnlock()
if err != nil {
return nil, err
}
return &subscriber{s: sub, opts: opt}, nil
}
func (n *natsBroker) String() string {
return "eats"
}
func (n *natsBroker) setOption(opts ...Option) {
for _, o := range opts {
o(&n.opts)
}
n.Once.Do(func() {
n.nopts = nats.GetDefaultOptions()
})
// local embedded server
n.local = true
// set to drain
n.drain = true
if !n.opts.Secure {
n.opts.Secure = n.nopts.Secure
}
if n.opts.TLSConfig == nil {
n.opts.TLSConfig = n.nopts.TLSConfig
}
n.addrs = n.setAddrs(n.opts.Addrs)
}
func (n *natsBroker) onClose(conn *nats.Conn) {
n.closeCh <- nil
}
func (n *natsBroker) onDisconnectedError(conn *nats.Conn, err error) {
n.closeCh <- err
}
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
}
}
func NewBroker(opts ...Option) Broker {
options := Options{
// Default codec
Codec: json.Marshaler{},
Context: context.Background(),
Registry: registry.DefaultRegistry,
}
n := &natsBroker{
opts: options,
closeCh: make(chan error),
}
n.setOption(opts...)
return n
}

View File

@@ -1,11 +0,0 @@
// Package http provides a http based message broker
package http
import (
"github.com/micro/go-micro/broker"
)
// NewBroker returns a new http broker
func NewBroker(opts ...broker.Option) broker.Broker {
return broker.NewBroker(opts...)
}

View File

@@ -1,23 +0,0 @@
package http
import (
"context"
"net/http"
"github.com/micro/go-micro/broker"
)
// Handle registers the handler for the given pattern.
func Handle(pattern string, handler http.Handler) broker.Option {
return func(o *broker.Options) {
if o.Context == nil {
o.Context = context.Background()
}
handlers, ok := o.Context.Value("http_handlers").(map[string]http.Handler)
if !ok {
handlers = make(map[string]http.Handler)
}
handlers[pattern] = handler
o.Context = context.WithValue(o.Context, "http_handlers", handlers)
}
}

View File

@@ -1,701 +0,0 @@
package broker
import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net"
"net/http"
"net/url"
"runtime"
"sync"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/codec/json"
merr "github.com/micro/go-micro/errors"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/registry/cache"
maddr "github.com/micro/go-micro/util/addr"
mnet "github.com/micro/go-micro/util/net"
mls "github.com/micro/go-micro/util/tls"
"golang.org/x/net/http2"
)
// HTTP Broker is a point to point async broker
type httpBroker struct {
id string
address string
opts Options
mux *http.ServeMux
c *http.Client
r registry.Registry
sync.RWMutex
subscribers map[string][]*httpSubscriber
running bool
exit chan chan error
// offline message inbox
mtx sync.RWMutex
inbox map[string][][]byte
}
type httpSubscriber struct {
opts SubscribeOptions
id string
topic string
fn Handler
svc *registry.Service
hb *httpBroker
}
type httpEvent struct {
m *Message
t string
}
var (
DefaultSubPath = "/_sub"
broadcastVersion = "ff.http.broadcast"
registerTTL = time.Minute
registerInterval = time.Second * 30
)
func init() {
rand.Seed(time.Now().Unix())
}
func newTransport(config *tls.Config) *http.Transport {
if config == nil {
config = &tls.Config{
InsecureSkipVerify: true,
}
}
dialTLS := func(network string, addr string) (net.Conn, error) {
return tls.Dial(network, addr, config)
}
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
DialTLS: dialTLS,
}
runtime.SetFinalizer(&t, func(tr **http.Transport) {
(*tr).CloseIdleConnections()
})
// setup http2
http2.ConfigureTransport(t)
return t
}
func newHttpBroker(opts ...Option) Broker {
options := Options{
Codec: json.Marshaler{},
Context: context.TODO(),
}
for _, o := range opts {
o(&options)
}
// set address
addr := ":0"
if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
addr = options.Addrs[0]
}
// get registry
reg, ok := options.Context.Value(registryKey).(registry.Registry)
if !ok {
reg = registry.DefaultRegistry
}
h := &httpBroker{
id: "go.micro.http.broker-" + uuid.New().String(),
address: addr,
opts: options,
r: reg,
c: &http.Client{Transport: newTransport(options.TLSConfig)},
subscribers: make(map[string][]*httpSubscriber),
exit: make(chan chan error),
mux: http.NewServeMux(),
inbox: make(map[string][][]byte),
}
// specify the message handler
h.mux.Handle(DefaultSubPath, h)
// get optional handlers
if h.opts.Context != nil {
handlers, ok := h.opts.Context.Value("http_handlers").(map[string]http.Handler)
if ok {
for pattern, handler := range handlers {
h.mux.Handle(pattern, handler)
}
}
}
return h
}
func (h *httpEvent) Ack() error {
return nil
}
func (h *httpEvent) Message() *Message {
return h.m
}
func (h *httpEvent) Topic() string {
return h.t
}
func (h *httpSubscriber) Options() SubscribeOptions {
return h.opts
}
func (h *httpSubscriber) Topic() string {
return h.topic
}
func (h *httpSubscriber) Unsubscribe() error {
return h.hb.unsubscribe(h)
}
func (h *httpBroker) saveMessage(topic string, msg []byte) {
h.mtx.Lock()
defer h.mtx.Unlock()
// get messages
c := h.inbox[topic]
// save message
c = append(c, msg)
// max length 64
if len(c) > 64 {
c = c[:64]
}
// save inbox
h.inbox[topic] = c
}
func (h *httpBroker) getMessage(topic string, num int) [][]byte {
h.mtx.Lock()
defer h.mtx.Unlock()
// get messages
c, ok := h.inbox[topic]
if !ok {
return nil
}
// more message than requests
if len(c) >= num {
msg := c[:num]
h.inbox[topic] = c[num:]
return msg
}
// reset inbox
h.inbox[topic] = nil
// return all messages
return c
}
func (h *httpBroker) subscribe(s *httpSubscriber) error {
h.Lock()
defer h.Unlock()
if err := h.r.Register(s.svc, registry.RegisterTTL(registerTTL)); err != nil {
return err
}
h.subscribers[s.topic] = append(h.subscribers[s.topic], s)
return nil
}
func (h *httpBroker) unsubscribe(s *httpSubscriber) error {
h.Lock()
defer h.Unlock()
var subscribers []*httpSubscriber
// look for subscriber
for _, sub := range h.subscribers[s.topic] {
// deregister and skip forward
if sub.id == s.id {
_ = h.r.Deregister(sub.svc)
continue
}
// keep subscriber
subscribers = append(subscribers, sub)
}
// set subscribers
h.subscribers[s.topic] = subscribers
return nil
}
func (h *httpBroker) run(l net.Listener) {
t := time.NewTicker(registerInterval)
defer t.Stop()
for {
select {
// heartbeat for each subscriber
case <-t.C:
h.RLock()
for _, subs := range h.subscribers {
for _, sub := range subs {
_ = h.r.Register(sub.svc, registry.RegisterTTL(registerTTL))
}
}
h.RUnlock()
// received exit signal
case ch := <-h.exit:
ch <- l.Close()
h.RLock()
for _, subs := range h.subscribers {
for _, sub := range subs {
_ = h.r.Deregister(sub.svc)
}
}
h.RUnlock()
return
}
}
}
func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
err := merr.BadRequest("go.micro.broker", "Method not allowed")
http.Error(w, err.Error(), http.StatusMethodNotAllowed)
return
}
defer req.Body.Close()
req.ParseForm()
b, err := ioutil.ReadAll(req.Body)
if err != nil {
errr := merr.InternalServerError("go.micro.broker", "Error reading request body: %v", err)
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
var m *Message
if err = h.opts.Codec.Unmarshal(b, &m); err != nil {
errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err)
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
topic := m.Header[":topic"]
delete(m.Header, ":topic")
if len(topic) == 0 {
errr := merr.InternalServerError("go.micro.broker", "Topic not found")
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
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 {
continue
}
subs = append(subs, subscriber.fn)
}
h.RUnlock()
// execute the handler
for _, fn := range subs {
fn(p)
}
}
func (h *httpBroker) Address() string {
h.RLock()
defer h.RUnlock()
return h.address
}
func (h *httpBroker) Connect() error {
h.RLock()
if h.running {
h.RUnlock()
return nil
}
h.RUnlock()
h.Lock()
defer h.Unlock()
var l net.Listener
var err error
if h.opts.Secure || h.opts.TLSConfig != nil {
config := h.opts.TLSConfig
fn := func(addr string) (net.Listener, error) {
if config == nil {
hosts := []string{addr}
// check if its a valid host:port
if host, _, err := net.SplitHostPort(addr); err == nil {
if len(host) == 0 {
hosts = maddr.IPs()
} else {
hosts = []string{host}
}
}
// generate a certificate
cert, err := mls.Certificate(hosts...)
if err != nil {
return nil, err
}
config = &tls.Config{Certificates: []tls.Certificate{cert}}
}
return tls.Listen("tcp", addr, config)
}
l, err = mnet.Listen(h.address, fn)
} else {
fn := func(addr string) (net.Listener, error) {
return net.Listen("tcp", addr)
}
l, err = mnet.Listen(h.address, fn)
}
if err != nil {
return err
}
addr := h.address
h.address = l.Addr().String()
go http.Serve(l, h.mux)
go func() {
h.run(l)
h.Lock()
h.opts.Addrs = []string{addr}
h.address = addr
h.Unlock()
}()
// get registry
reg, ok := h.opts.Context.Value(registryKey).(registry.Registry)
if !ok {
reg = registry.DefaultRegistry
}
// set cache
h.r = cache.New(reg)
// set running
h.running = true
return nil
}
func (h *httpBroker) Disconnect() error {
h.RLock()
if !h.running {
h.RUnlock()
return nil
}
h.RUnlock()
h.Lock()
defer h.Unlock()
// stop cache
rc, ok := h.r.(cache.Cache)
if ok {
rc.Stop()
}
// exit and return err
ch := make(chan error)
h.exit <- ch
err := <-ch
// set not running
h.running = false
return err
}
func (h *httpBroker) Init(opts ...Option) error {
h.RLock()
if h.running {
h.RUnlock()
return errors.New("cannot init while connected")
}
h.RUnlock()
h.Lock()
defer h.Unlock()
for _, o := range opts {
o(&h.opts)
}
if len(h.opts.Addrs) > 0 && len(h.opts.Addrs[0]) > 0 {
h.address = h.opts.Addrs[0]
}
if len(h.id) == 0 {
h.id = "go.micro.http.broker-" + uuid.New().String()
}
// get registry
reg, ok := h.opts.Context.Value(registryKey).(registry.Registry)
if !ok {
reg = registry.DefaultRegistry
}
// get cache
if rc, ok := h.r.(cache.Cache); ok {
rc.Stop()
}
// set registry
h.r = cache.New(reg)
// reconfigure tls config
if c := h.opts.TLSConfig; c != nil {
h.c = &http.Client{
Transport: newTransport(c),
}
}
return nil
}
func (h *httpBroker) Options() Options {
return h.opts
}
func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
// create the message first
m := &Message{
Header: make(map[string]string),
Body: msg.Body,
}
for k, v := range msg.Header {
m.Header[k] = v
}
m.Header[":topic"] = topic
// encode the message
b, err := h.opts.Codec.Marshal(m)
if err != nil {
return err
}
// save the message
h.saveMessage(topic, b)
// now attempt to get the service
h.RLock()
s, err := h.r.GetService(topic)
if err != nil {
h.RUnlock()
// ignore error
return nil
}
h.RUnlock()
pub := func(node *registry.Node, t string, b []byte) error {
scheme := "http"
// check if secure is added in metadata
if node.Metadata["secure"] == "true" {
scheme = "https"
}
vals := url.Values{}
vals.Add("id", node.Id)
uri := fmt.Sprintf("%s://%s%s?%s", scheme, node.Address, DefaultSubPath, vals.Encode())
r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
if err != nil {
return err
}
// discard response body
io.Copy(ioutil.Discard, r.Body)
r.Body.Close()
return nil
}
srv := func(s []*registry.Service, b []byte) {
for _, service := range s {
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)
}
// only process if we have nodes
if len(nodes) == 0 {
continue
}
switch service.Version {
// broadcast version means broadcast to all nodes
case broadcastVersion:
var success bool
// publish to all nodes
for _, node := range nodes {
// publish async
if err := pub(node, topic, b); err == nil {
success = true
}
}
// save if it failed to publish at least once
if !success {
h.saveMessage(topic, b)
}
default:
// select node to publish to
node := nodes[rand.Int()%len(nodes)]
// publish async to one node
if err := pub(node, topic, b); err != nil {
// if failed save it
h.saveMessage(topic, b)
}
}
}
}
// do the rest async
go func() {
// get a third of the backlog
messages := h.getMessage(topic, 8)
delay := (len(messages) > 1)
// publish all the messages
for _, msg := range messages {
// serialize here
srv(s, msg)
// sending a backlog of messages
if delay {
time.Sleep(time.Millisecond * 100)
}
}
}()
return nil
}
func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
var err error
var host, port string
options := NewSubscribeOptions(opts...)
// parse address for host, port
host, port, err = net.SplitHostPort(h.Address())
if err != nil {
return nil, err
}
addr, err := maddr.Extract(host)
if err != nil {
return nil, err
}
var secure bool
if h.opts.Secure || h.opts.TLSConfig != nil {
secure = true
}
// register service
node := &registry.Node{
Id: h.id,
Address: mnet.HostPort(addr, port),
Metadata: map[string]string{
"secure": fmt.Sprintf("%t", secure),
"broker": "http",
"topic": topic,
},
}
// check for queue group or broadcast queue
version := options.Queue
if len(version) == 0 {
version = broadcastVersion
}
service := &registry.Service{
Name: topic,
Version: version,
Nodes: []*registry.Node{node},
}
// generate subscriber
subscriber := &httpSubscriber{
opts: options,
hb: h,
id: h.id,
topic: topic,
fn: handler,
svc: service,
}
// subscribe now
if err := h.subscribe(subscriber); err != nil {
return nil, err
}
// return the subscriber
return subscriber, nil
}
func (h *httpBroker) String() string {
return "http"
}

View File

@@ -1,349 +0,0 @@
package broker
import (
"sync"
"testing"
"time"
glog "github.com/go-log/log"
"github.com/google/uuid"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/registry/memory"
"github.com/micro/go-micro/util/log"
)
func newTestRegistry() registry.Registry {
return memory.NewRegistry(memory.Services(testData))
}
func sub(be *testing.B, c int) {
// set no op logger
log.SetLogger(glog.DefaultLogger)
be.StopTimer()
m := newTestRegistry()
b := NewBroker(Registry(m))
topic := uuid.New().String()
if err := b.Init(); err != nil {
be.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
be.Fatalf("Unexpected connect error: %v", err)
}
msg := &Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
var subs []Subscriber
done := make(chan bool, c)
for i := 0; i < c; i++ {
sub, err := b.Subscribe(topic, func(p Event) error {
done <- true
m := p.Message()
if string(m.Body) != string(msg.Body) {
be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
return nil
}, Queue("shared"))
if err != nil {
be.Fatalf("Unexpected subscribe error: %v", err)
}
subs = append(subs, sub)
}
for i := 0; i < be.N; i++ {
be.StartTimer()
if err := b.Publish(topic, msg); err != nil {
be.Fatalf("Unexpected publish error: %v", err)
}
<-done
be.StopTimer()
}
for _, sub := range subs {
sub.Unsubscribe()
}
if err := b.Disconnect(); err != nil {
be.Fatalf("Unexpected disconnect error: %v", err)
}
}
func pub(be *testing.B, c int) {
// set no op logger
log.SetLogger(glog.DefaultLogger)
be.StopTimer()
m := newTestRegistry()
b := NewBroker(Registry(m))
topic := uuid.New().String()
if err := b.Init(); err != nil {
be.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
be.Fatalf("Unexpected connect error: %v", err)
}
msg := &Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
done := make(chan bool, c*4)
sub, err := b.Subscribe(topic, func(p Event) error {
done <- true
m := p.Message()
if string(m.Body) != string(msg.Body) {
be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
return nil
}, Queue("shared"))
if err != nil {
be.Fatalf("Unexpected subscribe error: %v", err)
}
var wg sync.WaitGroup
ch := make(chan int, c*4)
be.StartTimer()
for i := 0; i < c; i++ {
go func() {
for range ch {
if err := b.Publish(topic, msg); err != nil {
be.Fatalf("Unexpected publish error: %v", err)
}
select {
case <-done:
case <-time.After(time.Second):
}
wg.Done()
}
}()
}
for i := 0; i < be.N; i++ {
wg.Add(1)
ch <- i
}
wg.Wait()
be.StopTimer()
sub.Unsubscribe()
close(ch)
close(done)
if err := b.Disconnect(); err != nil {
be.Fatalf("Unexpected disconnect error: %v", err)
}
}
func TestBroker(t *testing.T) {
m := newTestRegistry()
b := NewBroker(Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
t.Fatalf("Unexpected connect error: %v", err)
}
msg := &Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
done := make(chan bool)
sub, err := b.Subscribe("test", func(p Event) error {
m := p.Message()
if string(m.Body) != string(msg.Body) {
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
close(done)
return nil
})
if err != nil {
t.Fatalf("Unexpected subscribe error: %v", err)
}
if err := b.Publish("test", msg); err != nil {
t.Fatalf("Unexpected publish error: %v", err)
}
<-done
sub.Unsubscribe()
if err := b.Disconnect(); err != nil {
t.Fatalf("Unexpected disconnect error: %v", err)
}
}
func TestConcurrentSubBroker(t *testing.T) {
m := newTestRegistry()
b := NewBroker(Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
t.Fatalf("Unexpected connect error: %v", err)
}
msg := &Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
var subs []Subscriber
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
sub, err := b.Subscribe("test", func(p Event) error {
defer wg.Done()
m := p.Message()
if string(m.Body) != string(msg.Body) {
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
return nil
})
if err != nil {
t.Fatalf("Unexpected subscribe error: %v", err)
}
wg.Add(1)
subs = append(subs, sub)
}
if err := b.Publish("test", msg); err != nil {
t.Fatalf("Unexpected publish error: %v", err)
}
wg.Wait()
for _, sub := range subs {
sub.Unsubscribe()
}
if err := b.Disconnect(); err != nil {
t.Fatalf("Unexpected disconnect error: %v", err)
}
}
func TestConcurrentPubBroker(t *testing.T) {
m := newTestRegistry()
b := NewBroker(Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
t.Fatalf("Unexpected connect error: %v", err)
}
msg := &Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
var wg sync.WaitGroup
sub, err := b.Subscribe("test", func(p Event) error {
defer wg.Done()
m := p.Message()
if string(m.Body) != string(msg.Body) {
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
return nil
})
if err != nil {
t.Fatalf("Unexpected subscribe error: %v", err)
}
for i := 0; i < 10; i++ {
wg.Add(1)
if err := b.Publish("test", msg); err != nil {
t.Fatalf("Unexpected publish error: %v", err)
}
}
wg.Wait()
sub.Unsubscribe()
if err := b.Disconnect(); err != nil {
t.Fatalf("Unexpected disconnect error: %v", err)
}
}
func BenchmarkSub1(b *testing.B) {
sub(b, 1)
}
func BenchmarkSub8(b *testing.B) {
sub(b, 8)
}
func BenchmarkSub32(b *testing.B) {
sub(b, 32)
}
func BenchmarkSub64(b *testing.B) {
sub(b, 64)
}
func BenchmarkSub128(b *testing.B) {
sub(b, 128)
}
func BenchmarkPub1(b *testing.B) {
pub(b, 1)
}
func BenchmarkPub8(b *testing.B) {
pub(b, 8)
}
func BenchmarkPub32(b *testing.B) {
pub(b, 32)
}
func BenchmarkPub64(b *testing.B) {
pub(b, 64)
}
func BenchmarkPub128(b *testing.B) {
pub(b, 128)
}

View File

@@ -2,15 +2,17 @@
package memory
import (
"context"
"errors"
"math/rand"
"sync"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/broker"
maddr "github.com/micro/go-micro/util/addr"
mnet "github.com/micro/go-micro/util/net"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/logger"
maddr "github.com/micro/go-micro/v2/util/addr"
mnet "github.com/micro/go-micro/v2/util/net"
)
type memoryBroker struct {
@@ -23,8 +25,10 @@ type memoryBroker struct {
}
type memoryEvent struct {
opts broker.Options
topic string
message *broker.Message
err error
message interface{}
}
type memorySubscriber struct {
@@ -85,7 +89,7 @@ func (m *memoryBroker) Init(opts ...broker.Option) error {
return nil
}
func (m *memoryBroker) Publish(topic string, message *broker.Message, opts ...broker.PublishOption) error {
func (m *memoryBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
m.RLock()
if !m.connected {
m.RUnlock()
@@ -98,13 +102,30 @@ func (m *memoryBroker) Publish(topic string, message *broker.Message, opts ...br
return nil
}
var v interface{}
if m.opts.Codec != nil {
buf, err := m.opts.Codec.Marshal(msg)
if err != nil {
return err
}
v = buf
} else {
v = msg
}
p := &memoryEvent{
topic: topic,
message: message,
message: v,
opts: m.opts,
}
for _, sub := range subs {
if err := sub.handler(p); err != nil {
p.err = err
if eh := m.opts.ErrorHandler; eh != nil {
eh(p)
continue
}
return err
}
}
@@ -163,13 +184,31 @@ func (m *memoryEvent) Topic() string {
}
func (m *memoryEvent) Message() *broker.Message {
return m.message
switch v := m.message.(type) {
case *broker.Message:
return v
case []byte:
msg := &broker.Message{}
if err := m.opts.Codec.Unmarshal(v, msg); err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("[memory]: failed to unmarshal: %v\n", err)
}
return nil
}
return msg
}
return nil
}
func (m *memoryEvent) Ack() error {
return nil
}
func (m *memoryEvent) Error() error {
return m.err
}
func (m *memorySubscriber) Options() broker.SubscribeOptions {
return m.opts
}
@@ -184,7 +223,10 @@ func (m *memorySubscriber) Unsubscribe() error {
}
func NewBroker(opts ...broker.Option) broker.Broker {
var options broker.Options
options := broker.Options{
Context: context.Background(),
}
rand.Seed(time.Now().UnixNano())
for _, o := range opts {
o(&options)

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/v2/broker"
)
func TestMemoryBroker(t *testing.T) {

View File

@@ -3,19 +3,9 @@ package nats
import (
"context"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/v2/broker"
)
// setSubscribeOption returns a function to setup a context with given value
func setSubscribeOption(k, v interface{}) broker.SubscribeOption {
return func(o *broker.SubscribeOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// setBrokerOption returns a function to setup a context with given value
func setBrokerOption(k, v interface{}) broker.Option {
return func(o *broker.Options) {
@@ -25,13 +15,3 @@ func setBrokerOption(k, v interface{}) broker.Option {
o.Context = context.WithValue(o.Context, k, v)
}
}
// setPublishOption returns a function to setup a context with given value
func setPublishOption(k, v interface{}) broker.PublishOption {
return func(o *broker.PublishOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}

View File

@@ -4,23 +4,44 @@ package nats
import (
"context"
"errors"
"net"
"net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/codec/json"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/codec/json"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/util/addr"
"github.com/nats-io/nats-server/v2/server"
nats "github.com/nats-io/nats.go"
)
type natsBroker struct {
sync.Once
sync.RWMutex
addrs []string
conn *nats.Conn
opts broker.Options
nopts nats.Options
// indicate if we're connected
connected bool
addrs []string
conn *nats.Conn
opts broker.Options
nopts nats.Options
// should we drain the connection
drain bool
closeCh chan (error)
// embedded server
server *server.Server
// configure to use local server
local bool
// server exit channel
exit chan bool
}
type subscriber struct {
@@ -29,8 +50,9 @@ type subscriber struct {
}
type publication struct {
t string
m *broker.Message
t string
err error
m *broker.Message
}
func (p *publication) Topic() string {
@@ -46,6 +68,10 @@ func (p *publication) Ack() error {
return nil
}
func (p *publication) Error() error {
return p.err
}
func (s *subscriber) Options() broker.SubscribeOptions {
return s.opts
}
@@ -62,6 +88,7 @@ func (n *natsBroker) Address() string {
if n.conn != nil && n.conn.IsConnected() {
return n.conn.ConnectedUrl()
}
if len(n.addrs) > 0 {
return n.addrs[0]
}
@@ -69,7 +96,8 @@ func (n *natsBroker) Address() string {
return ""
}
func setAddrs(addrs []string) []string {
func (n *natsBroker) setAddrs(addrs []string) []string {
//nolint:prealloc
var cAddrs []string
for _, addr := range addrs {
if len(addr) == 0 {
@@ -80,16 +108,188 @@ func setAddrs(addrs []string) []string {
}
cAddrs = append(cAddrs, addr)
}
if len(cAddrs) == 0 {
// if there's no address and we weren't told to
// embed a local server then use the default url
if len(cAddrs) == 0 && !n.local {
cAddrs = []string{nats.DefaultURL}
}
return cAddrs
}
// serve stats a local nats server if needed
func (n *natsBroker) serve(exit chan bool) error {
var host string
var port int
var local bool
// with no address we just default it
// this is a local client address
if len(n.addrs) == 0 {
// find an advertiseable ip
if h, err := addr.Extract(""); err != nil {
host = "127.0.0.1"
} else {
host = h
}
port = -1
local = true
} else {
address := n.addrs[0]
if strings.HasPrefix(address, "nats://") {
address = strings.TrimPrefix(address, "nats://")
}
// check if its a local address and only then embed
if addr.IsLocal(address) {
h, p, err := net.SplitHostPort(address)
if err == nil {
host = h
port, _ = strconv.Atoi(p)
local = true
}
}
}
// we only setup a server for local things
if !local {
return nil
}
// 1. create new server
// 2. register the server
// 3. connect to other servers
var cOpts server.ClusterOpts
var routes []*url.URL
// get existing nats servers to connect to
services, err := n.opts.Registry.GetService("go.micro.nats.broker")
if err == nil {
for _, service := range services {
for _, node := range service.Nodes {
u, err := url.Parse("nats://" + node.Address)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
continue
}
// append to the cluster routes
routes = append(routes, u)
}
}
}
// try get existing server
s := n.server
// get a host address
caddr, err := addr.Extract("")
if err != nil {
caddr = "0.0.0.0"
}
// set cluster opts
cOpts = server.ClusterOpts{
Host: caddr,
Port: -1,
}
if s == nil {
var err error
s, err = server.NewServer(&server.Options{
// Specify the host
Host: host,
// Use a random port
Port: port,
// Set the cluster ops
Cluster: cOpts,
// Set the routes
Routes: routes,
NoLog: true,
NoSigs: true,
MaxControlLine: 2048,
TLSConfig: n.opts.TLSConfig,
})
if err != nil {
return err
}
// save the server
n.server = s
}
// start the server
go s.Start()
var ready bool
// wait till its ready for connections
for i := 0; i < 3; i++ {
if s.ReadyForConnections(time.Second) {
ready = true
break
}
}
if !ready {
return errors.New("server not ready")
}
// set the client address
n.addrs = []string{s.ClientURL()}
go func() {
// register the cluster address
for {
select {
case <-exit:
// deregister on exit
n.opts.Registry.Deregister(&registry.Service{
Name: "go.micro.nats.broker",
Version: "v2",
Nodes: []*registry.Node{
{Id: s.ID(), Address: s.ClusterAddr().String()},
},
})
s.Shutdown()
return
default:
// register the broker
n.opts.Registry.Register(&registry.Service{
Name: "go.micro.nats.broker",
Version: "v2",
Nodes: []*registry.Node{
{Id: s.ID(), Address: s.ClusterAddr().String()},
},
}, registry.RegisterTTL(time.Minute))
time.Sleep(time.Minute)
}
}
}()
return nil
}
func (n *natsBroker) Connect() error {
n.Lock()
defer n.Unlock()
if !n.connected {
// create exit chan
n.exit = make(chan bool)
// start embedded server if asked to
if n.local {
if err := n.serve(n.exit); err != nil {
return err
}
}
// set to connected
n.connected = true
}
status := nats.CLOSED
if n.conn != nil {
status = n.conn.Status()
@@ -121,11 +321,29 @@ func (n *natsBroker) Connect() error {
func (n *natsBroker) Disconnect() error {
n.RLock()
defer n.RUnlock()
// drain the connection if specified
if n.drain {
n.conn.Drain()
return <-n.closeCh
n.closeCh <- nil
}
// close the client connection
n.conn.Close()
// shutdown the local server
// and deregister
if n.server != nil {
select {
case <-n.exit:
default:
close(n.exit)
}
}
// set not connected
n.connected = false
return nil
}
@@ -164,10 +382,30 @@ func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...bro
fn := func(msg *nats.Msg) {
var m broker.Message
if err := n.opts.Codec.Unmarshal(msg.Data, &m); err != nil {
pub := &publication{t: msg.Subject}
eh := n.opts.ErrorHandler
err := n.opts.Codec.Unmarshal(msg.Data, &m)
pub.err = err
pub.m = &m
if err != nil {
m.Body = msg.Data
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
if eh != nil {
eh(pub)
}
return
}
handler(&publication{m: &m, t: msg.Subject})
if err := handler(pub); err != nil {
pub.err = err
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
if eh != nil {
eh(pub)
}
}
}
var sub *nats.Subscription
@@ -190,21 +428,6 @@ func (n *natsBroker) String() string {
return "nats"
}
func NewBroker(opts ...broker.Option) broker.Broker {
options := broker.Options{
// Default codec
Codec: json.Marshaler{},
Context: context.Background(),
}
n := &natsBroker{
opts: options,
}
n.setOption(opts...)
return n
}
func (n *natsBroker) setOption(opts ...broker.Option) {
for _, o := range opts {
o(&n.opts)
@@ -218,10 +441,15 @@ func (n *natsBroker) setOption(opts ...broker.Option) {
n.nopts = nopts
}
local, ok := n.opts.Context.Value(localServerKey{}).(bool)
if ok {
n.local = local
}
// 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(n.opts.Addrs) == 0 {
if len(n.opts.Addrs) == 0 && !n.local {
n.opts.Addrs = n.nopts.Servers
}
@@ -232,13 +460,14 @@ func (n *natsBroker) setOption(opts ...broker.Option) {
if n.opts.TLSConfig == nil {
n.opts.TLSConfig = n.nopts.TLSConfig
}
n.addrs = setAddrs(n.opts.Addrs)
n.addrs = n.setAddrs(n.opts.Addrs)
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
n.nopts.DisconnectedErrCB = n.onDisconnectedError
}
}
@@ -253,3 +482,23 @@ func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err e
n.closeCh <- err
}
}
func (n *natsBroker) onDisconnectedError(conn *nats.Conn, err error) {
n.closeCh <- err
}
func NewBroker(opts ...broker.Option) broker.Broker {
options := broker.Options{
// Default codec
Codec: json.Marshaler{},
Context: context.Background(),
Registry: registry.DefaultRegistry,
}
n := &natsBroker{
opts: options,
}
n.setOption(opts...)
return n
}

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/v2/broker"
nats "github.com/nats-io/nats.go"
)
@@ -94,6 +94,5 @@ func TestInitAddrs(t *testing.T) {
}
}
})
}
}

View File

@@ -1,18 +1,24 @@
package nats
import (
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/v2/broker"
nats "github.com/nats-io/nats.go"
)
type optionsKey struct{}
type drainConnectionKey struct{}
type localServerKey struct{}
// Options accepts nats.Options
func Options(opts nats.Options) broker.Option {
return setBrokerOption(optionsKey{}, opts)
}
// LocalServer embeds a local server rather than connecting to one
func LocalServer() broker.Option {
return setBrokerOption(localServerKey{}, true)
}
// DrainConnection will drain subscription on close
func DrainConnection() broker.Option {
return setBrokerOption(drainConnectionKey{}, struct{}{})

View File

@@ -4,15 +4,22 @@ import (
"context"
"crypto/tls"
"github.com/micro/go-micro/codec"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/registry"
)
type Options struct {
Addrs []string
Secure bool
Codec codec.Marshaler
Addrs []string
Secure bool
Codec codec.Marshaler
// Handler executed when error happens in broker mesage
// processing
ErrorHandler Handler
TLSConfig *tls.Config
// Registry used for clustering
Registry registry.Registry
// Other options for implementations of the interface
// can be stored in a context
Context context.Context
@@ -44,10 +51,6 @@ type PublishOption func(*PublishOptions)
type SubscribeOption func(*SubscribeOptions)
var (
registryKey = "github.com/micro/go-micro/registry"
)
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
opt := SubscribeOptions{
AutoAck: true,
@@ -83,6 +86,14 @@ func DisableAutoAck() SubscribeOption {
}
}
// ErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors
func ErrorHandler(h Handler) Option {
return func(o *Options) {
o.ErrorHandler = h
}
}
// Queue sets the name of the queue to share messages on
func Queue(name string) SubscribeOption {
return func(o *SubscribeOptions) {
@@ -92,7 +103,7 @@ func Queue(name string) SubscribeOption {
func Registry(r registry.Registry) Option {
return func(o *Options) {
o.Context = context.WithValue(o.Context, registryKey, r)
o.Registry = r
}
}

View File

@@ -1,66 +0,0 @@
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

@@ -1,11 +1,15 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/broker/service/proto/broker.proto
// source: broker/service/proto/broker.proto
package go_micro_broker
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
@@ -30,7 +34,7 @@ func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) {
return fileDescriptor_178fdc60944ff5e5, []int{0}
return fileDescriptor_df4d8f04292cf3fe, []int{0}
}
func (m *Empty) XXX_Unmarshal(b []byte) error {
@@ -63,7 +67,7 @@ func (m *PublishRequest) Reset() { *m = PublishRequest{} }
func (m *PublishRequest) String() string { return proto.CompactTextString(m) }
func (*PublishRequest) ProtoMessage() {}
func (*PublishRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_178fdc60944ff5e5, []int{1}
return fileDescriptor_df4d8f04292cf3fe, []int{1}
}
func (m *PublishRequest) XXX_Unmarshal(b []byte) error {
@@ -110,7 +114,7 @@ func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} }
func (m *SubscribeRequest) String() string { return proto.CompactTextString(m) }
func (*SubscribeRequest) ProtoMessage() {}
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_178fdc60944ff5e5, []int{2}
return fileDescriptor_df4d8f04292cf3fe, []int{2}
}
func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error {
@@ -157,7 +161,7 @@ func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) {
return fileDescriptor_178fdc60944ff5e5, []int{3}
return fileDescriptor_df4d8f04292cf3fe, []int{3}
}
func (m *Message) XXX_Unmarshal(b []byte) error {
@@ -200,30 +204,171 @@ func init() {
proto.RegisterMapType((map[string]string)(nil), "go.micro.broker.Message.HeaderEntry")
}
func init() {
proto.RegisterFile("micro/go-micro/broker/service/proto/broker.proto", fileDescriptor_178fdc60944ff5e5)
func init() { proto.RegisterFile("broker/service/proto/broker.proto", fileDescriptor_df4d8f04292cf3fe) }
var fileDescriptor_df4d8f04292cf3fe = []byte{
// 299 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0x4d, 0x4b, 0xc3, 0x40,
0x14, 0xec, 0xb6, 0xb6, 0xa1, 0xaf, 0xa2, 0x65, 0x29, 0x12, 0x7a, 0x31, 0x0d, 0x1e, 0x72, 0xda,
0x48, 0xbc, 0xa8, 0x88, 0x07, 0xb1, 0xe0, 0x41, 0x41, 0xd6, 0x9b, 0xb7, 0x6c, 0xfa, 0x68, 0x43,
0x1b, 0x37, 0xdd, 0x4d, 0x0a, 0xf9, 0x23, 0x9e, 0xfc, 0xb1, 0xd2, 0xdd, 0xf8, 0xd5, 0x50, 0x6f,
0x6f, 0xde, 0xce, 0xce, 0x1b, 0x66, 0x60, 0x22, 0x94, 0x5c, 0xa2, 0x0a, 0x35, 0xaa, 0x4d, 0x9a,
0x60, 0x98, 0x2b, 0x59, 0xc8, 0xd0, 0x2e, 0x99, 0x01, 0xf4, 0x78, 0x2e, 0x59, 0x96, 0x26, 0x4a,
0x32, 0xbb, 0xf6, 0x1d, 0xe8, 0x4e, 0xb3, 0xbc, 0xa8, 0xfc, 0x57, 0x38, 0x7a, 0x2e, 0xc5, 0x2a,
0xd5, 0x0b, 0x8e, 0xeb, 0x12, 0x75, 0x41, 0x47, 0xd0, 0x2d, 0x64, 0x9e, 0x26, 0x2e, 0xf1, 0x48,
0xd0, 0xe7, 0x16, 0xd0, 0x08, 0x9c, 0x0c, 0xb5, 0x8e, 0xe7, 0xe8, 0xb6, 0x3d, 0x12, 0x0c, 0x22,
0x97, 0xed, 0x68, 0xb2, 0x27, 0xfb, 0xce, 0xbf, 0x88, 0xfe, 0x2d, 0x0c, 0x5f, 0x4a, 0xa1, 0x13,
0x95, 0x0a, 0xfc, 0x5f, 0x7d, 0x04, 0xdd, 0x75, 0x89, 0xa5, 0xd5, 0xee, 0x73, 0x0b, 0xfc, 0x77,
0x02, 0x4e, 0x2d, 0x4a, 0x6f, 0xa0, 0xb7, 0xc0, 0x78, 0x86, 0xca, 0x25, 0x5e, 0x27, 0x18, 0x44,
0x67, 0xfb, 0xce, 0xb3, 0x07, 0x43, 0x9b, 0xbe, 0x15, 0xaa, 0xe2, 0xf5, 0x1f, 0x4a, 0xe1, 0x40,
0xc8, 0x59, 0x65, 0xe4, 0x0f, 0xb9, 0x99, 0xc7, 0x57, 0x30, 0xf8, 0x45, 0xa5, 0x43, 0xe8, 0x2c,
0xb1, 0xaa, 0x6d, 0x6d, 0xc7, 0xad, 0xa9, 0x4d, 0xbc, 0xfa, 0x31, 0x65, 0xc0, 0x75, 0xfb, 0x92,
0x44, 0x1f, 0x04, 0x7a, 0x77, 0xe6, 0x2a, 0xbd, 0x07, 0xa7, 0xce, 0x8f, 0x9e, 0x36, 0x2c, 0xfd,
0x4d, 0x76, 0x7c, 0xd2, 0x20, 0xd8, 0x0e, 0x5a, 0xf4, 0x11, 0xfa, 0xdf, 0x49, 0xd1, 0x49, 0x83,
0xb6, 0x9b, 0xe2, 0x78, 0x6f, 0xf8, 0x7e, 0xeb, 0x9c, 0x88, 0x9e, 0x29, 0xfd, 0xe2, 0x33, 0x00,
0x00, 0xff, 0xff, 0x19, 0x9f, 0x10, 0x75, 0x19, 0x02, 0x00, 0x00,
}
var fileDescriptor_178fdc60944ff5e5 = []byte{
// 305 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0x4d, 0x4f, 0xc2, 0x40,
0x14, 0x64, 0x41, 0x68, 0x78, 0x18, 0x25, 0x1b, 0x62, 0x1a, 0x2e, 0x62, 0xe3, 0x81, 0x8b, 0x5b,
0x52, 0x2f, 0x6a, 0x8c, 0x07, 0x23, 0x89, 0x07, 0x4d, 0xcc, 0x7a, 0xf3, 0xd6, 0x2d, 0x2f, 0xa5,
0x81, 0xba, 0x65, 0xb7, 0x25, 0xe9, 0x1f, 0xf1, 0xe4, 0x8f, 0x35, 0xec, 0x16, 0x3f, 0x68, 0xf0,
0x36, 0xf3, 0x76, 0x76, 0xde, 0x64, 0x1e, 0x4c, 0xd2, 0x24, 0x52, 0xd2, 0x8f, 0xe5, 0x85, 0x05,
0x42, 0xc9, 0x05, 0x2a, 0x5f, 0xa3, 0x5a, 0x27, 0x11, 0xfa, 0x99, 0x92, 0xf9, 0x76, 0xc8, 0x0c,
0xa1, 0xc7, 0xb1, 0x64, 0x46, 0xcb, 0xec, 0xd8, 0x73, 0xa0, 0x3d, 0x4d, 0xb3, 0xbc, 0xf4, 0xde,
0xe0, 0xe8, 0xa5, 0x10, 0xcb, 0x44, 0xcf, 0x39, 0xae, 0x0a, 0xd4, 0x39, 0x1d, 0x40, 0x3b, 0x97,
0x59, 0x12, 0xb9, 0x64, 0x44, 0xc6, 0x5d, 0x6e, 0x09, 0x0d, 0xc0, 0x49, 0x51, 0xeb, 0x30, 0x46,
0xb7, 0x39, 0x22, 0xe3, 0x5e, 0xe0, 0xb2, 0x1d, 0x4f, 0xf6, 0x6c, 0xdf, 0xf9, 0x56, 0xe8, 0xdd,
0x41, 0xff, 0xb5, 0x10, 0x3a, 0x52, 0x89, 0xc0, 0xff, 0xdd, 0x07, 0xd0, 0x5e, 0x15, 0x58, 0x58,
0xef, 0x2e, 0xb7, 0xc4, 0xfb, 0x20, 0xe0, 0x54, 0xa6, 0xf4, 0x16, 0x3a, 0x73, 0x0c, 0x67, 0xa8,
0x5c, 0x32, 0x6a, 0x8d, 0x7b, 0xc1, 0xf9, 0xbe, 0xf5, 0xec, 0xd1, 0xc8, 0xa6, 0xef, 0xb9, 0x2a,
0x79, 0xf5, 0x87, 0x52, 0x38, 0x10, 0x72, 0x56, 0x1a, 0xfb, 0x43, 0x6e, 0xf0, 0xf0, 0x1a, 0x7a,
0xbf, 0xa4, 0xb4, 0x0f, 0xad, 0x05, 0x96, 0x55, 0xac, 0x0d, 0xdc, 0x84, 0x5a, 0x87, 0xcb, 0x9f,
0x50, 0x86, 0xdc, 0x34, 0xaf, 0x48, 0xf0, 0x49, 0xa0, 0x73, 0x6f, 0xb6, 0xd2, 0x07, 0x70, 0xaa,
0xfe, 0xe8, 0x69, 0x2d, 0xd2, 0xdf, 0x66, 0x87, 0x27, 0x35, 0x81, 0xbd, 0x41, 0x83, 0x3e, 0x41,
0xf7, 0xbb, 0x29, 0x7a, 0x56, 0x93, 0xed, 0xb6, 0x38, 0xdc, 0x5b, 0xbe, 0xd7, 0x98, 0x10, 0xd1,
0x31, 0x47, 0xbf, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x60, 0x8c, 0x40, 0xd5, 0x28, 0x02, 0x00,
0x00,
// 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
}
// UnimplementedBrokerServer can be embedded to have forward compatible implementations.
type UnimplementedBrokerServer struct {
}
func (*UnimplementedBrokerServer) Publish(ctx context.Context, req *PublishRequest) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented")
}
func (*UnimplementedBrokerServer) Subscribe(req *SubscribeRequest, srv Broker_SubscribeServer) error {
return status.Errorf(codes.Unimplemented, "method Subscribe not implemented")
}
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: "broker/service/proto/broker.proto",
}

View File

@@ -1,5 +1,5 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/broker/service/proto/broker.proto
// source: broker/service/proto/broker.proto
package go_micro_broker
@@ -11,8 +11,8 @@ import (
import (
context "context"
client "github.com/micro/go-micro/client"
server "github.com/micro/go-micro/server"
client "github.com/micro/go-micro/v2/client"
server "github.com/micro/go-micro/v2/server"
)
// Reference imports to suppress errors if they are not otherwise used.
@@ -44,12 +44,6 @@ type brokerService struct {
}
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,
@@ -79,6 +73,7 @@ func (c *brokerService) Subscribe(ctx context.Context, in *SubscribeRequest, opt
}
type Broker_SubscribeService interface {
Context() context.Context
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
@@ -93,6 +88,10 @@ func (x *brokerServiceSubscribe) Close() error {
return x.stream.Close()
}
func (x *brokerServiceSubscribe) Context() context.Context {
return x.stream.Context()
}
func (x *brokerServiceSubscribe) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
@@ -146,6 +145,7 @@ func (h *brokerHandler) Subscribe(ctx context.Context, stream server.Stream) err
}
type Broker_SubscribeStream interface {
Context() context.Context
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
@@ -160,6 +160,10 @@ func (x *brokerSubscribeStream) Close() error {
return x.stream.Close()
}
func (x *brokerSubscribeStream) Context() context.Context {
return x.stream.Context()
}
func (x *brokerSubscribeStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}

View File

@@ -5,10 +5,10 @@ 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"
"github.com/micro/go-micro/v2/broker"
pb "github.com/micro/go-micro/v2/broker/service/proto"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/logger"
)
type serviceBroker struct {
@@ -45,7 +45,9 @@ func (b *serviceBroker) Options() broker.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)
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Publishing to topic %s broker %v", topic, b.Addrs)
}
_, err := b.Client.Publish(context.TODO(), &pb.PublishRequest{
Topic: topic,
Message: &pb.Message{
@@ -61,7 +63,9 @@ func (b *serviceBroker) Subscribe(topic string, handler broker.Handler, opts ...
for _, o := range opts {
o(&options)
}
log.Debugf("Subscribing to topic %s queue %s broker %v", topic, options.Queue, b.Addrs)
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.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,
@@ -83,19 +87,27 @@ func (b *serviceBroker) Subscribe(topic string, handler broker.Handler, opts ...
for {
select {
case <-sub.closed:
log.Debugf("Unsubscribed from topic %s", topic)
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.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 logger.V(logger.DebugLevel, logger.DefaultLogger) {
// run the subscriber
logger.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)
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.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)
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Failed to resubscribe to topic %s: %v", topic, err)
}
time.Sleep(time.Second)
continue
}
@@ -124,9 +136,11 @@ func NewBroker(opts ...broker.Option) broker.Broker {
addrs = []string{"127.0.0.1:8001"}
}
cli := client.DefaultClient
return &serviceBroker{
Addrs: addrs,
Client: pb.NewBrokerService(DefaultName, client.DefaultClient),
Client: pb.NewBrokerService(DefaultName, cli),
options: options,
}
}

View File

@@ -1,9 +1,9 @@
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"
"github.com/micro/go-micro/v2/broker"
pb "github.com/micro/go-micro/v2/broker/service/proto"
"github.com/micro/go-micro/v2/logger"
)
type serviceSub struct {
@@ -17,6 +17,7 @@ type serviceSub struct {
type serviceEvent struct {
topic string
err error
message *broker.Message
}
@@ -32,6 +33,10 @@ func (s *serviceEvent) Ack() error {
return nil
}
func (s *serviceEvent) Error() error {
return s.err
}
func (s *serviceSub) isClosed() bool {
select {
case <-s.closed:
@@ -57,7 +62,9 @@ func (s *serviceSub) run() error {
// 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)
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Streaming error for subcription to topic %s: %v", s.Topic(), err)
}
// close the exit channel
close(exit)
@@ -71,14 +78,14 @@ func (s *serviceSub) run() error {
return err
}
// TODO: handle error
s.handler(&serviceEvent{
p := &serviceEvent{
topic: s.topic,
message: &broker.Message{
Header: msg.Header,
Body: msg.Body,
},
})
}
p.err = s.handler(p)
}
}

View File

@@ -2,16 +2,13 @@ package client
import (
"context"
"math"
"time"
"github.com/micro/go-micro/v2/util/backoff"
)
type BackoffFunc func(ctx context.Context, req Request, attempts int) (time.Duration, error)
// exponential backoff
func exponentialBackoff(ctx context.Context, req Request, attempts int) (time.Duration, error) {
if attempts == 0 {
return time.Duration(0), nil
}
return time.Duration(math.Pow(10, float64(attempts))) * time.Millisecond, nil
return backoff.Do(attempts), nil
}

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