Compare commits

..

358 Commits

Author SHA1 Message Date
Janos Dobronszki
87e898f4fc Secret implementation of config. Supporting config merge (#2027)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-09-29 15:30:51 +02:00
Asim Aslam
d246ccbeef Update tests.yml 2020-09-28 14:40:53 +01:00
Asim Aslam
017e156440 remove transport options 2020-09-26 13:29:09 +01:00
Asim Aslam
d8f17ac827 readd service package (#2026) 2020-09-26 13:15:05 +01:00
Asim Aslam
6e2c9e7cd4 env config implementation (#2024) 2020-09-25 10:24:02 +01:00
ben-toogood
4028e0156b runtime: remove builder package (moved to micro) (#2023) 2020-09-25 10:02:49 +01:00
Dominic Wong
76275e857c Fix branch names support for k8s runtime (#2020)
* fix branch names support for k8s

* remove logs

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-09-25 08:41:28 +01:00
wangzq
a18806c9ef fix config bug (#2021) 2020-09-25 08:14:21 +01:00
ben-toogood
255fecb4f4 runtime: minor fixes for local runtime (#2019) 2020-09-24 16:46:23 +01:00
Asim Aslam
af8d55eb7f remove memcache and update gomod 2020-09-23 21:16:16 +01:00
Janos Dobronszki
354a169050 Add errors to config methods (#2015) 2020-09-22 16:08:01 +02:00
ben-toogood
6e083b9aca store/file: fix segmentation violation bug (#2013) 2020-09-22 10:00:14 +01:00
Janos Dobronszki
9dbd75f2cc Config interface change (#2010) 2020-09-21 17:45:45 +02:00
ben-toogood
8975184b88 proxy/grpc: fix client streaming bug (EOF not sent to the server) (#2011) 2020-09-18 14:20:42 +01:00
ben-toogood
19a54f2970 client/grpc: fix stream closed bug (#2009)
* client/grpc: fix stream closed bug

* client/grpc: don't use dial context for the stream
2020-09-17 14:08:21 +01:00
ben-toogood
3c7f663e8b store/file: don't keep connection to boltdb open (#2006) 2020-09-16 13:09:04 +01:00
ben-toogood
8fdc8f05ce runtime/builder with golang implementation (#2003) 2020-09-15 17:26:27 +01:00
ben-toogood
35349bd313 store: implement s3 blob store (#2005) 2020-09-15 17:09:40 +01:00
ben-toogood
d5bfa1e795 store: add blob interface with file implementation (#2004) 2020-09-15 14:05:10 +01:00
ben-toogood
3bb76868d1 auth: remove micro specific code (#1999) 2020-09-11 13:41:13 +01:00
Janos Dobronszki
275e92be32 Fix running subfolders (#1998) 2020-09-11 12:57:23 +02:00
ben-toogood
d2728b498c api: fix request body re-sequencing bug (#1996)
* api: don't mutate request body on POST requests

* fix test suite

* Improve solution

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-09-10 16:07:37 +01:00
Dominic Wong
601b223cfb add Name to auth.Account as a user friendly alias (#1992)
* add Name to auth.Account as a user friendly alias
2020-09-10 14:49:51 +01:00
Janos Dobronszki
04d2aa4696 Fixing top level run outside repo (#1993) 2020-09-10 14:36:36 +02:00
ben-toogood
b8f79a3fc6 runtime: normalised service statuses (#1994) 2020-09-10 12:17:11 +01:00
Janos Dobronszki
acd3bea0c6 Add 'Namespace' header to allowed CORS headers (#1990) 2020-09-09 15:40:02 +02:00
Asim Aslam
a02a25d955 Remove all the external plugins except grpc (#1988)
* Remove all the external plugins except grpc

* strip cockroach

* strip nats test

* fix build
2020-09-08 13:14:45 +01:00
ben-toogood
d5e345d41d util/kubernetes: fix TCPSocketAction bug (#1987) 2020-09-08 11:43:47 +01:00
Prawn
71f8cbd5e2 Fixing the metric tagging issue here (#1986)
Co-authored-by: chris <chris@Profanity.local>
2020-09-07 09:10:04 +01:00
Asim Aslam
f12473f4b1 Update runtime.go 2020-09-04 22:43:32 +01:00
Dominic Wong
724e2b5830 Memory events stream not pushing publications correctly (#1984)
* subs not filtered correctly on publish. simplify retry logic

* check fo invalid ackwait
2020-09-04 08:31:49 +01:00
Dominic Wong
6bdf33c4ee Event stream updates (#1981)
- auto and manual acking
- retry limits
2020-09-02 13:28:54 +01:00
dy1006
84f52fd7ac Update log.go (#1976)
change nlog.DefaultLogger.Log to nlog.DefaultLogger.Logf in the Logf function
2020-08-31 07:18:55 +01:00
Dominic Wong
6e30b53280 fix cockroach init to create table in correct database (#1977) 2020-08-28 10:35:47 +01:00
dy1006
a60426c884 Update rpc.go (#1975)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-08-27 10:24:19 +01:00
Prawn
2998735bf3 Tidying up the new Metrics implementations (#1974)
* Unit tests to check tagging and aggregation of Prometheus metrics

* Removing the logger output routing (because it doesn't actually work in the logger implementation)

* Emitting values with the logging reporter

Co-authored-by: chris <chris@Profanity.local>
2020-08-27 09:08:51 +01:00
wangxu
3a96135df8 add log grpc handler err (#1973)
Co-authored-by: wangxu <wangxu@oneniceapp.com>
2020-08-25 10:11:02 +01:00
zuoan
bf8b3aeac7 remove redunant code and cleanup (#1970)
* remove redundant code

* check invalid ip address first

* remove redundant code

* cleanup

Co-authored-by: 刘海洋 <haiyang@snqu.com>
2020-08-25 09:10:46 +01:00
Dominic Wong
5a52b5929c add create and delete namespace to runtime (#1965)
* add create and delete namespace to runtime

* dial down aggressive expiry

* add logging

* fix deletenamespace

* add start of k8s unit tests

* fix workflow

* turn on k8s tests

* ease tight tests

* mkdir in workflow

* dammit -p

* setup folder
2020-08-24 16:54:39 +01:00
ben-toogood
0adb469a85 runtime/local: fix unknown dir path (#1964)
Co-authored-by: Asim Aslam <asim@aslam.me>
Co-authored-by: Janos Dobronszki <dobronszki@gmail.com>
2020-08-24 15:23:44 +02:00
Asim Aslam
21004341bf Rename reporter.go to metrics.go 2020-08-23 22:18:28 +01:00
Asim Aslam
cc26f2b8b1 delete api service proto 2020-08-23 21:58:16 +01:00
Asim Aslam
1a6652fe6b sql model template 2020-08-23 21:30:46 +01:00
Asim Aslam
d28f0670d6 Move git into local/source 2020-08-23 21:23:07 +01:00
Asim Aslam
7bdd619e1b move plugin to util 2020-08-23 21:18:59 +01:00
Asim Aslam
c62d1d5eb8 move transport (#1967) 2020-08-23 18:37:22 +01:00
Asim Aslam
d60d85de5c move tunnel/resolver into network 2020-08-23 15:00:27 +01:00
Asim Aslam
44f281f8d9 remove test request file 2020-08-23 14:13:45 +01:00
Asim Aslam
f698feac9c remove context from client/server 2020-08-23 14:11:57 +01:00
Asim Aslam
f55701b374 Strip cache from the client. Its only used externally in a wrapper 2020-08-23 13:59:19 +01:00
Asim Aslam
82e8298b73 Router table.Read replaces List/Query (#1966)
* Table.REad insted of list and query

* fmt
2020-08-23 13:10:48 +01:00
Asim Aslam
fc54503232 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-08-22 20:55:52 +01:00
Asim Aslam
6f0594eebe Update static router 2020-08-22 20:55:43 +01:00
Asim Aslam
6b52f859cf Update mucp.go 2020-08-22 16:36:03 +01:00
Asim Aslam
a3d4b8f79b Update mucp.go 2020-08-22 16:35:12 +01:00
Asim Aslam
7c7df6b35d Remove quic transport, move route into router 2020-08-22 16:15:44 +01:00
Asim Aslam
e80eab397a Update events.go 2020-08-22 09:20:53 +01:00
ben-toogood
6cda6ef92e runtime/local: add support for idiomatic folder structures (#1963)
* runtime/local: add support for idiomatic folder structures

* runtime/local: add test coverage

* runtime/local: increase test coverage

* runtime/local: add test for empty local source

* runtime/local: make entrypoint public
2020-08-21 11:17:42 +01:00
Prawn
f9f61d29de Observability/metrics update (#1962)
* Removing logging from the NOOP implementatino

* Simplifying the percentiles option

* Simple logging implementation

Co-authored-by: chris <chris@Profanity.local>
2020-08-21 20:57:10 +12:00
ben-toogood
1ae825032c store: remove write TTL & expiry options (#1960) 2020-08-21 09:35:53 +01:00
Asim Aslam
f146b52418 Registry router fixes (#1961)
* only cache routes if told to do so

* Use roundrobin selector and retry in proxy

* Update lookup to require service

* Fix compile

* Fix compile

* Update

* Update

* rename query to lookup

* Update router.go

* Update
2020-08-21 09:23:01 +01:00
Dominic Wong
78a79ca9e1 Memory and file store list fixes (#1959)
* Refactor file and memory stores
2020-08-20 15:08:35 +01:00
ben-toogood
329bc2f265 events: add store implementation (#1957) 2020-08-20 11:28:04 +01:00
Asim Aslam
8738ed7757 Update debug.go 2020-08-20 10:06:02 +01:00
ben-toogood
29e8cdbfe9 events: update interface (#1954) 2020-08-20 09:29:29 +01:00
Dominic Wong
47f356fc5f Unify the store tests (#1952)
Add more tests for store
2020-08-19 23:41:03 +01:00
Janos Dobronszki
21ffc73c4f Generic git checkout (#1951) 2020-08-19 17:24:42 +02:00
ben-toogood
81a9342b83 util/file: allow context to be passed (#1950) 2020-08-19 16:03:19 +01:00
ben-toogood
66df1bb361 events/nats: add support for TLS config (#1946)
* events/nats: add support for TLS config

* events/nats: improve error logging
2020-08-19 10:55:54 +01:00
Asim Aslam
7eaec450a1 support error handler in memory broker (#1947) 2020-08-19 10:20:43 +01:00
Asim Aslam
5d6b7b3d7d Move the network resolver out (#1944) 2020-08-18 21:38:29 +01:00
Dominic Wong
2eac8ed64f Fix cockroach store not respecting WriteTTL option (#1943)
* cockroach fixes for expiry

* cockroach should run in the background
2020-08-18 18:30:05 +01:00
Janos Dobronszki
2b2dc2f811 Support private repos in env 'local' (#1938) 2020-08-18 18:26:14 +02:00
ben-toogood
21cca297c0 events: implement package with memory & nats streams (#1942) 2020-08-18 16:19:53 +01:00
Asim Aslam
19ef225b2f Revert "grpc: avoid allocations for each message (#1939)" (#1941)
This reverts commit 2a23224d91.
2020-08-18 14:44:29 +01:00
2a23224d91 grpc: avoid allocations for each message (#1939)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-08-18 14:24:11 +01:00
Asim Aslam
dd2dc7a2b9 Update broker.go 2020-08-18 14:05:25 +01:00
Asim Aslam
4413372a3f Decruft the broker by removing Event interface (#1940) 2020-08-18 14:00:51 +01:00
Janos Dobronszki
a2a808f2d6 Baseurl, gitlab support, single word service names (#1933) 2020-08-18 11:31:49 +02:00
Asim Aslam
7a6669d199 Update reporter.go 2020-08-18 08:30:29 +01:00
Asim Aslam
09fdd3c121 Update reporter.go 2020-08-18 08:29:26 +01:00
Prawn
da4159513e Metrics interface and Prometheus implementation (#1929)
* Metrics interface

* Prometheus implementation

* NoOp implementation

Co-authored-by: chris <chris@Profanity.local>
2020-08-18 08:27:50 +01:00
Asim Aslam
e1248f90f4 update options 2020-08-17 23:09:41 +01:00
Asim Aslam
3011bad518 sort routes by metric 2020-08-17 23:09:24 +01:00
Asim Aslam
bb7fe21c46 unused variables 2020-08-17 23:00:27 +01:00
Asim Aslam
4fd4a116f2 allow setting registry router in client 2020-08-17 22:53:20 +01:00
Asim Aslam
50ec6c748f cleanup client/selector/lookup (#1937)
* cleanup client/selector/lookup

* add mdns router, remove registry from client

* fix roundtripper

* remove comment

* fix compile issue

* fix mucp test

* fix api router
2020-08-17 22:44:45 +01:00
Asim Aslam
7135787b78 Update README.md 2020-08-17 19:43:28 +01:00
Asim Aslam
870a1ebc07 dead code 2020-08-17 10:04:52 +01:00
zuoan
61899398b3 simplifies code (#1934)
Co-authored-by: 刘海洋 <haiyang@snqu.com>
2020-08-17 11:10:42 +03:00
Asim Aslam
55d62fc1a5 Strip Advertise/Process from router 2020-08-14 23:51:52 +01:00
Asim Aslam
5238a8a85f don't delete own routes 2020-08-14 23:04:55 +01:00
Asim Aslam
9fffd0419e Merge branch 'master' of ssh://github.com/micro/go-micro 2020-08-14 21:52:18 +01:00
Asim Aslam
58794df27c default to noop registry in network 2020-08-14 21:52:05 +01:00
ben-toogood
5a88ea7247 runtime: resource limits (kubernetes implementation) (#1931)
* runtime: add resource limit CreateOptions

* util/kubernetes/client: implement support for resource limits

* runtime/kubernetes: set resource limits for k8s deployments

* util/kubernetes: remove template check for ints

* util/kubernetes: fix incorrect yaml syntax

* runtime/kubernetes: fix incorrect units

* runtime: update create options to use Resources struct
2020-08-14 11:47:28 +01:00
Asim Aslam
374aae1490 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-08-13 14:10:49 +01:00
Asim Aslam
ccf2f4efd6 fix windows 2020-08-13 14:10:41 +01:00
ben-toogood
9380b365de runtime/local: fix injection of secrets as env vars (#1930) 2020-08-13 09:22:25 +01:00
Asim Aslam
f0142febcf executable is now os 2020-08-13 07:57:57 +01:00
Asim Aslam
1fa3ac5599 write nil when expiry is zero 2020-08-12 12:52:14 +01:00
Asim Aslam
375b67ee16 simplify runtime logs 2020-08-11 22:57:30 +01:00
Dominic Wong
69a53e8070 expiry can be taken from options or record (#1928) 2020-08-11 18:11:18 +01:00
Asim Aslam
b6e1c7ac99 make source dir a variable 2020-08-11 17:25:43 +01:00
Asim Aslam
e83a808b05 make log dir a variable 2020-08-11 17:23:00 +01:00
ben-toogood
012ec6a998 router/registry: fix expiring routes bug (#1927) 2020-08-11 16:57:04 +01:00
Asim Aslam
fae4151027 Add a build package (#1926)
* Add a build package

* fix go mod

* package tar
2020-08-11 16:51:58 +01:00
ben-toogood
e162e6d505 router/registry: fix bug which impacts service registered in multiple domains (#1925)
* router/registry: fix bug which impacts service registered in multiple domains

* router/registry: bugfix
2020-08-11 12:42:22 +01:00
Asim Aslam
c51ef6fc29 move wrapper to client 2020-08-11 11:25:49 +01:00
Asim Aslam
28d6340f04 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-08-11 10:32:25 +01:00
Asim Aslam
4fc193f95d Delete the cmd package 2020-08-11 10:24:55 +01:00
Asim Aslam
d072eb6ff2 Deprecate service (#1924) 2020-08-11 10:03:47 +01:00
ben-toogood
1263806a39 util/kubernetes: add readiness check to deployments (#1923) 2020-08-11 08:38:30 +01:00
Asim Aslam
959407bad9 support wrapper slice in api server options 2020-08-10 22:38:54 +01:00
Asim Aslam
61d12d3a39 fix etcd keys for services (#1922) 2020-08-10 21:58:35 +01:00
Asim Aslam
4db8ea8f6a Move tunnel to its own package (#1921) 2020-08-10 17:31:21 +01:00
Asim Aslam
13f495587e cleanup debug and transport (#1920) 2020-08-10 15:58:39 +01:00
ben-toogood
593b543230 runtime/kubernetes: fix streaming logs error handling (#1919) 2020-08-10 15:20:33 +01:00
ben-toogood
fdce953c15 runtime/kubernetes: fix update bug (#1918) 2020-08-10 15:08:04 +01:00
Janos Dobronszki
96836f2e43 Decrease log levels in router/registry package to not appear for CLI users (#1917) 2020-08-10 13:57:45 +01:00
Asim Aslam
65e6ee8566 use noop resolver in network by default 2020-08-09 22:11:57 +01:00
Asim Aslam
a7c70c66b1 return a micro error on lookup failure 2020-08-09 21:44:39 +01:00
Asim Aslam
b2582c0992 fix deadlock bug 2020-08-09 19:39:21 +01:00
Asim Aslam
6373cc91b7 remove print statement 2020-08-09 19:35:07 +01:00
Asim Aslam
ed704640aa getDomain should return the default domain 2020-08-09 19:08:25 +01:00
Asim Aslam
cd9e5a1e9e continue to allow endpoint routing 2020-08-09 16:57:34 +01:00
Asim Aslam
dcf040ec9f strip back the grpc proxy 2020-08-09 16:47:00 +01:00
Asim Aslam
f838c33008 noop.NewRegistry function 2020-08-09 16:26:51 +01:00
Asim Aslam
e8ea0f85e9 add a noop registry 2020-08-09 16:17:52 +01:00
Asim Aslam
51f8b4ae3d embed grpc server stream and client so they can be accessed (#1916) 2020-08-09 15:43:41 +01:00
Asim Aslam
69a2032dd7 lower log level to debug 2020-08-08 14:04:18 +01:00
Asim Aslam
64feb6dff2 Add subscriber naem 2020-08-08 09:21:13 +01:00
Asim Aslam
4c95c65d81 Return service name in error 2020-08-08 09:09:34 +01:00
Asim Aslam
4469a41ae7 use a totally different client for the watcher in etcd 2020-08-08 01:40:41 +01:00
Asim Aslam
fc67593ee4 cleanup router watcher logic 2020-08-08 01:04:38 +01:00
Asim Aslam
e7cc3c2210 protect etcd watcher stop against race condition 2020-08-08 00:57:57 +01:00
Asim Aslam
712fe39a62 initChan is never evaluated because watchRegistry is a blocking call 2020-08-07 23:44:43 +01:00
Asim Aslam
9b14eb8aec close the existing etcd client if it exists 2020-08-07 23:09:06 +01:00
Asim Aslam
124b1bd7b7 add https prefix when using tls config for etcd 2020-08-07 22:46:05 +01:00
ben-toogood
ac1aace214 route the API subdomain (#1910)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-08-07 20:54:55 +01:00
Asim Aslam
324c4e6886 Router refresh (#1912)
* checkpoint

* Refresh and prune routes periodically in the registry router

* remove comment
2020-08-07 20:53:38 +01:00
Asim Aslam
d69a4a30cd fix etcd bug that causes deregister to be skipped (#1911) 2020-08-07 19:58:25 +01:00
Asim Aslam
a6d7b1d710 Move api/router/util to util/router (#1909) 2020-08-07 13:30:29 +01:00
Asim Aslam
8ee31b94a1 remove handler/util package in favour of util/router (#1908) 2020-08-07 12:47:20 +01:00
Asim Aslam
37cc7fda92 Update pprof.go 2020-08-07 12:18:01 +01:00
Asim Aslam
d61cbd29db Update README.md 2020-08-07 11:22:57 +01:00
Asim Aslam
b6ab124d83 Update README.md 2020-08-07 11:21:59 +01:00
Dominic Wong
835343d6a5 logs should return for non existent services (#1906) 2020-08-06 22:56:05 +01:00
Maarten Bezemer
74907987d1 codec - Allow to Write() nil body (#1905)
* codec - Allow to Write() nil body

* Oops we are in v3 now
2020-08-06 18:51:00 +01:00
Asim Aslam
fb8533b74e Update tunnel 2020-08-06 18:50:35 +01:00
Z
dcf785677f Fix: file-watcher bugs (#1897)
* Fix: file-watcher bugs

* Update watcher_linux.go

Co-authored-by: 杨铭哲 <yangmz@weipaitang.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-08-06 18:21:09 +01:00
ben-toogood
991cdba91d registry/etcd: fix services combining (#1901) 2020-08-06 15:38:50 +01:00
Asim Aslam
aefd052dd7 Etcd router bug fixing etcd path prefix matching name (#1899)
* add logging and don't get nodes where they exist in router

* add more logging

* Fix the etcd bug for name matching of keys and prefixes matching names
2020-08-06 12:42:14 +01:00
Asim Aslam
2b79910ad9 add logging and don't get nodes where they exist in router (#1898)
* add logging and don't get nodes where they exist in router

* add more logging
2020-08-06 11:32:06 +01:00
Asim Aslam
8674dc8e62 Remove precache in favour of just pulling by default 2020-08-06 10:27:58 +01:00
Asim Aslam
b93cd0c964 dont process endpoint unless absolutely necessary 2020-08-05 18:09:04 +01:00
Asim Aslam
39bd6a6ced skip watching routes if client proxy is set, remove later 2020-08-05 18:05:25 +01:00
Asim Aslam
03d47afe47 Fix proxy selector memory leak 2020-08-05 17:38:41 +01:00
ben-toogood
38ec233350 proxy/mucp: don't lookup routes if client using proxy (#1896) 2020-08-05 12:45:56 +01:00
Dominic Wong
eee91ed976 Check chan not closed before sending updates for memory loader (#1894)
* dont send on closed chan
2020-08-04 16:21:03 +01:00
ben-toogood
07fef9fd33 router/registry: fix initialization bug (#1893) 2020-08-04 11:43:01 +01:00
Asim Aslam
1106f1d996 remove agent 2020-08-04 07:51:10 +01:00
Asim Aslam
8e126e4fc1 Remove go-micro/web 2020-08-04 07:47:20 +01:00
Asim Aslam
1439b101ec ensure register ttl and interval are set 2020-08-03 22:44:29 +01:00
Asim Aslam
24e5b2a034 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-08-03 22:21:13 +01:00
Asim Aslam
971a962894 Fix typo 2020-08-03 22:20:30 +01:00
ben-toogood
31ed4aa0e8 registry/etcd: fix logging nil pointer dereference (#1889)
* registry/etcd: fix logging nil pointer dereference

* Fix stupid mistake

* Fix merge
2020-07-31 15:05:32 +01:00
ben-toogood
d2cea4b7b7 registry/etcd: fix logging nil pointer dereference (#1888) 2020-07-31 14:40:11 +01:00
ben-toogood
0b73d411ad client: rename WithServiceToken => WithAuthToken (#1887) 2020-07-31 11:36:33 +01:00
ben-toogood
83a64797fb Fix proxy being overriden by default addresses (#1886) 2020-07-31 08:55:08 +01:00
ben-toogood
e9fc5b1671 client: add proxy option (#1885)
* client: add proxy option

* client: add WithProxy CallOption

* use address option

* ProxyAddress => Proxy
2020-07-30 15:22:36 +01:00
ben-toogood
006bbefaf3 runtime: support for dynamic secrets (#1861)
* runtime: replace CreateCredentials with CreateSecret

* runtime/kubernetes: secrets support

* runtime: CreateSecret => WithSecret

* runtime: use map[string]string for secrets

* runtime/kubernetes: update to use kv secrets

* Fix merge conflict (missing import)

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-29 13:41:50 +01:00
Lars Lehtonen
3d1ba914fc tunnel: remove unused test loop (#1878)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-29 12:51:09 +01:00
yu
d66803a136 fix bug https://github.com/micro/go-micro/issues/1883 (#1884)
* fix #1883

* fix #1883

Co-authored-by: 杨羽 <yangyu@doumi.com>
2020-07-29 12:45:25 +01:00
ben-toogood
9813f98c8b config: remove default config (#1882)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-28 13:54:58 +01:00
c6163bb22f fix qson parsing on invalid input, close #1874 (#1880)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-07-28 12:34:50 +01:00
ben-toogood
cb4a2864da router/registry: fix not started bug (#1877) 2020-07-28 09:01:08 +01:00
Asim Aslam
f17e4fdb44 Update README.md (#1876) 2020-07-27 16:42:50 +01:00
Asim Aslam
563768b58a v3 refactor (#1868)
* Move to v3

Co-authored-by: Ben Toogood <bentoogood@gmail.com>
2020-07-27 13:22:00 +01:00
Dominic Wong
9dfeb98111 cockroach typo in init (#1872) 2020-07-27 10:16:40 +01:00
Asim Aslam
5f4491bb86 change network resolver http to localhost:8080 2020-07-26 11:21:57 +01:00
Janos Dobronszki
fbdf1f2c1c Fixing micro logs being follow by default against k8s (#1866) 2020-07-23 09:50:38 +02:00
ben-toogood
a3a7434f2c client/grpc: fix error panic (#1860) 2020-07-20 10:04:51 +01:00
Asim Aslam
592179c0a2 Remove deprecations since next release is v3 2020-07-19 19:59:33 +01:00
Asim Aslam
9b74bc52d6 Move defaults to defaults.go 2020-07-19 19:48:11 +01:00
Asim Aslam
05f3e1a125 Add cmd.Run function 2020-07-19 18:43:33 +01:00
Asim Aslam
16c591d741 call cmd.Run 2020-07-19 18:20:17 +01:00
Asim Aslam
755b816086 Use Run instead of Init for cmd 2020-07-19 18:14:18 +01:00
Asim Aslam
7aa92fa8b5 add String method to command 2020-07-19 17:20:05 +01:00
ijayer
5077683b70 refactor(logger): fix the name of defaultLogger receiver (#1859)
Closes #1858

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-19 16:55:50 +01:00
Asim Aslam
7f6cefd9c9 fix grpc test 2020-07-19 15:54:33 +01:00
Asim Aslam
647ce61dec some renaming of types in auth 2020-07-19 14:41:31 +01:00
Asim Aslam
d3326efd4b Move out the token package to util 2020-07-19 13:41:23 +01:00
Asim Aslam
6920677f1e Move rules.Verify to auth.VerifyAccess 2020-07-19 13:12:03 +01:00
Asim Aslam
1838e4a1ee remove auth provider 2020-07-19 11:37:40 +01:00
Asim Aslam
96233b2d9b auth jwt / service package comments 2020-07-19 10:53:38 +01:00
Asim Aslam
e082ac42a0 go fmt 2020-07-19 10:51:16 +01:00
Asim Aslam
d7ef224447 add selector package comment 2020-07-19 10:51:04 +01:00
Asim Aslam
8c6f4062ef Remove error proto in favour of go type 2020-07-19 09:29:48 +01:00
ben-toogood
0d860c53a6 runtime/kubernetes: ignore namespace already exists errors (#1852)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-18 11:12:05 +01:00
bcc890e47c router: pass node metadata to route instead of service metadata (#1855)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-07-17 16:45:22 +01:00
Di Wu
f9bf562393 fix typo in comments (#1840)
* remove global error tracking

* rpc_server: fix invalid register err

* fix typo

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-16 16:33:11 +01:00
dfa50a888d make LookupRoute exported (#1850)
* make LookupRoute exported

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

* add missing file

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-07-16 16:32:37 +01:00
Asim Aslam
e63b9015ae Add memcache implementation of cache (#1848) 2020-07-16 14:13:38 +01:00
3627e47f04 client/grpc: dont use codec for raw bytes payload (#1847)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-07-16 13:35:06 +01:00
Asim Aslam
7d41c2224e Don't close the stream (#1844) 2020-07-15 14:44:31 +01:00
ben-toogood
68927e875b store/service: use client passed in Init options (#1843) 2020-07-15 14:01:33 +01:00
ben-toogood
0c19a87c89 cmd/cmd: use service namespace as store database (#1842) 2020-07-15 11:31:42 +01:00
ben-toogood
f73ec65ac3 runtime/kubernetes: increase debugging (#1841) 2020-07-15 09:26:25 +01:00
Asim Aslam
b27e71ae64 rip out cmd.DefaultOptions 2020-07-14 22:23:54 +01:00
Asim Aslam
0299517f0d remove config readme 2020-07-14 21:55:05 +01:00
Asim Aslam
e1404a1100 remove secrets package 2020-07-14 21:53:43 +01:00
Asim Aslam
057d61063f Move back command 2020-07-14 20:57:13 +01:00
ben-toogood
73a3f596e8 util/kubernetes/client: set imagePullPolicy to always (#1838) 2020-07-14 15:17:23 +01:00
ben-toogood
0287ab8751 auth/service/proto: add Delete RPC (#1836)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-14 13:41:35 +01:00
Alex Unger
42c28f2b6d Fix Comments (#1833)
* update store url

* fix leftover copy paste
2020-07-14 13:13:32 +01:00
ben-toogood
a2bb0bea2d auth: add token issuer option (#1835) 2020-07-14 13:44:51 +02:00
ben-toogood
9f9c748f9b auth/service: fix account issuer bug (#1834) 2020-07-14 12:16:52 +01:00
ben-toogood
a5e9dc21ca util/wrapper: allow enforcing a specific namespace when verifying requests (#1832)
* auth/jwt: add debugging

* auth: more debugging

* auth: more debugging

* util/wrapper: don't use request context

* util/wrapper: AuthHandlerNamespace

* remove debugging
2020-07-14 10:27:15 +01:00
Janos Dobronszki
3f4b58b58c Let bolt do locking per each List Get etc op, instead of managing fil… (#1831) 2020-07-14 10:35:46 +02:00
Asim Aslam
0a79db498c do not compare snapshot unless non nil (#1830) 2020-07-14 07:52:45 +01:00
Dominic Wong
7c5e3b0f30 report errors from log streaming (#1828) 2020-07-13 17:35:23 +01:00
ben-toogood
07fbb06ed8 auth/service: fix jwt blank refresh bug (#1827) 2020-07-13 17:12:03 +01:00
ben-toogood
a4252ba69c router/registry: use warn error level (#1826) 2020-07-13 15:27:14 +01:00
ben-toogood
8fe4f1f2c3 config/cmd: don't fatally error if config can't be loaded (#1825)
* config/cmd: don't fatally error if config cannot be loaded

* config/cmd: fix log level typo
2020-07-13 15:08:53 +01:00
ben-toogood
2e04fcd718 config/cmd: don't fatally error if auth account cannot be generated (#1824) 2020-07-13 12:52:54 +01:00
ben-toogood
7355455020 auth/service: generate accounts client side if JWT credentials present (#1823) 2020-07-13 10:20:31 +01:00
Lars Lehtonen
040577fb74 transport/grpc: replace deprecated grpc.WithTimeout() (#1822) 2020-07-13 07:35:53 +01:00
ben-toogood
4e7621da18 config/cmd: standardise error handling (#1816)
* runtime/kubernetes: remove reference to runtime cells

* config/cmd: standardize error handling

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-12 07:22:08 +01:00
Huan Huang
8e30ede8c7 track error (#1815)
* track error

* remove useless code

Co-authored-by: huanghuan.27@bytedance.com <huanghuan.27@bytedance.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-12 07:17:56 +01:00
Asim Aslam
630ceb5dad Update README.md (#1820) 2020-07-11 22:18:53 +01:00
Asim Aslam
85ae232936 Add model to service (#1819) 2020-07-11 21:15:59 +01:00
ben-toogood
13ea0eec02 :registry/mdns: fix nil pointer bug (#1818) 2020-07-10 17:47:07 +01:00
ben-toogood
09ec20fded runtime: provide credentials to services (#1817)
* runtime: inject credentials into service

* util/auth: self generate accounts (needed for jwt)

* runtime/kubernetes: add logging for creds

* runtime/kubernetes: serialize secret name

* runtime/kubernetes: remove unused code

* runtime/kubernetes: base64 encode secret

* runtime/kubernetes: remove metadata from secret

* util/kubernetes/client: omit empty secret metadata

* util/kubernetes/client: fix secret template

* util/kubernetes/client: fix secrets

* web: update auth util

* util/auth: fix missing arg

* extend token expiry

* extend token expiry
2020-07-10 16:25:46 +01:00
ben-toogood
3480e0a64e runtime/kubernetes: remove reference to runtime cells (#1814) 2020-07-09 20:21:34 +01:00
ben-toogood
318a80f824 config/cmd: improve cert loading for infra (#1813)
* config/cmd: improve cert loading for infra

* config/cmd: remove certificate_authorities flag

* config/cmd: fix caps

* config/cmd: fix bug with IsSet

* config/cmd: fix bool flags
2020-07-09 18:02:24 +01:00
ben-toogood
6d9a38a747 kubernetes: fixes for production (#1812)
* util/kubernetes/client: add secrets to deployments

* util/kubernetes/client: remove ServiceAccountName override

* debugging

* runtime/kubernetes: fix error

* runtime/kubernetes: remove test secret

* util/kubernetes/client: update default image

* util/kubernetes/client: remove default command for deployments

* runtime/kubernetes: pass source as arg

* runtime/kubernetes: remove debugging

* util/kubernetes/client: revert default image change
2020-07-09 16:29:01 +01:00
Asim Aslam
58d6726380 The start of mud - the micro data model (#1811)
* The start of mud - the micro data model

* add comments
2020-07-09 12:11:32 +01:00
Huan Huang
e5db6ea8a7 close r/w (#1810)
Co-authored-by: huanghuan.27@bytedance.com <huanghuan.27@bytedance.com>
2020-07-09 10:28:20 +01:00
Huan Huang
3468331506 feat: refactor register func (#1807)
Co-authored-by: huanghuan.27@bytedance.com <huanghuan.27@bytedance.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-08 18:38:01 +01:00
王旭
1bac08cc0e server.Init(Wait(nil)) update server options context (#1804)
Co-authored-by: wangxu <wangxu@oneniceapp.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-08 18:36:07 +01:00
ben-toogood
333320dcb8 config/cmd: secure broker (#1806)
* config/cmd: secure broker

* config/cmd: remove testing
2020-07-08 16:22:48 +01:00
Asim Aslam
ce12c040fa Model updates 2020-07-08 15:09:18 +01:00
Asim Aslam
ee36e26edc Add model interface (#1808) 2020-07-08 14:57:45 +01:00
Asim Aslam
3ffb899951 Fix cache options 2020-07-08 14:53:38 +01:00
Asim Aslam
00bd2bc65f cache interface (#1805) 2020-07-08 11:08:59 +01:00
ben-toogood
86f4235aaf config/cmd: custom certificate authorities & secure registry (#1803)
* config/cmd: add registry_secure option

* config/cmd: tmp load ca

* config/cmd: tmp load ca

* config/cmd: refactor certificate_authorities setup

* config/cmd: improve usage
2020-07-08 08:50:08 +01:00
Di Wu
b37f9c94b8 Fix invalid register check err in log (#1801)
* remove global error tracking

* rpc_server: fix invalid register err

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-07 14:14:23 +01:00
Asim Aslam
0ed1c70d29 Update README.md 2020-07-07 13:34:42 +01:00
Asim Aslam
db8e10834b Update README.md (#1802) 2020-07-07 13:33:54 +01:00
ben-toogood
0a937745cd auth: pass namespace options in auth service requests (#1800)
* auth: pass namespace options in auth service requests

* auth/service/proto: update field index

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-07 08:30:25 +01:00
ben-toogood
f5ed7e5833 config/source/service: change default namespace to micro (#1798)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-07 08:19:52 +01:00
Metauro
859b9e7786 feat(errors): add gateway, service error (#1797) 2020-07-06 20:14:59 +01:00
Lars Lehtonen
2b033b6495 store/cockroach: fix dropped errors (#1796)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-06 20:14:36 +01:00
Di Wu
51caf2a24e remove global error tracking (#1777)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-06 20:14:22 +01:00
Enix Yu
eaa46c2de7 Add recover in grpc service handler (#1727)
* 🐛 bug(grpc): add missing recover in grpc unkonwn service handler

* Add grpc handler recover testcases

* improve test case

Co-authored-by: ben-toogood <bentoogood@gmail.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-06 20:13:51 +01:00
Colin Hoglund
90dca65f55 make environment variable interpolation preprocessor optional (#1715) 2020-07-06 20:13:35 +01:00
97ae2979ad pass request context from request rpc endpoints (#1799)
http middleware can add additional metadata to context,
for example tracing wrappers, pass down it to underlining
services

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-07-06 13:55:17 +01:00
Asim Aslam
6f309dada3 fix typo (#1789) 2020-07-06 13:52:42 +01:00
Huan Huang
f99b436ec2 feat: fix register bug (#1791) 2020-07-03 14:38:25 +01:00
ben-toogood
c817f29d6e router/registry: fix registry not found bug (#1794) 2020-07-03 13:35:59 +01:00
ben-toogood
f744c6248f runtime/service: pass namespace option to service (#1792) 2020-07-03 11:37:14 +01:00
ben-toogood
4ff114e798 router/registry: fix rlock bug when registry errors (#1788)
* client: add select options

* router/registry: fix rlock bug when registry errors

* Revert "client: add select options"

This reverts commit 4d5283452e183f7387b604b51bde1deaf87ee391.

* router/registry: findRoutes under rlock

* add test

Co-authored-by: Dominic Wong <dom@micro.mu>
2020-07-02 18:29:11 +01:00
ben-toogood
c58ac35dfc broker/service: use wrapped micro client to authenticate requests (#1782) 2020-07-02 17:54:53 +01:00
ben-toogood
b5314829fa client/{grpc,rpc}: fix previous breaking change with CallFunc (router.Route vs registry.Node) (#1781) 2020-07-02 17:26:45 +01:00
ben-toogood
41c7688697 options: add router option (#1783)
* broker/service: use wrapped micro client to authenticate requests

* options: add router option

* Revert "broker/service: use wrapped micro client to authenticate requests"

This reverts commit f2383f37c86467b4ce534313a7f59993a904a712.
2020-07-02 17:17:07 +01:00
ben-toogood
b021546c09 client: add select options (#1786) 2020-07-02 17:03:08 +01:00
ben-toogood
6898a65508 selector: add filters to replace depricated client/selector filters (#1785) 2020-07-02 16:09:48 +01:00
ben-toogood
d577dd6abe config: add namespace support (#1780)
* config/cmd: use service namespace for config

* config/service: add namespace option to list
2020-07-02 14:07:58 +01:00
Janos Dobronszki
3c633e3577 Sparse checkout of repos, all repo downloads happen to different folders for concurrency support (#1779) 2020-07-02 14:40:40 +02:00
ben-toogood
174e44b846 Deprecate client/selector (#1767)
* client/{grpc,rpc}: depricate selector (wip)

* {client,cmd}: remove client/selector

* deprecate client/selector

* router/static: fix lookup

* config/cmd: add support for legacy static selector flag

* config/cmd: add support for legacy dns selector flag
2020-07-01 17:06:59 +01:00
Dominic Wong
a63480a81a router/registry: fix fallback fails if service has been seen already (#1776) 2020-07-01 15:20:30 +01:00
ben-toogood
6d9d94b105 api/router/registry: use resolved domain (#1774) 2020-07-01 12:23:55 +01:00
ben-toogood
64e9185386 router/registry: fix nil eventChan bug (#1773)
* router/registry: fix nil eventChan bug
2020-07-01 12:03:13 +01:00
ben-toogood
1b5c83f3cc registry/mdns: fix deregister bug (#1771)
* registry/mdns: fix deregister bug

* Update registry/mdns_registry.go

Co-authored-by: Dominic Wong <domwongemail@googlemail.com>
2020-07-01 11:09:20 +01:00
ben-toogood
979af853b9 proxy/mucp: use Micro-Namespace to filter networks (#1772) 2020-07-01 10:55:46 +01:00
ben-toogood
a64078b5c3 router/service: handle not found error (#1770) 2020-07-01 10:18:19 +01:00
Dominic Wong
58845d7012 only prewarm the route table if requested (#1765) 2020-07-01 10:05:21 +01:00
ben-toogood
dcf01ebbf0 router/registry: fix concurrent map iteration and map write (#1762) 2020-06-30 17:40:38 +01:00
Dominic Wong
355ad2a1af push tags to docker hub (#1766) 2020-06-30 17:26:59 +01:00
ben-toogood
b882ff3df9 selector: update selector.Select to accept a slice of structs (#1764) 2020-06-30 15:51:26 +01:00
ben-toogood
6337c92cd0 selector: new selector interface with random & roundrobin implementation (#1761)
* selector: implement new selector interface plus random & roundrobin implementations

* selector/roundrobin: remove unused consts

* router: add close method to interface

* selector/roundrobin: fix concurrent map iteration and map write

* selector: replace variadic argument on Select
2020-06-30 14:54:38 +01:00
ben-toogood
a95accad56 router: add metadata to routes (#1763)
* router: add metadata to route

* router/registry: set node metadata in route

* router/service: pass metadata to/from router service
2020-06-30 14:10:13 +01:00
Dominic Wong
6532b6208b MDNS registry fix for users on VPNs (#1759)
* filter out unsolicited responses
* send to local ip in case
* allow ip func to be passed in. add option for sending to 0.0.0.0
2020-06-30 11:12:52 +01:00
ben-toogood
0f5c53b6e4 selector: use custom domain (#1760)
* util/wrapper: improve auth errors

* client: add network call option

* client/selector: add domain select option

* client/grpc: pass network option to selector
2020-06-30 10:07:52 +01:00
ben-toogood
deea8fecf4 router/registry: add fallback if routes aren't found in the cache (#1758)
* router/registry: add fallback if routes aren't found in the cache

* router: fix rlock bug

* router/registry: pass fetchRoutes into the table, not the router
2020-06-30 09:53:49 +01:00
ben-toogood
df3e5364ca api/resolver: add resolve options (#1756)
* api/resolver: Resolve options

* router/registry: fix init bug

* router/registry: fix wildcard query bug

* web: fix registation domain bug

* registry/etcd: pass domain in service metadata

* api/resolver/subdomain: expose domain func

* Update api/resolver/subdomain/subdomain.go

Co-authored-by: Dominic Wong <domwongemail@googlemail.com>

Co-authored-by: Dominic Wong <domwongemail@googlemail.com>
2020-06-29 16:37:45 +01:00
Di Wu
132c1e35fe Fix invalid usage for sync.WaitGroup (#1752)
* Custom private blocks

* Fix invalid usage for sync.WaitGroup

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-27 20:07:04 +01:00
Dominic Wong
5967a68e78 cached file store (#1739)
* cached file store
2020-06-26 16:13:53 +01:00
ben-toogood
104b7d8f8d api/resolver: update resolver to enable subdomain routing (#1747)
* api/resolver: update domain / service prefix usage

* api/resolver/subdomain: implement subdomain resolver for domain resolution

* api/handler: fix tests
2020-06-26 14:28:18 +01:00
ben-toogood
4f0f4326df router: improve router configuration (#1745)
* router: update default address to :8084

* service: add router to service options

* config/cmd: improve router setup
2020-06-26 10:38:11 +01:00
ben-toogood
ee02511658 proxy/mucp: add support for multi-tenancy (#1746) 2020-06-26 10:31:06 +01:00
ben-toogood
a8fc5590a8 client/selector: query across multiple domains (#1725)
* client/selector: query across multiple domains

* client/selector: check for nil services

* config/cmd: fix merge bug
2020-06-25 15:40:23 +01:00
ben-toogood
bc60f23ff6 config/cmd: setup registry before router (#1743) 2020-06-25 15:24:31 +01:00
ben-toogood
2000da6fd8 router/registry: add support for registry domains (#1744) 2020-06-25 12:35:00 +01:00
ben-toogood
5ab475636a server/{grpc,rpc}: fix deregister domain bug (#1742)
* server/{grpc,rpc}: fix deregister domain bug

* server/grpc: remove unnecessary slice
2020-06-25 11:25:43 +01:00
ben-toogood
51b4ab0abc registry/memory: watcher bug fixes (#1740)
* registry/memory: watcher bugfixes

* registry/memory: fix nil watcher bug

* registry/memory: fix watcher test
2020-06-25 11:02:35 +01:00
ben-toogood
687a5e2e58 util/wrapper: fix 401 error when requesting the default namespace (#1741) 2020-06-25 10:19:03 +01:00
Dominic Wong
fcd307d902 Build and test forked PRs properly (#1738)
* build forks on PRs properly
2020-06-24 23:41:27 +01:00
ben-toogood
00cd07a3a6 util/wrapper: set auth credentials on streams (#1735) 2020-06-24 16:45:34 +01:00
Dominic Wong
a2a1f4dfbd support mono repo deps (#1736)
* support mono repo deps

* add protoc
2020-06-24 16:27:22 +01:00
ben-toogood
2b506b1a2a auth/service: use address option since router may not be configured (#1734) 2020-06-24 13:47:43 +01:00
ben-toogood
a2550820d3 router: add to service options; add dns and static implementations (#1733)
* config/cmd: add router to service options

* router/service: use micro client
2020-06-24 11:46:51 +01:00
ben-toogood
c940961574 router: update interface (#1732)
* router: replace Start and Stop with Close

* router: update default network to micro

* router: update tests
2020-06-24 11:09:16 +01:00
Dominic Wong
695cc9d526 Build against micro and examples on pull requests (#1724)
* Build against micro and examples on pull requests
2020-06-19 17:19:58 +01:00
ben-toogood
87543b2c8a registry/etcd: add support for domain options (#1714) 2020-06-19 14:58:16 +01:00
ben-toogood
5f9c3a6efd registry/cache: add support for the domain option (#1722) 2020-06-19 13:16:44 +01:00
ben-toogood
2b889087bd config/cmd: fix selector setup bug (#1723) 2020-06-19 13:11:48 +01:00
ben-toogood
ece02a6d21 util/wrapper: fix noop auth nil account bug (#1721)
* util/wrapper: fix noop nil account

* util/wrapper: improve comments

* util/wrapper: update tests
2020-06-19 12:16:39 +01:00
ben-toogood
58c6bbbf6b registry/service: pass domain options via rpc (#1719)
* registry/service: regenerate proto

* registry/service: pass domain in proto request options

* registry/service: stop defaulting metadata

* registry: add default domain const; remove from implementations

* registry/memory: fix typo
2020-06-19 10:34:12 +01:00
ben-toogood
c16f4b741c server: register in the services namespace (#1718) 2020-06-19 09:24:32 +01:00
Colin Hoglund
83cecdb294 config: use configured reader by default (#1717) 2020-06-19 08:49:30 +01:00
ben-toogood
8c7c27c573 registry/memory: add support for domain options (#1713)
* registry/memory: add support for the domain options

* registry/memory: swap Fatal test cases with Error

* registry/memory: fix wildcard not found bug

* registry/memory: replace locks with rlocks

* registry/memory: fix deregistration bug
2020-06-18 12:39:19 +01:00
ben-toogood
5fd36d6cc0 config/cmd: remove package duplicate initialization (#1711) 2020-06-18 10:14:04 +01:00
ben-toogood
3b40fde68b registry/mdns: add domain support (#1708)
* registry: add domain options

* registry/mdns: implement domain options

* registry/mdns: return node domain in metadata when querying using wildcard

* Fix nil pointer exception

* registry/mdns: return error from deregister

* registy/mdns: rename tld => domain
2020-06-17 13:23:41 +01:00
ben-toogood
9d3365c4be auth: rename auth.Namespace to auth.Issuer (#1710) 2020-06-17 12:26:27 +01:00
sunfuze
2efb459c66 Fix config watch (#1670)
* add dirty overrite test case

* need version to figure out if config need update or not

* using nanosecond as version for two goroutine can run in same second

* config should check snapshot version when update

* set checksum of ChangeSet

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-16 17:10:52 +01:00
Di Wu
6add74b4f6 Custom private blocks (#1705)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-16 17:05:42 +01:00
Dominic Wong
c67d78f1ef update PR template, not using gitflow anymore 2020-06-16 17:00:15 +01:00
Dominic Wong
a89610ffea Merge pull request #1706 from micro/develop
Develop -> Master. Abandoning gitflow model
2020-06-16 16:57:40 +01:00
Dominic Wong
da9bb11240 Merge branch 'master' into develop 2020-06-16 16:52:24 +01:00
Dmitry Kozlov
a3a1a84172 Split long discord output message into the chunks by 2000 characters (#1704)
Signed-off-by: Dmitry Kozlov <dmitry.f.kozlov@gmail.com>
2020-06-15 22:22:00 +01:00
ben-toogood
1179d7e89a registry/mdns: fix nil host bug (#1703) 2020-06-15 16:13:45 +01:00
Asim Aslam
a5df913926 Update README.md (#1695) 2020-06-12 15:07:19 +01:00
Asim Aslam
9ce706191b Update FUNDING.yml (#1692) 2020-06-12 15:07:19 +01:00
Dominic Wong
0327f30e3c Fix regex detection. Fixes #1663 (#1696) 2020-06-12 10:42:52 +01:00
Dominic Wong
0ce132eb8f Fix race condition when updating process being waited on (#1694) 2020-06-12 10:42:52 +01:00
Janos Dobronszki
00b76e0a64 Initialize selector before we make an auth.Generate call (#1693) 2020-06-12 10:42:52 +01:00
Dominic Wong
aec27be9b4 Fix race when opening DB for first time (#1691) 2020-06-12 10:42:52 +01:00
Dominic Wong
86dfcb819b Ignore "no such process" error (#1686)
* Cleanup how status is updated for service. Ignore "no such process" error as it could be that the pid died

* add nice error log to record process error exit
2020-06-12 10:42:52 +01:00
Janos Dobronszki
d613804b0a Sigterm instead of Sigkill (#1687)
Co-authored-by: Dominic Wong <domwongemail@googlemail.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-12 10:42:52 +01:00
92e9d05432 api/handler/rpc: dont log error on normal websocket error code (#1688)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-06-12 10:42:52 +01:00
ben-toogood
8dfd93e915 util/wrapper: Add Static Client wrapper (#1685)
* util/wrapper: Add Static Client wrapper

* util/wrapper/static: pass address to stream too

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

* add static client wrapper tests

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

* server: fix error message spaces between words

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

* server/{rpc,grpc}: replace log.Error with log.Errorf

* server/grpc: fix log typo

* server/rpc: fix log typo

Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-06-12 10:42:52 +01:00
Dominic Wong
e5136332e3 Add build and test of micro to pre-release testing (#1684)
* fix up example test build

* build and test micro when cutting a new release
2020-06-12 10:42:52 +01:00
Dominic Wong
f10fd4b479 Build all micro/examples for release-X.X.X branches (#1683)
* Build all the examples on push to any release branch
2020-06-12 10:42:52 +01:00
ben-toogood
74368026a5 Fix incorrect namespace variable name (merge conflict) (#1677) 2020-06-12 10:42:52 +01:00
ben-toogood
fde1aa9d6a Move auth account creation to config/cmd (#1676) 2020-06-12 10:42:52 +01:00
ben-toogood
f45cdba9ba Apply wrappers to gRPC streams (#1675)
* Add wrappers to grpc streams

* Fix typo
2020-06-12 10:42:52 +01:00
Dominic Wong
73c2f25935 Fix regex detection. Fixes #1663 (#1696) 2020-06-10 11:18:03 +01:00
Asim Aslam
b270860b79 Update README.md (#1695) 2020-06-10 10:22:53 +01:00
Dominic Wong
8e81cea96f Fix race condition when updating process being waited on (#1694) 2020-06-10 09:36:41 +01:00
Janos Dobronszki
cdd8f9fd82 Initialize selector before we make an auth.Generate call (#1693) 2020-06-09 12:47:31 +02:00
Asim Aslam
e7ba930236 Update FUNDING.yml (#1692) 2020-06-08 18:12:19 +01:00
Dominic Wong
a346064eaf Fix race when opening DB for first time (#1691) 2020-06-08 16:19:22 +01:00
Dominic Wong
47bdd5c993 Ignore "no such process" error (#1686)
* Cleanup how status is updated for service. Ignore "no such process" error as it could be that the pid died

* add nice error log to record process error exit
2020-06-08 10:47:25 +01:00
Janos Dobronszki
9af12ff9df Sigterm instead of Sigkill (#1687)
Co-authored-by: Dominic Wong <domwongemail@googlemail.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-06 14:04:14 +01:00
6c7bcf3883 api/handler/rpc: dont log error on normal websocket error code (#1688)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-06-06 00:03:43 +03:00
ben-toogood
bbc3b7040b util/wrapper: Add Static Client wrapper (#1685)
* util/wrapper: Add Static Client wrapper

* util/wrapper/static: pass address to stream too

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

* add static client wrapper tests

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

* server: fix error message spaces between words

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

* server/{rpc,grpc}: replace log.Error with log.Errorf

* server/grpc: fix log typo

* server/rpc: fix log typo

Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-06-05 10:18:35 +01:00
Dominic Wong
582f2e8b94 Add build and test of micro to pre-release testing (#1684)
* fix up example test build

* build and test micro when cutting a new release
2020-06-04 13:46:22 +01:00
Dominic Wong
bd3ef67328 Build all micro/examples for release-X.X.X branches (#1683)
* Build all the examples on push to any release branch
2020-06-04 09:32:17 +01:00
Dominic Wong
1ccd4cd940 Merge branch 'master' into develop 2020-06-03 10:41:39 +01:00
Dominic Wong
aa679f7a73 Create PULL_REQUEST_TEMPLATE.md 2020-06-03 10:32:28 +01:00
ben-toogood
003731ace9 Fix incorrect namespace variable name (merge conflict) (#1677) 2020-06-03 09:48:19 +01:00
Asim Aslam
7b379bf1f1 WIP: Add metadata to store record (#1604)
* Add metadata to store record

* Add metadata to cockroach store

* add metadata to store service implementation

* fix breaking cache test

* Test/fix cockroach metadata usage

* fix store memory metadata bug
2020-06-03 09:45:08 +01:00
ben-toogood
b6f3e8b715 Move auth account creation to config/cmd (#1676) 2020-06-03 09:43:20 +01:00
ben-toogood
8f6ec21b91 Apply wrappers to gRPC streams (#1675)
* Add wrappers to grpc streams

* Fix typo
2020-06-02 17:56:26 +01:00
Dominic Wong
e4e56b0f3f Merge pull request #1671 from sadwxqezc/fix-jwt
Fix jwt revoke
2020-06-02 09:27:14 +01:00
huanghuan.27@bytedance.com
219d29f664 fix jwt revoke 2020-06-02 10:26:33 +08:00
Asim Aslam
8fb138af06 Update README.md 2020-05-31 11:56:55 +01:00
579 changed files with 15005 additions and 39345 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
# These are supported funding model platforms
github: asim
github: micro

9
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,9 @@
## Pull Request template
Please, go through these steps before clicking submit on this PR.
1. Give a descriptive title to your PR.
2. Provide a description of your changes.
3. Make sure you have some relevant tests.
4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable).
**PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING**

View File

@@ -19,3 +19,4 @@ jobs:
name: micro/go-micro
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tag_names: true

View File

@@ -25,4 +25,11 @@ jobs:
id: tests
env:
IN_TRAVIS_CI: yes
run: go test -v ./...
S3_BLOB_STORE_REGION: ${{ secrets.SCALEWAY_REGION }}
S3_BLOB_STORE_ENDPOINT: ${{ secrets.SCALEWAY_ENDPOINT }}
S3_BLOB_STORE_ACCESS_KEY: ${{ secrets.SCALEWAY_ACCESS_KEY }}
S3_BLOB_STORE_SECRET_KEY: ${{ secrets.SCALEWAY_SECRET_KEY }}
run: |
wget -qO- https://binaries.cockroachdb.com/cockroach-v20.1.4.linux-amd64.tgz | tar xvz
cockroach-v20.1.4.linux-amd64/cockroach start-single-node --insecure &
go test -v ./...

View File

@@ -0,0 +1,41 @@
#!/bin/bash
# set -x
function build_binary {
echo building $1
pushd $1
go build -o _main
local ret=$?
if [ $ret -gt 0 ]; then
failed=1
failed_arr+=($1)
fi
popd
}
function is_main {
grep "package main" -l -dskip $1/*.go > /dev/null 2>&1
}
function check_dir {
is_main $1
local ret=$?
if [ $ret == 0 ]; then
build_binary $1 $2
fi
for filename in $1/*; do
if [ -d $filename ]; then
check_dir $filename $2
fi
done
}
failed_arr=()
failed=0
go mod edit -replace github.com/micro/go-micro/v2=github.com/$2/v2@$1
check_dir . $1
if [ $failed -gt 0 ]; then
echo Some builds failed
printf '%s\n' "${failed_arr[@]}"
fi
exit $failed

19
.github/workflows/scripts/build-micro.sh vendored Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
# set -x
failed=0
go mod edit -replace github.com/micro/go-micro/v2=github.com/$2/v2@$1
# basic test, build the binary
go install
failed=$?
if [ $failed -gt 0 ]; then
exit $failed
fi
# unit tests
IN_TRAVIS_CI=yes go test -v ./...
./scripts/test-docker.sh
# Generate keys for JWT tests
ssh-keygen -f /tmp/sshkey -m pkcs8 -q -N ""
ssh-keygen -f /tmp/sshkey -e -m pkcs8 > /tmp/sshkey.pub
go clean -testcache && IN_TRAVIS_CI=yes go test --tags=integration -v ./test

View File

@@ -7,13 +7,17 @@ jobs:
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: Setup Kind
uses: engineerd/setup-kind@v0.4.0
with:
version: v0.8.1
- name: Check out code into the Go module directory
uses: actions/checkout@v2
@@ -25,27 +29,20 @@ jobs:
id: tests
env:
IN_TRAVIS_CI: yes
run: go test -v ./...
S3_BLOB_STORE_REGION: ${{ secrets.SCALEWAY_REGION }}
S3_BLOB_STORE_ENDPOINT: ${{ secrets.SCALEWAY_ENDPOINT }}
S3_BLOB_STORE_ACCESS_KEY: ${{ secrets.SCALEWAY_ACCESS_KEY }}
S3_BLOB_STORE_SECRET_KEY: ${{ secrets.SCALEWAY_SECRET_KEY }}
run: |
kubectl apply -f runtime/kubernetes/test/test.yaml
sudo mkdir -p /var/run/secrets/kubernetes.io/serviceaccount
sudo chmod 777 /var/run/secrets/kubernetes.io/serviceaccount
wget -qO- https://binaries.cockroachdb.com/cockroach-v20.1.4.linux-amd64.tgz | tar -xvz
cockroach-v20.1.4.linux-amd64/cockroach start-single-node --insecure &
wget -q https://github.com/nats-io/nats-streaming-server/releases/download/v0.18.0/nats-streaming-server-v0.18.0-linux-amd64.zip
unzip ./nats-streaming-server-v0.18.0-linux-amd64.zip
export PATH=$PATH:./nats-streaming-server-v0.18.0-linux-amd64
nats-streaming-server &
go test -tags kubernetes,nats -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 }}

View File

@@ -1,6 +1,6 @@
# 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) <a href="https://slack.micro.mu"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
# 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/v3?tab=overview) [![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 distributed systems development.
Go Micro is a standard library for microservices.
## Overview
@@ -48,24 +48,7 @@ are pluggable and allows Go Micro to be runtime agnostic. You can plugin any und
## Getting Started
To make use of Go Micro
```golang
import "github.com/micro/go-micro/v2"
// create a new service
service := micro.NewService(
micro.Name("helloworld"),
)
// initialise flags
service.Init()
// start the service
service.Run()
```
See the [docs](https://dev.micro.mu) for detailed information on the architecture, installation and use of go-micro.
See [pkg.go.dev](https://pkg.go.dev/github.com/micro/go-micro/v3?tab=overview) for usage.
## License

View File

@@ -1,2 +0,0 @@
// Package agent provides an interface for building robots
package agent

View File

@@ -1,54 +0,0 @@
// Package command is an interface for defining bot commands
package command
var (
// Commmands keyed by golang/regexp patterns
// regexp.Match(key, input) is used to match
Commands = map[string]Command{}
)
// Command is the interface for specific named
// commands executed via plugins or the bot.
type Command interface {
// Executes the command with args passed in
Exec(args ...string) ([]byte, error)
// Usage of the command
Usage() string
// Description of the command
Description() string
// Name of the command
String() string
}
type cmd struct {
name string
usage string
description string
exec func(args ...string) ([]byte, error)
}
func (c *cmd) Description() string {
return c.description
}
func (c *cmd) Exec(args ...string) ([]byte, error) {
return c.exec(args...)
}
func (c *cmd) Usage() string {
return c.usage
}
func (c *cmd) String() string {
return c.name
}
// NewCommand helps quickly create a new command
func NewCommand(name, usage, description string, exec func(args ...string) ([]byte, error)) Command {
return &cmd{
name: name,
usage: usage,
description: description,
exec: exec,
}
}

View File

@@ -1,65 +0,0 @@
package command
import (
"testing"
)
func TestCommand(t *testing.T) {
c := &cmd{
name: "test",
usage: "test usage",
description: "test description",
exec: func(args ...string) ([]byte, error) {
return []byte("test"), nil
},
}
if c.String() != c.name {
t.Fatalf("expected name %s got %s", c.name, c.String())
}
if c.Usage() != c.usage {
t.Fatalf("expected usage %s got %s", c.usage, c.Usage())
}
if c.Description() != c.description {
t.Fatalf("expected description %s got %s", c.description, c.Description())
}
if r, err := c.Exec(); err != nil {
t.Fatal(err)
} else if string(r) != "test" {
t.Fatalf("expected exec result test got %s", string(r))
}
}
func TestNewCommand(t *testing.T) {
c := &cmd{
name: "test",
usage: "test usage",
description: "test description",
exec: func(args ...string) ([]byte, error) {
return []byte("test"), nil
},
}
nc := NewCommand(c.name, c.usage, c.description, c.exec)
if nc.String() != c.name {
t.Fatalf("expected name %s got %s", c.name, nc.String())
}
if nc.Usage() != c.usage {
t.Fatalf("expected usage %s got %s", c.usage, nc.Usage())
}
if nc.Description() != c.description {
t.Fatalf("expected description %s got %s", c.description, nc.Description())
}
if r, err := nc.Exec(); err != nil {
t.Fatal(err)
} else if string(r) != "test" {
t.Fatalf("expected exec result test got %s", string(r))
}
}

View File

@@ -1,22 +0,0 @@
# Discord input for micro-bot
[Discord](https://discordapp.com) support for micro bot based on [discordgo](github.com/bwmarrin/discordgo).
This was originally written by Aleksandr Tihomirov (@zet4) and can be found at https://github.com/zet4/micro-misc/.
## Options
### discord_token
You have to supply an application token via `--discord_token`.
Head over to Discord's [developer introduction](https://discordapp.com/developers/docs/intro)
to learn how to create applications and how the API works.
### discord_prefix
Set a command prefix with `--discord_prefix`. The default prefix is `Micro `.
You can mention the bot or use the prefix to run a command.
### discord_whitelist
Pass a list of comma-separated user IDs with `--discord_whitelist`. Only allow
these users to use the bot.

View File

@@ -1,96 +0,0 @@
package discord
import (
"errors"
"strings"
"sync"
"github.com/bwmarrin/discordgo"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v2/logger"
)
type discordConn struct {
master *discordInput
exit chan struct{}
recv chan *discordgo.Message
sync.Mutex
}
func newConn(master *discordInput) *discordConn {
conn := &discordConn{
master: master,
exit: make(chan struct{}),
recv: make(chan *discordgo.Message),
}
conn.master.session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.ID == master.botID {
return
}
whitelisted := false
for _, ID := range conn.master.whitelist {
if m.Author.ID == ID {
whitelisted = true
break
}
}
if len(master.whitelist) > 0 && !whitelisted {
return
}
var valid bool
m.Message.Content, valid = conn.master.prefixfn(m.Message.Content)
if !valid {
return
}
conn.recv <- m.Message
})
return conn
}
func (dc *discordConn) Recv(event *input.Event) error {
for {
select {
case <-dc.exit:
return errors.New("connection closed")
case msg := <-dc.recv:
event.From = msg.ChannelID + ":" + msg.Author.ID
event.To = dc.master.botID
event.Type = input.TextEvent
event.Data = []byte(msg.Content)
return nil
}
}
}
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 {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error("[bot][loop][send]", err)
}
}
return nil
}
func (dc *discordConn) Close() error {
if err := dc.master.session.Close(); err != nil {
return err
}
select {
case <-dc.exit:
return nil
default:
close(dc.exit)
}
return nil
}

View File

@@ -1,153 +0,0 @@
package discord
import (
"fmt"
"sync"
"errors"
"strings"
"github.com/bwmarrin/discordgo"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
)
func init() {
input.Inputs["discord"] = newInput()
}
func newInput() *discordInput {
return &discordInput{}
}
type discordInput struct {
token string
whitelist []string
prefix string
prefixfn func(string) (string, bool)
botID string
session *discordgo.Session
sync.Mutex
running bool
exit chan struct{}
}
func (d *discordInput) Flags() []cli.Flag {
return []cli.Flag{
&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",
EnvVars: []string{"MICRO_DISCORD_WHITELIST"},
Usage: "Discord Whitelist (seperated by ,)",
},
&cli.StringFlag{
Name: "discord_prefix",
Usage: "Discord Prefix",
EnvVars: []string{"MICRO_DISCORD_PREFIX"},
Value: "Micro ",
},
}
}
func (d *discordInput) Init(ctx *cli.Context) error {
token := ctx.String("discord_token")
whitelist := ctx.String("discord_whitelist")
prefix := ctx.String("discord_prefix")
if len(token) == 0 {
return errors.New("require token")
}
d.token = token
d.prefix = prefix
if len(whitelist) > 0 {
d.whitelist = strings.Split(whitelist, ",")
}
return nil
}
func (d *discordInput) Start() error {
if len(d.token) == 0 {
return errors.New("missing discord configuration")
}
d.Lock()
defer d.Unlock()
if d.running {
return nil
}
var err error
d.session, err = discordgo.New("Bot " + d.token)
if err != nil {
return err
}
u, err := d.session.User("@me")
if err != nil {
return err
}
d.botID = u.ID
d.prefixfn = CheckPrefixFactory(fmt.Sprintf("<@%s> ", d.botID), fmt.Sprintf("<@!%s> ", d.botID), d.prefix)
d.exit = make(chan struct{})
d.running = true
return nil
}
func (d *discordInput) Stream() (input.Conn, error) {
d.Lock()
defer d.Unlock()
if !d.running {
return nil, errors.New("not running")
}
//Fire-n-forget close just in case...
d.session.Close()
conn := newConn(d)
if err := d.session.Open(); err != nil {
return nil, err
}
return conn, nil
}
func (d *discordInput) Stop() error {
d.Lock()
defer d.Unlock()
if !d.running {
return nil
}
close(d.exit)
d.running = false
return nil
}
func (d *discordInput) String() string {
return "discord"
}
// CheckPrefixFactory Creates a prefix checking function and stuff.
func CheckPrefixFactory(prefixes ...string) func(string) (string, bool) {
return func(content string) (string, bool) {
for _, prefix := range prefixes {
if strings.HasPrefix(content, prefix) {
return strings.TrimPrefix(content, prefix), true
}
}
return "", false
}
}

View File

@@ -1,55 +0,0 @@
// Package input is an interface for bot inputs
package input
import (
"github.com/micro/cli/v2"
)
type EventType string
const (
TextEvent EventType = "text"
)
var (
// Inputs keyed by name
// Example slack or hipchat
Inputs = map[string]Input{}
)
// Event is the unit sent and received
type Event struct {
Type EventType
From string
To string
Data []byte
Meta map[string]interface{}
}
// Input is an interface for sources which
// provide a way to communicate with the bot.
// Slack, HipChat, XMPP, etc.
type Input interface {
// Provide cli flags
Flags() []cli.Flag
// Initialise input using cli context
Init(*cli.Context) error
// Stream events from the input
Stream() (Conn, error)
// Start the input
Start() error
// Stop the input
Stop() error
// name of the input
String() string
}
// Conn interface provides a way to
// send and receive events. Send and
// Recv both block until succeeding
// or failing.
type Conn interface {
Close() error
Recv(*Event) error
Send(*Event) error
}

View File

@@ -1,160 +0,0 @@
package slack
import (
"errors"
"fmt"
"strings"
"sync"
"time"
"github.com/micro/go-micro/v2/agent/input"
"github.com/nlopes/slack"
)
// Satisfies the input.Conn interface
type slackConn struct {
auth *slack.AuthTestResponse
rtm *slack.RTM
exit chan bool
sync.Mutex
names map[string]string
}
func (s *slackConn) run() {
// func retrieves user names and maps to IDs
setNames := func() {
names := make(map[string]string)
users, err := s.rtm.Client.GetUsers()
if err != nil {
return
}
for _, user := range users {
names[user.ID] = user.Name
}
s.Lock()
s.names = names
s.Unlock()
}
setNames()
t := time.NewTicker(time.Minute)
defer t.Stop()
for {
select {
case <-s.exit:
return
case <-t.C:
setNames()
}
}
}
func (s *slackConn) getName(id string) string {
s.Lock()
name := s.names[id]
s.Unlock()
return name
}
func (s *slackConn) Close() error {
select {
case <-s.exit:
return nil
default:
close(s.exit)
}
return nil
}
func (s *slackConn) Recv(event *input.Event) error {
if event == nil {
return errors.New("event cannot be nil")
}
for {
select {
case <-s.exit:
return errors.New("connection closed")
case e := <-s.rtm.IncomingEvents:
switch ev := e.Data.(type) {
case *slack.MessageEvent:
// only accept type message
if ev.Type != "message" {
continue
}
// only accept DMs or messages to me
switch {
case strings.HasPrefix(ev.Channel, "D"):
case strings.HasPrefix(ev.Text, s.auth.User):
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
default:
continue
}
// Strip username from text
switch {
case strings.HasPrefix(ev.Text, s.auth.User):
args := strings.Split(ev.Text, " ")[1:]
ev.Text = strings.Join(args, " ")
event.To = s.auth.User
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
args := strings.Split(ev.Text, " ")[1:]
ev.Text = strings.Join(args, " ")
event.To = s.auth.UserID
}
if event.Meta == nil {
event.Meta = make(map[string]interface{})
}
// fill in the blanks
event.From = ev.Channel + ":" + ev.User
event.Type = input.TextEvent
event.Data = []byte(ev.Text)
event.Meta["reply"] = ev
return nil
case *slack.InvalidAuthEvent:
return errors.New("invalid credentials")
}
}
}
}
func (s *slackConn) Send(event *input.Event) error {
var channel, message, name string
if len(event.To) == 0 {
return errors.New("require Event.To")
}
parts := strings.Split(event.To, ":")
if len(parts) == 2 {
channel = parts[0]
name = s.getName(parts[1])
// try using reply meta
} else if ev, ok := event.Meta["reply"]; ok {
channel = ev.(*slack.MessageEvent).Channel
name = s.getName(ev.(*slack.MessageEvent).User)
}
// don't know where to send the message
if len(channel) == 0 {
return errors.New("could not determine who message is to")
}
if len(name) == 0 || strings.HasPrefix(channel, "D") {
message = string(event.Data)
} else {
message = fmt.Sprintf("@%s: %s", name, string(event.Data))
}
s.rtm.SendMessage(s.rtm.NewOutgoingMessage(message, channel))
return nil
}

View File

@@ -1,147 +0,0 @@
package slack
import (
"errors"
"sync"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
"github.com/nlopes/slack"
)
type slackInput struct {
debug bool
token string
sync.Mutex
running bool
exit chan bool
api *slack.Client
}
func init() {
input.Inputs["slack"] = NewInput()
}
func (p *slackInput) Flags() []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: "slack_debug",
Usage: "Slack debug output",
EnvVars: []string{"MICRO_SLACK_DEBUG"},
},
&cli.StringFlag{
Name: "slack_token",
Usage: "Slack token",
EnvVars: []string{"MICRO_SLACK_TOKEN"},
},
}
}
func (p *slackInput) Init(ctx *cli.Context) error {
debug := ctx.Bool("slack_debug")
token := ctx.String("slack_token")
if len(token) == 0 {
return errors.New("missing slack token")
}
p.debug = debug
p.token = token
return nil
}
func (p *slackInput) Stream() (input.Conn, error) {
p.Lock()
defer p.Unlock()
if !p.running {
return nil, errors.New("not running")
}
// test auth
auth, err := p.api.AuthTest()
if err != nil {
return nil, err
}
rtm := p.api.NewRTM()
exit := make(chan bool)
go rtm.ManageConnection()
go func() {
select {
case <-p.exit:
select {
case <-exit:
return
default:
close(exit)
}
case <-exit:
}
rtm.Disconnect()
}()
conn := &slackConn{
auth: auth,
rtm: rtm,
exit: exit,
names: make(map[string]string),
}
go conn.run()
return conn, nil
}
func (p *slackInput) Start() error {
if len(p.token) == 0 {
return errors.New("missing slack token")
}
p.Lock()
defer p.Unlock()
if p.running {
return nil
}
api := slack.New(p.token, slack.OptionDebug(p.debug))
// test auth
_, err := api.AuthTest()
if err != nil {
return err
}
p.api = api
p.exit = make(chan bool)
p.running = true
return nil
}
func (p *slackInput) Stop() error {
p.Lock()
defer p.Unlock()
if !p.running {
return nil
}
close(p.exit)
p.running = false
return nil
}
func (p *slackInput) String() string {
return "slack"
}
func NewInput() input.Input {
return &slackInput{}
}

View File

@@ -1,18 +0,0 @@
# Telegram Messenger input for micro bot
[Telegram](https://telegram.org) support for micro bot based on [telegram-bot-api](https://github.com/go-telegram-bot-api/telegram-bot-api).
## Options
### --telegram_token (required)
Sets bot's token for interacting with API.
Head over to Telegram's [API documentation](https://core.telegram.org/bots/api)
to learn how to create bots and how the API works.
### --telegram_debug
Sets the debug flag to make the bot's output verbose.
### --telegram_whitelist
Sets a list of comma-separated nicknames (without @ symbol in the beginning) for interacting with bot. Only these users can use the bot.

View File

@@ -1,115 +0,0 @@
package telegram
import (
"errors"
"strings"
"sync"
"github.com/forestgiant/sliceutil"
"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 {
input *telegramInput
recv <-chan tgbotapi.Update
exit chan bool
syncCond *sync.Cond
mutex sync.Mutex
}
func newConn(input *telegramInput) (*telegramConn, error) {
conn := &telegramConn{
input: input,
}
conn.syncCond = sync.NewCond(&conn.mutex)
go conn.run()
return conn, nil
}
func (tc *telegramConn) run() {
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates, err := tc.input.api.GetUpdatesChan(u)
if err != nil {
return
}
tc.recv = updates
tc.syncCond.Signal()
select {
case <-tc.exit:
return
}
}
func (tc *telegramConn) Close() error {
return nil
}
func (tc *telegramConn) Recv(event *input.Event) error {
if event == nil {
return errors.New("event cannot be nil")
}
for {
if tc.recv == nil {
tc.mutex.Lock()
tc.syncCond.Wait()
}
update := <-tc.recv
if update.Message == nil || (len(tc.input.whitelist) > 0 && !sliceutil.Contains(tc.input.whitelist, update.Message.From.UserName)) {
continue
}
if event.Meta == nil {
event.Meta = make(map[string]interface{})
}
event.Type = input.TextEvent
event.From = update.Message.From.UserName
event.To = tc.input.api.Self.UserName
event.Data = []byte(update.Message.Text)
event.Meta["chatId"] = update.Message.Chat.ID
event.Meta["chatType"] = update.Message.Chat.Type
event.Meta["messageId"] = update.Message.MessageID
return nil
}
}
func (tc *telegramConn) Send(event *input.Event) error {
messageText := strings.TrimSpace(string(event.Data))
chatId := event.Meta["chatId"].(int64)
chatType := ChatType(event.Meta["chatType"].(string))
msgConfig := tgbotapi.NewMessage(chatId, messageText)
msgConfig.ParseMode = tgbotapi.ModeHTML
if sliceutil.Contains([]ChatType{Group, Supergroup}, chatType) {
msgConfig.ReplyToMessageID = event.Meta["messageId"].(int)
}
_, err := tc.input.api.Send(msgConfig)
if err != nil {
// probably it could be because of nested HTML tags -- telegram doesn't allow nested tags
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)
}
return nil
}

View File

@@ -1,101 +0,0 @@
package telegram
import (
"errors"
"strings"
"sync"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
tgbotapi "gopkg.in/telegram-bot-api.v4"
)
type telegramInput struct {
sync.Mutex
debug bool
token string
whitelist []string
api *tgbotapi.BotAPI
}
type ChatType string
const (
Private ChatType = "private"
Group ChatType = "group"
Supergroup ChatType = "supergroup"
)
func init() {
input.Inputs["telegram"] = &telegramInput{}
}
func (ti *telegramInput) Flags() []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: "telegram_debug",
EnvVars: []string{"MICRO_TELEGRAM_DEBUG"},
Usage: "Telegram debug output",
},
&cli.StringFlag{
Name: "telegram_token",
EnvVars: []string{"MICRO_TELEGRAM_TOKEN"},
Usage: "Telegram token",
},
&cli.StringFlag{
Name: "telegram_whitelist",
EnvVars: []string{"MICRO_TELEGRAM_WHITELIST"},
Usage: "Telegram bot's users (comma-separated values)",
},
}
}
func (ti *telegramInput) Init(ctx *cli.Context) error {
ti.debug = ctx.Bool("telegram_debug")
ti.token = ctx.String("telegram_token")
whitelist := ctx.String("telegram_whitelist")
if whitelist != "" {
ti.whitelist = strings.Split(whitelist, ",")
}
if len(ti.token) == 0 {
return errors.New("missing telegram token")
}
return nil
}
func (ti *telegramInput) Stream() (input.Conn, error) {
ti.Lock()
defer ti.Unlock()
return newConn(ti)
}
func (ti *telegramInput) Start() error {
ti.Lock()
defer ti.Unlock()
api, err := tgbotapi.NewBotAPI(ti.token)
if err != nil {
return err
}
ti.api = api
api.Debug = ti.debug
return nil
}
func (ti *telegramInput) Stop() error {
return nil
}
func (p *telegramInput) String() string {
return "telegram"
}

View File

@@ -1,333 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// 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"
)
// 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 HelpRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
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_79b974b8c77805fa, []int{0}
}
func (m *HelpRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HelpRequest.Unmarshal(m, b)
}
func (m *HelpRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HelpRequest.Marshal(b, m, deterministic)
}
func (m *HelpRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelpRequest.Merge(m, src)
}
func (m *HelpRequest) XXX_Size() int {
return xxx_messageInfo_HelpRequest.Size(m)
}
func (m *HelpRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HelpRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HelpRequest proto.InternalMessageInfo
type HelpResponse struct {
Usage string `protobuf:"bytes,1,opt,name=usage,proto3" json:"usage,omitempty"`
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
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_79b974b8c77805fa, []int{1}
}
func (m *HelpResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HelpResponse.Unmarshal(m, b)
}
func (m *HelpResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HelpResponse.Marshal(b, m, deterministic)
}
func (m *HelpResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelpResponse.Merge(m, src)
}
func (m *HelpResponse) XXX_Size() int {
return xxx_messageInfo_HelpResponse.Size(m)
}
func (m *HelpResponse) XXX_DiscardUnknown() {
xxx_messageInfo_HelpResponse.DiscardUnknown(m)
}
var xxx_messageInfo_HelpResponse proto.InternalMessageInfo
func (m *HelpResponse) GetUsage() string {
if m != nil {
return m.Usage
}
return ""
}
func (m *HelpResponse) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
type ExecRequest struct {
Args []string `protobuf:"bytes,1,rep,name=args,proto3" json:"args,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
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_79b974b8c77805fa, []int{2}
}
func (m *ExecRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ExecRequest.Unmarshal(m, b)
}
func (m *ExecRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ExecRequest.Marshal(b, m, deterministic)
}
func (m *ExecRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ExecRequest.Merge(m, src)
}
func (m *ExecRequest) XXX_Size() int {
return xxx_messageInfo_ExecRequest.Size(m)
}
func (m *ExecRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ExecRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ExecRequest proto.InternalMessageInfo
func (m *ExecRequest) GetArgs() []string {
if m != nil {
return m.Args
}
return nil
}
type ExecResponse struct {
Result []byte `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"`
Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
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_79b974b8c77805fa, []int{3}
}
func (m *ExecResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ExecResponse.Unmarshal(m, b)
}
func (m *ExecResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ExecResponse.Marshal(b, m, deterministic)
}
func (m *ExecResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ExecResponse.Merge(m, src)
}
func (m *ExecResponse) XXX_Size() int {
return xxx_messageInfo_ExecResponse.Size(m)
}
func (m *ExecResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ExecResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ExecResponse proto.InternalMessageInfo
func (m *ExecResponse) GetResult() []byte {
if m != nil {
return m.Result
}
return nil
}
func (m *ExecResponse) GetError() string {
if m != nil {
return m.Error
}
return ""
}
func init() {
proto.RegisterType((*HelpRequest)(nil), "go.micro.bot.HelpRequest")
proto.RegisterType((*HelpResponse)(nil), "go.micro.bot.HelpResponse")
proto.RegisterType((*ExecRequest)(nil), "go.micro.bot.ExecRequest")
proto.RegisterType((*ExecResponse)(nil), "go.micro.bot.ExecResponse")
}
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,
}
// 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,110 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: agent/proto/bot.proto
package go_micro_bot
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
api "github.com/micro/go-micro/v2/api"
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 _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Command service
func NewCommandEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Command service
type CommandService interface {
Help(ctx context.Context, in *HelpRequest, opts ...client.CallOption) (*HelpResponse, error)
Exec(ctx context.Context, in *ExecRequest, opts ...client.CallOption) (*ExecResponse, error)
}
type commandService struct {
c client.Client
name string
}
func NewCommandService(name string, c client.Client) CommandService {
return &commandService{
c: c,
name: name,
}
}
func (c *commandService) Help(ctx context.Context, in *HelpRequest, opts ...client.CallOption) (*HelpResponse, error) {
req := c.c.NewRequest(c.name, "Command.Help", in)
out := new(HelpResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *commandService) Exec(ctx context.Context, in *ExecRequest, opts ...client.CallOption) (*ExecResponse, error) {
req := c.c.NewRequest(c.name, "Command.Exec", in)
out := new(ExecResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Command service
type CommandHandler interface {
Help(context.Context, *HelpRequest, *HelpResponse) error
Exec(context.Context, *ExecRequest, *ExecResponse) error
}
func RegisterCommandHandler(s server.Server, hdlr CommandHandler, opts ...server.HandlerOption) error {
type command interface {
Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error
Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error
}
type Command struct {
command
}
h := &commandHandler{hdlr}
return s.Handle(s.NewHandler(&Command{h}, opts...))
}
type commandHandler struct {
CommandHandler
}
func (h *commandHandler) Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error {
return h.CommandHandler.Help(ctx, in, out)
}
func (h *commandHandler) Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error {
return h.CommandHandler.Exec(ctx, in, out)
}

View File

@@ -1,25 +0,0 @@
syntax = "proto3";
package go.micro.bot;
service Command {
rpc Help(HelpRequest) returns (HelpResponse) {};
rpc Exec(ExecRequest) returns (ExecResponse) {};
}
message HelpRequest {
}
message HelpResponse {
string usage = 1;
string description = 2;
}
message ExecRequest {
repeated string args = 1;
}
message ExecResponse {
bytes result = 1;
string error = 2;
}

View File

@@ -5,8 +5,8 @@ import (
"regexp"
"strings"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/server"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/server"
)
type Api interface {
@@ -18,7 +18,7 @@ type Api interface {
Register(*Endpoint) error
// Register a route
Deregister(*Endpoint) error
// Implemenation of api
// Implementation of api
String() string
}

View File

@@ -4,13 +4,13 @@ package api
import (
"net/http"
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"
goapi "github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
api "github.com/micro/go-micro/v3/api/proto"
"github.com/micro/go-micro/v3/client"
"github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/util/ctx"
"github.com/micro/go-micro/v3/util/router"
)
type apiHandler struct {
@@ -71,10 +71,8 @@ func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// create the context from headers
cx := ctx.FromRequest(r)
// create strategy
so := selector.WithStrategy(strategy(service.Services))
if err := c.Call(cx, req, rsp, client.WithSelectOption(so)); err != nil {
if err := c.Call(cx, req, rsp, client.WithRouter(router.New(service.Services))); err != nil {
w.Header().Set("Content-Type", "application/json")
ce := errors.Parse(err.Error())
switch ce.Code {

View File

@@ -7,9 +7,7 @@ import (
"net/http"
"strings"
api "github.com/micro/go-micro/v2/api/proto"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v2/registry"
api "github.com/micro/go-micro/v3/api/proto"
"github.com/oxtoacart/bpool"
)
@@ -109,11 +107,3 @@ func requestToProto(r *http.Request) (*api.Request, error) {
return req, nil
}
// strategy is a hack for selection
func strategy(services []*registry.Service) selector.Strategy {
return func(_ []*registry.Service) selector.Next {
// ignore input to this function, use services above
return selector.Random(services)
}
}

View File

@@ -11,9 +11,9 @@ import (
"time"
"github.com/google/uuid"
"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/micro/go-micro/v3/api/handler"
proto "github.com/micro/go-micro/v3/api/proto"
"github.com/micro/go-micro/v3/util/ctx"
"github.com/oxtoacart/bpool"
)

View File

@@ -4,13 +4,14 @@ package http
import (
"errors"
"fmt"
"math/rand"
"net/http"
"net/http/httputil"
"net/url"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/registry"
)
const (
@@ -20,7 +21,7 @@ const (
type httpHandler struct {
options handler.Options
// set with different initialiser
// set with different initializer
s *api.Service
}
@@ -64,16 +65,19 @@ func (h *httpHandler) getService(r *http.Request) (string, error) {
return "", errors.New("no route found")
}
// create a random selector
next := selector.Random(service.Services)
// get the next node
s, err := next()
if err != nil {
return "", nil
// get the nodes for this service
var nodes []*registry.Node
for _, srv := range service.Services {
nodes = append(nodes, srv.Nodes...)
}
return fmt.Sprintf("http://%s", s.Address), nil
// select a random node
if len(nodes) == 0 {
return "", errors.New("no route found")
}
node := nodes[rand.Int()%len(nodes)]
return fmt.Sprintf("http://%s", node.Address), nil
}
func (h *httpHandler) String() string {

View File

@@ -6,13 +6,13 @@ import (
"net/http/httptest"
"testing"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v2/api/resolver/vpath"
"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"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/api/resolver"
"github.com/micro/go-micro/v3/api/resolver/vpath"
"github.com/micro/go-micro/v3/api/router"
regRouter "github.com/micro/go-micro/v3/api/router/registry"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/memory"
)
func testHttp(t *testing.T, path, service, ns string) {
@@ -58,7 +58,7 @@ func testHttp(t *testing.T, path, service, ns string) {
router.WithHandler("http"),
router.WithRegistry(r),
router.WithResolver(vpath.NewResolver(
resolver.WithNamespace(resolver.StaticNamespace(ns)),
resolver.WithServicePrefix(ns),
)),
)

View File

@@ -1,9 +1,9 @@
package handler
import (
"github.com/micro/go-micro/v2/api/router"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/grpc"
"github.com/micro/go-micro/v3/api/router"
"github.com/micro/go-micro/v3/client"
"github.com/micro/go-micro/v3/client/grpc"
)
var (
@@ -62,7 +62,7 @@ func WithClient(c client.Client) Option {
}
}
// WithmaxRecvSize specifies max body size
// WithMaxRecvSize specifies max body size
func WithMaxRecvSize(size int64) Option {
return func(o *Options) {
o.MaxRecvSize = size

View File

@@ -5,25 +5,23 @@ import (
"encoding/json"
"io"
"net/http"
"net/textproto"
"strconv"
"strings"
jsonpatch "github.com/evanphx/json-patch/v5"
"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/micro/go-micro/v2/util/qson"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/api/internal/proto"
"github.com/micro/go-micro/v3/client"
"github.com/micro/go-micro/v3/codec"
"github.com/micro/go-micro/v3/codec/jsonrpc"
"github.com/micro/go-micro/v3/codec/protorpc"
"github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/go-micro/v3/metadata"
"github.com/micro/go-micro/v3/util/ctx"
"github.com/micro/go-micro/v3/util/qson"
"github.com/micro/go-micro/v3/util/router"
"github.com/oxtoacart/bpool"
)
@@ -65,14 +63,6 @@ func (b *buffer) Write(_ []byte) (int, error) {
return 0, nil
}
// strategy is a hack for selection
func strategy(services []*registry.Service) selector.Strategy {
return func(_ []*registry.Service) selector.Next {
// ignore input to this function, use services above
return selector.Random(services)
}
}
func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if h.opts.MaxRecvSize > 0 {
@@ -113,36 +103,17 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// create context
cx := ctx.FromRequest(r)
// get context from http handler wrappers
md, ok := metadata.FromContext(r.Context())
if !ok {
md = make(metadata.Metadata)
}
// fill contex with http headers
md["Host"] = r.Host
md["Method"] = r.Method
// get canonical headers
for k, _ := range r.Header {
// may be need to get all values for key like r.Header.Values() provide in go 1.14
md[textproto.CanonicalMIMEHeaderKey(k)] = r.Header.Get(k)
}
// merge context with overwrite
cx = metadata.MergeContext(cx, md, true)
// set merged context to request
*r = *r.Clone(cx)
// if stream we currently only support json
if isStream(r, service) {
// drop older context as it can have timeouts and create new
// md, _ := metadata.FromContext(cx)
//serveWebsocket(context.TODO(), w, r, service, c)
serveWebsocket(cx, w, r, service, c)
return
}
// create strategy
so := selector.WithStrategy(strategy(service.Services))
// create custom router
callOpt := client.WithRouter(router.New(service.Services))
// walk the standard call path
// get payload
@@ -174,7 +145,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
)
// make the call
if err := c.Call(cx, req, response, client.WithSelectOption(so)); err != nil {
if err := c.Call(cx, req, response, callOpt); err != nil {
writeError(w, r, err)
return
}
@@ -209,7 +180,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
client.WithContentType(ct),
)
// make the call
if err := c.Call(cx, req, &response, client.WithSelectOption(so)); err != nil {
if err := c.Call(cx, req, &response, callOpt); err != nil {
writeError(w, r, err)
return
}
@@ -278,7 +249,7 @@ func requestPayload(r *http.Request) ([]byte, error) {
return nil, err
}
return raw.Marshal()
case strings.Contains(ct, "application/www-x-form-urlencoded"):
case strings.Contains(ct, "application/x-www-form-urlencoded"):
r.ParseForm()
// generate a new set of values from the form
@@ -294,7 +265,7 @@ func requestPayload(r *http.Request) ([]byte, error) {
// otherwise as per usual
ctx := r.Context()
// dont user meadata.FromContext as it mangles names
// dont user metadata.FromContext as it mangles names
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(map[string]string)
@@ -393,6 +364,13 @@ func requestPayload(r *http.Request) ([]byte, error) {
bodybuf = b
}
if bodydst == "" || bodydst == "*" {
// jsonpatch resequences the json object so we avoid it if possible (some usecases such as
// validating signatures require the request body to be unchangedd). We're keeping support
// for the custom paramaters for backwards compatability reasons.
if string(out) == "{}" {
return bodybuf, nil
}
if out, err = jsonpatch.MergeMergePatches(out, bodybuf); err == nil {
return out, nil
}
@@ -439,7 +417,6 @@ func requestPayload(r *http.Request) ([]byte, error) {
//fallback to previous unknown behaviour
return bodybuf, nil
}
return []byte{}, nil

View File

@@ -7,7 +7,7 @@ import (
"testing"
"github.com/golang/protobuf/proto"
go_api "github.com/micro/go-micro/v2/api/proto"
go_api "github.com/micro/go-micro/v3/api/proto"
)
func TestRequestPayloadFromRequest(t *testing.T) {

View File

@@ -12,11 +12,11 @@ import (
"github.com/gobwas/httphead"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
raw "github.com/micro/go-micro/v2/codec/bytes"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/client"
raw "github.com/micro/go-micro/v3/codec/bytes"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/go-micro/v3/util/router"
)
// serveWebsocket will stream rpc back over websockets assuming json
@@ -110,9 +110,11 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
client.StreamingRequest(),
)
so := selector.WithStrategy(strategy(service.Services))
// create custom router
callOpt := client.WithRouter(router.New(service.Services))
// create a new stream
stream, err := c.Stream(ctx, req, client.WithSelectOption(so))
stream, err := c.Stream(ctx, req, callOpt)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
@@ -185,7 +187,11 @@ func writeLoop(rw io.ReadWriter, stream client.Stream) {
if err != nil {
if wserr, ok := err.(wsutil.ClosedError); ok {
switch wserr.Code {
case ws.StatusGoingAway:
// this happens when user leave the page
return
case ws.StatusNormalClosure, ws.StatusNoStatusRcvd:
// this happens when user close ws connection, or we don't get any status
return
}
}

View File

@@ -5,15 +5,16 @@ import (
"errors"
"fmt"
"io"
"math/rand"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/registry"
)
const (
@@ -70,16 +71,19 @@ func (wh *webHandler) getService(r *http.Request) (string, error) {
return "", errors.New("no route found")
}
// create a random selector
next := selector.Random(service.Services)
// get the next node
s, err := next()
if err != nil {
return "", nil
// get the nodes
var nodes []*registry.Node
for _, srv := range service.Services {
nodes = append(nodes, srv.Nodes...)
}
if len(nodes) == 0 {
return "", errors.New("no route found")
}
return fmt.Sprintf("http://%s", s.Address), nil
// select a random node
node := nodes[rand.Int()%len(nodes)]
return fmt.Sprintf("http://%s", node.Address), nil
}
// serveWebSocket used to serve a web socket proxied connection

View File

@@ -6,12 +6,17 @@ import (
"net/http"
"strings"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v3/api/resolver"
)
type Resolver struct{}
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
// parse options
options := resolver.NewResolveOptions(opts...)
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
// /foo.Bar/Service
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
@@ -26,6 +31,7 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
@@ -34,5 +40,5 @@ func (r *Resolver) String() string {
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
return &Resolver{opts: resolver.NewOptions(opts...)}
}

View File

@@ -4,19 +4,23 @@ package host
import (
"net/http"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v3/api/resolver"
)
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
// parse options
options := resolver.NewResolveOptions(opts...)
return &resolver.Endpoint{
Name: req.Host,
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}

View File

@@ -1,23 +1,16 @@
package resolver
import (
"net/http"
"github.com/micro/go-micro/v3/registry"
)
// NewOptions returns new initialised options
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
if options.Namespace == nil {
options.Namespace = StaticNamespace("go.micro")
}
return options
type Options struct {
Handler string
ServicePrefix string
}
type Option func(o *Options)
// WithHandler sets the handler being used
func WithHandler(h string) Option {
return func(o *Options) {
@@ -25,9 +18,46 @@ func WithHandler(h string) Option {
}
}
// WithNamespace sets the function which determines the namespace for a request
func WithNamespace(n func(*http.Request) string) Option {
// WithServicePrefix sets the ServicePrefix option
func WithServicePrefix(p string) Option {
return func(o *Options) {
o.Namespace = n
o.ServicePrefix = p
}
}
// NewOptions returns new initialised options
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
return options
}
// ResolveOptions are used when resolving a request
type ResolveOptions struct {
Domain string
}
// ResolveOption sets an option
type ResolveOption func(*ResolveOptions)
// Domain sets the resolve Domain option
func Domain(n string) ResolveOption {
return func(o *ResolveOptions) {
o.Domain = n
}
}
// NewResolveOptions returns new initialised resolve options
func NewResolveOptions(opts ...ResolveOption) ResolveOptions {
var options ResolveOptions
for _, o := range opts {
o(&options)
}
if len(options.Domain) == 0 {
options.Domain = registry.DefaultDomain
}
return options
}

View File

@@ -5,26 +5,29 @@ import (
"net/http"
"strings"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v3/api/resolver"
)
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
// parse options
options := resolver.NewResolveOptions(opts...)
if req.URL.Path == "/" {
return nil, resolver.ErrNotFound
}
parts := strings.Split(req.URL.Path[1:], "/")
ns := r.opts.Namespace(req)
return &resolver.Endpoint{
Name: ns + "." + parts[0],
Name: r.opts.ServicePrefix + "." + parts[0],
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}

View File

@@ -13,7 +13,7 @@ var (
// Resolver resolves requests to endpoints
type Resolver interface {
Resolve(r *http.Request) (*Endpoint, error)
Resolve(r *http.Request, opts ...ResolveOption) (*Endpoint, error)
String() string
}
@@ -27,18 +27,6 @@ type Endpoint struct {
Method string
// HTTP Path e.g /greeter.
Path string
}
type Options struct {
Handler string
Namespace func(*http.Request) string
}
type Option func(o *Options)
// StaticNamespace returns the same namespace for each request
func StaticNamespace(ns string) func(*http.Request) string {
return func(*http.Request) string {
return ns
}
// Domain endpoint exists within
Domain string
}

View File

@@ -0,0 +1,85 @@
// Package subdomain is a resolver which uses the subdomain to determine the domain to route to. It
// offloads the endpoint resolution to a child resolver which is provided in New.
package subdomain
import (
"net"
"net/http"
"strings"
"github.com/micro/go-micro/v3/api/resolver"
"github.com/micro/go-micro/v3/logger"
"golang.org/x/net/publicsuffix"
)
func NewResolver(parent resolver.Resolver, opts ...resolver.Option) resolver.Resolver {
options := resolver.NewOptions(opts...)
return &Resolver{options, parent}
}
type Resolver struct {
opts resolver.Options
resolver.Resolver
}
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
if dom := r.Domain(req); len(dom) > 0 {
opts = append(opts, resolver.Domain(dom))
}
return r.Resolver.Resolve(req, opts...)
}
func (r *Resolver) Domain(req *http.Request) string {
// determine the host, e.g. foobar.m3o.app
host := req.URL.Hostname()
if len(host) == 0 {
if h, _, err := net.SplitHostPort(req.Host); err == nil {
host = h // host does contain a port
} else if strings.Contains(err.Error(), "missing port in address") {
host = req.Host // host does not contain a port
}
}
// check for an ip address
if net.ParseIP(host) != nil {
return ""
}
// check for dev enviroment
if host == "localhost" || host == "127.0.0.1" {
return ""
}
// extract the top level domain plus one (e.g. 'myapp.com')
domain, err := publicsuffix.EffectiveTLDPlusOne(host)
if err != nil {
logger.Debugf("Unable to extract domain from %v", host)
return ""
}
// there was no subdomain
if host == domain {
return ""
}
// remove the domain from the host, leaving the subdomain, e.g. "staging.foo.myapp.com" => "staging.foo"
subdomain := strings.TrimSuffix(host, "."+domain)
// ignore the API subdomain
if subdomain == "api" {
return ""
}
// return the reversed subdomain as the namespace, e.g. "staging.foo" => "foo-staging"
comps := strings.Split(subdomain, ".")
for i := len(comps)/2 - 1; i >= 0; i-- {
opp := len(comps) - 1 - i
comps[i], comps[opp] = comps[opp], comps[i]
}
return strings.Join(comps, "-")
}
func (r *Resolver) String() string {
return "subdomain"
}

View File

@@ -0,0 +1,71 @@
package subdomain
import (
"net/http"
"net/url"
"testing"
"github.com/micro/go-micro/v3/api/resolver/vpath"
"github.com/stretchr/testify/assert"
)
func TestResolve(t *testing.T) {
tt := []struct {
Name string
Host string
Result string
}{
{
Name: "Top level domain",
Host: "micro.mu",
Result: "micro",
},
{
Name: "Effective top level domain",
Host: "micro.com.au",
Result: "micro",
},
{
Name: "Subdomain dev",
Host: "dev.micro.mu",
Result: "dev",
},
{
Name: "Subdomain foo",
Host: "foo.micro.mu",
Result: "foo",
},
{
Name: "Multi-level subdomain",
Host: "staging.myapp.m3o.app",
Result: "myapp-staging",
},
{
Name: "Dev host",
Host: "127.0.0.1",
Result: "micro",
},
{
Name: "Localhost",
Host: "localhost",
Result: "micro",
},
{
Name: "IP host",
Host: "81.151.101.146",
Result: "micro",
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
r := NewResolver(vpath.NewResolver())
result, err := r.Resolve(&http.Request{URL: &url.URL{Host: tc.Host, Path: "foo/bar"}})
assert.Nil(t, err, "Expecter err to be nil")
if result != nil {
assert.Equal(t, tc.Result, result.Domain, "Expected %v but got %v", tc.Result, result.Domain)
}
})
}
}

View File

@@ -7,7 +7,7 @@ import (
"regexp"
"strings"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v3/api/resolver"
)
func NewResolver(opts ...resolver.Option) resolver.Resolver {
@@ -22,36 +22,41 @@ var (
re = regexp.MustCompile("^v[0-9]+$")
)
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
}
options := resolver.NewResolveOptions(opts...)
parts := strings.Split(req.URL.Path[1:], "/")
if len(parts) == 1 {
return &resolver.Endpoint{
Name: r.withNamespace(req, parts...),
Name: r.withPrefix(parts...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
// /v1/foo
if re.MatchString(parts[0]) {
return &resolver.Endpoint{
Name: r.withNamespace(req, parts[0:2]...),
Name: r.withPrefix(parts[0:2]...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
return &resolver.Endpoint{
Name: r.withNamespace(req, parts[0]),
Name: r.withPrefix(parts[0]),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
@@ -59,11 +64,12 @@ func (r *Resolver) String() string {
return "path"
}
func (r *Resolver) withNamespace(req *http.Request, parts ...string) string {
ns := r.opts.Namespace(req)
if len(ns) == 0 {
return strings.Join(parts, ".")
// withPrefix transforms "foo" into "go.micro.api.foo"
func (r *Resolver) withPrefix(parts ...string) string {
p := r.opts.ServicePrefix
if len(p) > 0 {
parts = append([]string{p}, parts...)
}
return strings.Join(append([]string{ns}, parts...), ".")
return strings.Join(parts, ".")
}

View File

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

View File

@@ -10,13 +10,13 @@ import (
"sync"
"time"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/router"
"github.com/micro/go-micro/v2/api/router/util"
"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/registry/cache"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/router"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/go-micro/v3/metadata"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/cache"
util "github.com/micro/go-micro/v3/util/router"
)
// endpoint struct, that holds compiled pcre
@@ -99,7 +99,7 @@ func (r *registryRouter) process(res *registry.Result) {
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)
logger.Errorf("unable to get %v service: %v", res.Service.Name, err)
}
return
}
@@ -127,7 +127,10 @@ func (r *registryRouter) store(services []*registry.Service) {
key := fmt.Sprintf("%s.%s", service.Name, sep.Name)
// decode endpoint
end := api.Decode(sep.Metadata)
// no endpoint or no name
if end == nil || len(end.Name) == 0 {
continue
}
// if we got nothing skip
if err := api.Validate(end); err != nil {
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
@@ -188,7 +191,7 @@ func (r *registryRouter) store(services []*registry.Service) {
for _, p := range ep.Endpoint.Path {
var pcreok bool
if p[0] == '^' && p[len(p)-1] != '$' {
if p[0] == '^' && p[len(p)-1] == '$' {
pcrereg, err := regexp.CompilePOSIX(p)
if err == nil {
cep.pcreregs = append(cep.pcreregs, pcrereg)
@@ -260,7 +263,7 @@ func (r *registryRouter) watch() {
res, err := w.Next()
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("error getting next endoint: %v", err)
logger.Errorf("error getting next endpoint: %v", err)
}
close(ch)
break
@@ -434,7 +437,7 @@ func (r *registryRouter) Route(req *http.Request) (*api.Service, error) {
name := rp.Name
// get service
services, err := r.rc.GetService(name)
services, err := r.rc.GetService(name, registry.GetDomain(rp.Domain))
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,34 @@
package registry
import (
"testing"
"github.com/micro/go-micro/v3/registry"
"github.com/stretchr/testify/assert"
)
func TestStoreRegex(t *testing.T) {
router := newRouter()
router.store([]*registry.Service{
{
Name: "Foobar",
Version: "latest",
Endpoints: []*registry.Endpoint{
{
Name: "foo",
Metadata: map[string]string{
"endpoint": "FooEndpoint",
"description": "Some description",
"method": "POST",
"path": "^/foo/$",
"handler": "rpc",
},
},
},
Metadata: map[string]string{},
},
},
)
assert.Len(t, router.ceps["Foobar.foo"].pcreregs, 1)
}

View File

@@ -4,7 +4,7 @@ package router
import (
"net/http"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v3/api"
)
// Router is used to determine an endpoint for a request

View File

@@ -9,18 +9,20 @@ import (
"testing"
"time"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/api/handler/rpc"
"github.com/micro/go-micro/v2/api/router"
rregistry "github.com/micro/go-micro/v2/api/router/registry"
rstatic "github.com/micro/go-micro/v2/api/router/static"
"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"
pb "github.com/micro/go-micro/v2/server/grpc/proto"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/api/handler/rpc"
"github.com/micro/go-micro/v3/api/router"
rregistry "github.com/micro/go-micro/v3/api/router/registry"
rstatic "github.com/micro/go-micro/v3/api/router/static"
"github.com/micro/go-micro/v3/client"
gcli "github.com/micro/go-micro/v3/client/grpc"
rmemory "github.com/micro/go-micro/v3/registry/memory"
rt "github.com/micro/go-micro/v3/router"
regRouter "github.com/micro/go-micro/v3/router/registry"
"github.com/micro/go-micro/v3/server"
gsrv "github.com/micro/go-micro/v3/server/grpc"
pb "github.com/micro/go-micro/v3/server/grpc/proto"
)
// server is used to implement helloworld.GreeterServer.
@@ -55,9 +57,13 @@ func initial(t *testing.T) (server.Server, client.Client) {
server.Registry(r),
)
rtr := regRouter.NewRouter(
rt.Registry(r),
)
// create a new server
c := gcli.NewClient(
client.Registry(r),
client.Router(rtr),
)
h := &testServer{}

View File

@@ -8,13 +8,13 @@ import (
"strings"
"sync"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/router"
"github.com/micro/go-micro/v2/api/router/util"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/registry"
rutil "github.com/micro/go-micro/v2/util/registry"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/router"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/go-micro/v3/metadata"
"github.com/micro/go-micro/v3/registry"
rutil "github.com/micro/go-micro/v3/util/registry"
util "github.com/micro/go-micro/v3/util/router"
)
type endpoint struct {

View File

@@ -7,8 +7,8 @@ import (
"net"
"os"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/api/server/acme"
"github.com/micro/go-micro/v3/logger"
"golang.org/x/crypto/acme/autocert"
)

View File

@@ -8,8 +8,8 @@ import (
"time"
"github.com/caddyserver/certmagic"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/api/server/acme"
"github.com/micro/go-micro/v3/logger"
)
type certmagicProvider struct {

View File

@@ -10,8 +10,8 @@ import (
"time"
"github.com/caddyserver/certmagic"
"github.com/micro/go-micro/v2/store"
"github.com/micro/go-micro/v2/sync"
"github.com/micro/go-micro/v3/store"
"github.com/micro/go-micro/v3/sync"
)
// File represents a "File" that will be stored in store.Store - the contents and last modified time

View File

@@ -40,5 +40,5 @@ func SetHeaders(w http.ResponseWriter, r *http.Request) {
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")
set(w, "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Namespace")
}

View File

@@ -9,9 +9,9 @@ import (
"sync"
"github.com/gorilla/handlers"
"github.com/micro/go-micro/v2/api/server"
"github.com/micro/go-micro/v2/api/server/cors"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/api/server"
"github.com/micro/go-micro/v3/api/server/cors"
"github.com/micro/go-micro/v3/logger"
)
type httpServer struct {

View File

@@ -4,8 +4,8 @@ import (
"crypto/tls"
"net/http"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/micro/go-micro/v3/api/resolver"
"github.com/micro/go-micro/v3/api/server/acme"
)
type Option func(o *Options)
@@ -23,9 +23,9 @@ type Options struct {
type Wrapper func(h http.Handler) http.Handler
func WrapHandler(w Wrapper) Option {
func WrapHandler(w ...Wrapper) Option {
return func(o *Options) {
o.Wrappers = append(o.Wrappers, w)
o.Wrappers = append(o.Wrappers, w...)
}
}

View File

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

@@ -1,110 +0,0 @@
// 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"
api "github.com/micro/go-micro/v2/api"
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 _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Api service
func NewApiEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// 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

@@ -1,18 +0,0 @@
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 {}

View File

@@ -2,14 +2,11 @@
package auth
import (
"context"
"errors"
"time"
)
const (
// BearerScheme used for Authorization header
BearerScheme = "Bearer "
// ScopePublic is the scope applied to a rule to allow access to the public
ScopePublic = ""
// ScopeAccount is the scope applied to a rule to limit to users with any valid account
@@ -49,7 +46,7 @@ type Auth interface {
// Account provided by an auth provider
type Account struct {
// ID of the account e.g. email
// ID of the account e.g. UUID. Should not change
ID string `json:"id"`
// Type of the account, e.g. service
Type string `json:"type"`
@@ -61,6 +58,8 @@ type Account struct {
Scopes []string `json:"scopes"`
// Secret for the account, e.g. the password
Secret string `json:"secret"`
// Name of the account. User friendly name that might change e.g. a username or email
Name string `json:"name"`
}
// Token can be short or long lived
@@ -115,19 +114,3 @@ type Rule struct {
// rule will be applied
Priority int32
}
type accountKey struct{}
// 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, bool) {
acc, ok := ctx.Value(accountKey{}).(*Account)
return acc, ok
}
// ContextWithAccount sets the account in the context
func ContextWithAccount(ctx context.Context, account *Account) context.Context {
return context.WithValue(ctx, accountKey{}, account)
}

View File

@@ -1,88 +0,0 @@
package auth
import (
"github.com/google/uuid"
"github.com/micro/go-micro/v2/auth/provider/basic"
)
var (
DefaultAuth = NewAuth()
)
func NewAuth(opts ...Option) Auth {
options := Options{
Provider: basic.NewProvider(),
}
for _, o := range opts {
o(&options)
}
return &noop{
opts: options,
}
}
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,
Secret: options.Secret,
Metadata: options.Metadata,
Scopes: options.Scopes,
Issuer: n.Options().Namespace,
}, nil
}
// Grant access to a resource
func (n *noop) Grant(rule *Rule) error {
return nil
}
// Revoke access to a resource
func (n *noop) Revoke(rule *Rule) error {
return nil
}
// Rules used to verify requests
func (n *noop) Rules(opts ...RulesOption) ([]*Rule, error) {
return []*Rule{}, nil
}
// Verify an account has access to a resource
func (n *noop) Verify(acc *Account, res *Resource, opts ...VerifyOption) error {
return nil
}
// Inspect a token
func (n *noop) Inspect(token string) (*Account, error) {
return &Account{ID: uuid.New().String(), Issuer: n.Options().Namespace}, nil
}
// Token generation using an account id and secret
func (n *noop) Token(opts ...TokenOption) (*Token, error) {
return &Token{}, nil
}

View File

@@ -1,35 +1,35 @@
// Package jwt is a jwt implementation of the auth interface
package jwt
import (
"sync"
"time"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/rules"
"github.com/micro/go-micro/v2/auth/token"
jwtToken "github.com/micro/go-micro/v2/auth/token/jwt"
"github.com/micro/go-micro/v3/auth"
"github.com/micro/go-micro/v3/util/token"
"github.com/micro/go-micro/v3/util/token/jwt"
)
// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
j := new(jwt)
j := new(jwtAuth)
j.Init(opts...)
return j
}
type jwt struct {
type jwtAuth struct {
options auth.Options
jwt token.Provider
token token.Provider
rules []*auth.Rule
sync.Mutex
}
func (j *jwt) String() string {
func (j *jwtAuth) String() string {
return "jwt"
}
func (j *jwt) Init(opts ...auth.Option) {
func (j *jwtAuth) Init(opts ...auth.Option) {
j.Lock()
defer j.Unlock()
@@ -37,31 +37,39 @@ func (j *jwt) Init(opts ...auth.Option) {
o(&j.options)
}
j.jwt = jwtToken.NewTokenProvider(
j.token = jwt.NewTokenProvider(
token.WithPrivateKey(j.options.PrivateKey),
token.WithPublicKey(j.options.PublicKey),
)
}
func (j *jwt) Options() auth.Options {
func (j *jwtAuth) Options() auth.Options {
j.Lock()
defer j.Unlock()
return j.options
}
func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
func (j *jwtAuth) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
options := auth.NewGenerateOptions(opts...)
if len(options.Issuer) == 0 {
options.Issuer = j.Options().Issuer
}
name := options.Name
if name == "" {
name = id
}
account := &auth.Account{
ID: id,
Type: options.Type,
Scopes: options.Scopes,
Metadata: options.Metadata,
Issuer: j.Options().Namespace,
Issuer: options.Issuer,
Name: name,
}
// generate a JWT secret which can be provided to the Token() method
// and exchanged for an access token
secret, err := j.jwt.Generate(account)
secret, err := j.token.Generate(account, token.WithExpiry(time.Hour*24*365))
if err != nil {
return nil, err
}
@@ -71,19 +79,19 @@ func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, e
return account, nil
}
func (j *jwt) Grant(rule *auth.Rule) error {
func (j *jwtAuth) Grant(rule *auth.Rule) error {
j.Lock()
defer j.Unlock()
j.rules = append(j.rules, rule)
return nil
}
func (j *jwt) Revoke(rule *auth.Rule) error {
func (j *jwtAuth) Revoke(rule *auth.Rule) error {
j.Lock()
defer j.Unlock()
rules := []*auth.Rule{}
for _, r := range rules {
for _, r := range j.rules {
if r.ID != rule.ID {
rules = append(rules, r)
}
@@ -93,7 +101,7 @@ func (j *jwt) Revoke(rule *auth.Rule) error {
return nil
}
func (j *jwt) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
func (j *jwtAuth) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
j.Lock()
defer j.Unlock()
@@ -102,20 +110,20 @@ func (j *jwt) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyO
o(&options)
}
return rules.Verify(j.rules, acc, res)
return auth.VerifyAccess(j.rules, acc, res)
}
func (j *jwt) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
func (j *jwtAuth) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
j.Lock()
defer j.Unlock()
return j.rules, nil
}
func (j *jwt) Inspect(token string) (*auth.Account, error) {
return j.jwt.Inspect(token)
func (j *jwtAuth) Inspect(token string) (*auth.Account, error) {
return j.token.Inspect(token)
}
func (j *jwt) Token(opts ...auth.TokenOption) (*auth.Token, error) {
func (j *jwtAuth) Token(opts ...auth.TokenOption) (*auth.Token, error) {
options := auth.NewTokenOptions(opts...)
secret := options.RefreshToken
@@ -123,17 +131,17 @@ func (j *jwt) Token(opts ...auth.TokenOption) (*auth.Token, error) {
secret = options.Secret
}
account, err := j.jwt.Inspect(secret)
account, err := j.token.Inspect(secret)
if err != nil {
return nil, err
}
access, err := j.jwt.Generate(account, token.WithExpiry(options.Expiry))
access, err := j.token.Generate(account, token.WithExpiry(options.Expiry))
if err != nil {
return nil, err
}
refresh, err := j.jwt.Generate(account, token.WithExpiry(options.Expiry+time.Hour))
refresh, err := j.token.Generate(account, token.WithExpiry(options.Expiry+time.Hour))
if err != nil {
return nil, err
}

85
auth/noop/noop.go Normal file
View File

@@ -0,0 +1,85 @@
package noop
import (
"github.com/google/uuid"
"github.com/micro/go-micro/v3/auth"
)
func NewAuth(opts ...auth.Option) auth.Auth {
var options auth.Options
for _, o := range opts {
o(&options)
}
return &noop{
opts: options,
}
}
type noop struct {
opts auth.Options
}
// String returns the name of the implementation
func (n *noop) String() string {
return "noop"
}
// Init the auth
func (n *noop) Init(opts ...auth.Option) {
for _, o := range opts {
o(&n.opts)
}
}
// Options set for auth
func (n *noop) Options() auth.Options {
return n.opts
}
// Generate a new account
func (n *noop) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
options := auth.NewGenerateOptions(opts...)
name := options.Name
if name == "" {
name = id
}
return &auth.Account{
ID: id,
Secret: options.Secret,
Metadata: options.Metadata,
Scopes: options.Scopes,
Issuer: n.Options().Issuer,
Name: name,
}, nil
}
// Grant access to a resource
func (n *noop) Grant(rule *auth.Rule) error {
return nil
}
// Revoke access to a resource
func (n *noop) Revoke(rule *auth.Rule) error {
return nil
}
// Rules used to verify requests
func (n *noop) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
return []*auth.Rule{}, nil
}
// Verify an account has access to a resource
func (n *noop) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
return nil
}
// Inspect a token
func (n *noop) Inspect(token string) (*auth.Account, error) {
return &auth.Account{ID: uuid.New().String(), Issuer: n.Options().Issuer}, nil
}
// Token generation using an account id and secret
func (n *noop) Token(opts ...auth.TokenOption) (*auth.Token, error) {
return &auth.Token{}, nil
}

View File

@@ -4,9 +4,7 @@ import (
"context"
"time"
"github.com/micro/go-micro/v2/auth/provider"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/store"
"github.com/micro/go-micro/v3/store"
)
func NewOptions(opts ...Option) Options {
@@ -14,16 +12,12 @@ func NewOptions(opts ...Option) Options {
for _, o := range opts {
o(&options)
}
if options.Client == nil {
options.Client = client.DefaultClient
}
return options
}
type Options struct {
// Namespace the service belongs to
Namespace string
// Issuer of the service's account
Issuer string
// ID is the services auth ID
ID string
// Secret is used to authenticate the service
@@ -34,16 +28,14 @@ type Options struct {
PublicKey string
// PrivateKey for encoding JWTs
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
// Client to use for RPC
Client client.Client
// Addrs sets the addresses of auth
Addrs []string
// Context to store other options
Context context.Context
}
type Option func(o *Options)
@@ -55,10 +47,10 @@ func Addrs(addrs ...string) Option {
}
}
// Namespace the service belongs to
func Namespace(n string) Option {
// Issuer of the services account
func Issuer(i string) Option {
return func(o *Options) {
o.Namespace = n
o.Issuer = i
}
}
@@ -98,13 +90,6 @@ func ClientToken(token *Token) Option {
}
}
// 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) {
@@ -112,13 +97,6 @@ func LoginURL(url string) Option {
}
}
// WithClient sets the client to use when making requests
func WithClient(c client.Client) Option {
return func(o *Options) {
o.Client = c
}
}
type GenerateOptions struct {
// Metadata associated with the account
Metadata map[string]string
@@ -130,6 +108,10 @@ type GenerateOptions struct {
Type string
// Secret used to authenticate the account
Secret string
// Issuer of the account, e.g. micro
Issuer string
// Name of the acouunt e.g. an email or username
Name string
}
type GenerateOption func(o *GenerateOptions)
@@ -169,6 +151,20 @@ func WithScopes(s ...string) GenerateOption {
}
}
// WithIssuer for the generated account
func WithIssuer(i string) GenerateOption {
return func(o *GenerateOptions) {
o.Issuer = i
}
}
// WithName for the generated account
func WithName(n string) GenerateOption {
return func(o *GenerateOptions) {
o.Name = n
}
}
// NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions
@@ -187,6 +183,8 @@ type TokenOptions struct {
RefreshToken string
// Expiry is the time the token should live for
Expiry time.Duration
// Issuer of the account
Issuer string
}
type TokenOption func(o *TokenOptions)
@@ -211,6 +209,12 @@ func WithToken(rt string) TokenOption {
}
}
func WithTokenIssuer(iss string) TokenOption {
return func(o *TokenOptions) {
o.Issuer = iss
}
}
// NewTokenOptions from a slice of options
func NewTokenOptions(opts ...TokenOption) TokenOptions {
var options TokenOptions
@@ -227,7 +231,8 @@ func NewTokenOptions(opts ...TokenOption) TokenOptions {
}
type VerifyOptions struct {
Context context.Context
Context context.Context
Namespace string
}
type VerifyOption func(o *VerifyOptions)
@@ -237,9 +242,15 @@ func VerifyContext(ctx context.Context) VerifyOption {
o.Context = ctx
}
}
func VerifyNamespace(ns string) VerifyOption {
return func(o *VerifyOptions) {
o.Namespace = ns
}
}
type RulesOptions struct {
Context context.Context
Context context.Context
Namespace string
}
type RulesOption func(o *RulesOptions)
@@ -249,3 +260,9 @@ func RulesContext(ctx context.Context) RulesOption {
o.Context = ctx
}
}
func RulesNamespace(ns string) RulesOption {
return func(o *RulesOptions) {
o.Namespace = ns
}
}

View File

@@ -1,34 +0,0 @@
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(...provider.EndpointOption) string {
return ""
}
func (b *basic) Redirect() string {
return ""
}

View File

@@ -1,65 +0,0 @@
package oauth
import (
"fmt"
"net/url"
"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(opts ...provider.EndpointOption) string {
var options provider.EndpointOptions
for _, o := range opts {
o(&options)
}
params := make(url.Values)
params.Add("response_type", "code")
if len(options.State) > 0 {
params.Add("state", options.State)
}
if len(options.LoginHint) > 0 {
params.Add("login_hint", options.LoginHint)
}
if clientID := o.opts.ClientID; len(clientID) > 0 {
params.Add("client_id", clientID)
}
if scope := o.opts.Scope; len(scope) > 0 {
params.Add("scope", scope)
}
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
}

View File

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

View File

@@ -1,49 +0,0 @@
// 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(...EndpointOption) 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
}
type EndpointOptions struct {
// State is a code to verify the req
State string
// LoginHint prefils the user id on oauth clients
LoginHint string
}
type EndpointOption func(*EndpointOptions)
func WithState(c string) EndpointOption {
return func(o *EndpointOptions) {
o.State = c
}
}
func WithLoginHint(hint string) EndpointOption {
return func(o *EndpointOptions) {
o.LoginHint = hint
}
}

View File

@@ -1,17 +1,15 @@
package rules
package auth
import (
"fmt"
"sort"
"strings"
"github.com/micro/go-micro/v2/auth"
)
// Verify an account has access to a resource using the rules provided. If the account does not have
// VerifyAccess an account has access to a resource using the rules provided. If the account does not have
// access an error will be returned. If there are no rules provided which match the resource, an error
// will be returned
func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
func VerifyAccess(rules []*Rule, acc *Account, res *Resource) error {
// the rule is only to be applied if the type matches the resource or is catch-all (*)
validTypes := []string{"*", res.Type}
@@ -29,7 +27,7 @@ func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
}
// filter the rules to the ones which match the criteria above
filteredRules := make([]*auth.Rule, 0)
filteredRules := make([]*Rule, 0)
for _, rule := range rules {
if !include(validTypes, rule.Resource.Type) {
continue
@@ -51,9 +49,9 @@ func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
// loop through the rules and check for a rule which applies to this account
for _, rule := range filteredRules {
// a blank scope indicates the rule applies to everyone, even nil accounts
if rule.Scope == auth.ScopePublic && rule.Access == auth.AccessDenied {
return auth.ErrForbidden
} else if rule.Scope == auth.ScopePublic && rule.Access == auth.AccessGranted {
if rule.Scope == ScopePublic && rule.Access == AccessDenied {
return ErrForbidden
} else if rule.Scope == ScopePublic && rule.Access == AccessGranted {
return nil
}
@@ -63,22 +61,22 @@ func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
}
// this rule applies to any account
if rule.Scope == auth.ScopeAccount && rule.Access == auth.AccessDenied {
return auth.ErrForbidden
} else if rule.Scope == auth.ScopeAccount && rule.Access == auth.AccessGranted {
if rule.Scope == ScopeAccount && rule.Access == AccessDenied {
return ErrForbidden
} else if rule.Scope == ScopeAccount && rule.Access == AccessGranted {
return nil
}
// if the account has the necessary scope
if include(acc.Scopes, rule.Scope) && rule.Access == auth.AccessDenied {
return auth.ErrForbidden
} else if include(acc.Scopes, rule.Scope) && rule.Access == auth.AccessGranted {
if include(acc.Scopes, rule.Scope) && rule.Access == AccessDenied {
return ErrForbidden
} else if include(acc.Scopes, rule.Scope) && rule.Access == AccessGranted {
return nil
}
}
// if no rules matched then return forbidden
return auth.ErrForbidden
return ErrForbidden
}
// include is a helper function which checks to see if the slice contains the value. includes is

View File

@@ -1,25 +1,23 @@
package rules
package auth
import (
"testing"
"github.com/micro/go-micro/v2/auth"
)
func TestVerify(t *testing.T) {
srvResource := &auth.Resource{
srvResource := &Resource{
Type: "service",
Name: "go.micro.service.foo",
Endpoint: "Foo.Bar",
}
webResource := &auth.Resource{
webResource := &Resource{
Type: "service",
Name: "go.micro.web.foo",
Endpoint: "/foo/bar",
}
catchallResource := &auth.Resource{
catchallResource := &Resource{
Type: "*",
Name: "*",
Endpoint: "*",
@@ -27,24 +25,24 @@ func TestVerify(t *testing.T) {
tt := []struct {
Name string
Rules []*auth.Rule
Account *auth.Account
Resource *auth.Resource
Rules []*Rule
Account *Account
Resource *Resource
Error error
}{
{
Name: "NoRules",
Rules: []*auth.Rule{},
Rules: []*Rule{},
Account: nil,
Resource: srvResource,
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "CatchallPublicAccount",
Account: &auth.Account{},
Account: &Account{},
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "",
Resource: catchallResource,
},
@@ -53,8 +51,8 @@ func TestVerify(t *testing.T) {
{
Name: "CatchallPublicNoAccount",
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "",
Resource: catchallResource,
},
@@ -62,10 +60,10 @@ func TestVerify(t *testing.T) {
},
{
Name: "CatchallPrivateAccount",
Account: &auth.Account{},
Account: &Account{},
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
},
@@ -74,22 +72,22 @@ func TestVerify(t *testing.T) {
{
Name: "CatchallPrivateNoAccount",
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "CatchallServiceRuleMatch",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: srvResource.Type,
Name: srvResource.Name,
Endpoint: "*",
@@ -100,27 +98,27 @@ func TestVerify(t *testing.T) {
{
Name: "CatchallServiceRuleNoMatch",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: srvResource.Type,
Name: "wrongname",
Endpoint: "*",
},
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "ExactRuleValidScope",
Resource: srvResource,
Account: &auth.Account{
Account: &Account{
Scopes: []string{"neededscope"},
},
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "neededscope",
Resource: srvResource,
},
@@ -129,58 +127,58 @@ func TestVerify(t *testing.T) {
{
Name: "ExactRuleInvalidScope",
Resource: srvResource,
Account: &auth.Account{
Account: &Account{
Scopes: []string{"neededscope"},
},
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "invalidscope",
Resource: srvResource,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "CatchallDenyWithAccount",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Access: AccessDenied,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "CatchallDenyWithNoAccount",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Access: AccessDenied,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "RulePriorityGrantFirst",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessGranted,
Access: AccessGranted,
Priority: 1,
},
&auth.Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Access: AccessDenied,
Priority: 0,
},
},
@@ -188,29 +186,29 @@ func TestVerify(t *testing.T) {
{
Name: "RulePriorityDenyFirst",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessGranted,
Access: AccessGranted,
Priority: 0,
},
&auth.Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Access: AccessDenied,
Priority: 1,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "WebExactEndpointValid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: webResource,
},
@@ -219,27 +217,27 @@ func TestVerify(t *testing.T) {
{
Name: "WebExactEndpointInalid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "invalidendpoint",
},
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "WebWildcardEndpoint",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "*",
@@ -250,11 +248,11 @@ func TestVerify(t *testing.T) {
{
Name: "WebWildcardPathEndpointValid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/foo/*",
@@ -265,24 +263,24 @@ func TestVerify(t *testing.T) {
{
Name: "WebWildcardPathEndpointInvalid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/bar/*",
},
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
if err := Verify(tc.Rules, tc.Account, tc.Resource); err != tc.Error {
if err := VerifyAccess(tc.Rules, tc.Account, tc.Resource); err != tc.Error {
t.Errorf("Expected %v but got %v", tc.Error, err)
}
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,279 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: auth/service/proto/auth.proto
package go_micro_auth
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
api "github.com/micro/go-micro/v2/api"
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 _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Auth service
func NewAuthEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// 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)
}
// Api Endpoints for Accounts service
func NewAccountsEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// 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)
}
// Api Endpoints for Rules service
func NewRulesEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// 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

@@ -1,127 +0,0 @@
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 access_token = 1;
string refresh_token = 2;
int64 created = 3;
int64 expiry = 4;
}
message Account {
string id = 1;
string type = 2;
map<string, string> metadata = 4;
repeated string scopes = 5;
string issuer = 6;
string secret = 7;
}
message Resource{
string name = 1;
string type = 2;
string endpoint = 3;
}
message GenerateRequest {
string id = 1;
map<string, string> metadata = 3;
repeated string scopes = 4;
string secret = 5;
string type = 6;
string provider = 7;
}
message GenerateResponse {
Account account = 1;
}
message GrantRequest {
string scope = 1;
Resource resource = 2;
}
message GrantResponse {}
message RevokeRequest {
string scope = 1;
Resource resource = 2;
}
message RevokeResponse {}
message InspectRequest {
string token = 1;
}
message InspectResponse {
Account account = 1;
}
message TokenRequest {
string id = 1;
string secret = 2;
string refresh_token = 3;
int64 token_expiry = 4;
}
message TokenResponse {
Token token = 1;
}
enum Access {
UNKNOWN = 0;
GRANTED = 1;
DENIED = 2;
}
message Rule {
string id = 1;
string scope = 2;
Resource resource = 3;
Access access = 4;
int32 priority = 5;
}
message CreateRequest {
Rule rule = 1;
}
message CreateResponse {}
message DeleteRequest {
string id = 1;
}
message DeleteResponse {}
message ListRequest {
}
message ListResponse {
repeated Rule rules = 1;
}

View File

@@ -1,228 +0,0 @@
package service
import (
"context"
"strings"
"time"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/rules"
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"
)
// svc is the service implementation of the Auth interface
type svc struct {
options auth.Options
auth pb.AuthService
rules pb.RulesService
jwt token.Provider
}
func (s *svc) String() string {
return "service"
}
func (s *svc) Init(opts ...auth.Option) {
for _, o := range opts {
o(&s.options)
}
if s.options.Client == nil {
s.options.Client = client.DefaultClient
}
s.auth = pb.NewAuthService("go.micro.auth", s.options.Client)
s.rules = pb.NewRulesService("go.micro.auth", s.options.Client)
// 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))
}
}
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,
Type: options.Type,
Secret: options.Secret,
Scopes: options.Scopes,
Metadata: options.Metadata,
Provider: options.Provider,
})
if err != nil {
return nil, err
}
return serializeAccount(rsp.Account), nil
}
// Grant access to a resource
func (s *svc) Grant(rule *auth.Rule) error {
access := pb.Access_UNKNOWN
if rule.Access == auth.AccessGranted {
access = pb.Access_GRANTED
} else if rule.Access == auth.AccessDenied {
access = pb.Access_DENIED
}
_, err := s.rules.Create(context.TODO(), &pb.CreateRequest{
Rule: &pb.Rule{
Id: rule.ID,
Scope: rule.Scope,
Priority: rule.Priority,
Access: access,
Resource: &pb.Resource{
Type: rule.Resource.Type,
Name: rule.Resource.Name,
Endpoint: rule.Resource.Endpoint,
},
},
})
return err
}
// Revoke access to a resource
func (s *svc) Revoke(rule *auth.Rule) error {
_, err := s.rules.Delete(context.TODO(), &pb.DeleteRequest{
Id: rule.ID,
})
return err
}
func (s *svc) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
var options auth.RulesOptions
for _, o := range opts {
o(&options)
}
if options.Context == nil {
options.Context = context.TODO()
}
rsp, err := s.rules.List(options.Context, &pb.ListRequest{}, client.WithCache(time.Second*30))
if err != nil {
return nil, err
}
rules := make([]*auth.Rule, len(rsp.Rules))
for i, r := range rsp.Rules {
rules[i] = serializeRule(r)
}
return rules, nil
}
// Verify an account has access to a resource
func (s *svc) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
var options auth.VerifyOptions
for _, o := range opts {
o(&options)
}
rs, err := s.Rules(auth.RulesContext(options.Context))
if err != nil {
return err
}
return rules.Verify(rs, acc, res)
}
// 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
if len(strings.Split(token, ".")) == 3 && s.jwt != nil {
return s.jwt.Inspect(token)
}
// the token is not a JWT or we do not have the keys to decode it,
// fall back to the auth service
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(opts ...auth.TokenOption) (*auth.Token, error) {
options := auth.NewTokenOptions(opts...)
rsp, err := s.auth.Token(context.Background(), &pb.TokenRequest{
Id: options.ID,
Secret: options.Secret,
RefreshToken: options.RefreshToken,
TokenExpiry: int64(options.Expiry.Seconds()),
})
if err != nil {
return nil, err
}
return serializeToken(rsp.Token), nil
}
func serializeToken(t *pb.Token) *auth.Token {
return &auth.Token{
AccessToken: t.AccessToken,
RefreshToken: t.RefreshToken,
Created: time.Unix(t.Created, 0),
Expiry: time.Unix(t.Expiry, 0),
}
}
func serializeAccount(a *pb.Account) *auth.Account {
return &auth.Account{
ID: a.Id,
Secret: a.Secret,
Issuer: a.Issuer,
Metadata: a.Metadata,
Scopes: a.Scopes,
}
}
func serializeRule(r *pb.Rule) *auth.Rule {
var access auth.Access
if r.Access == pb.Access_GRANTED {
access = auth.AccessGranted
} else {
access = auth.AccessDenied
}
return &auth.Rule{
ID: r.Id,
Scope: r.Scope,
Access: access,
Priority: r.Priority,
Resource: &auth.Resource{
Type: r.Resource.Type,
Name: r.Resource.Name,
Endpoint: r.Resource.Endpoint,
},
}
}
// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
options := auth.NewOptions(opts...)
if options.Client == nil {
options.Client = client.DefaultClient
}
return &svc{
auth: pb.NewAuthService("go.micro.auth", options.Client),
rules: pb.NewRulesService("go.micro.auth", options.Client),
options: options,
}
}

View File

@@ -14,54 +14,18 @@ type Broker interface {
}
// Handler is used to process messages via a subscription of a topic.
// The handler is passed a publication interface which contains the
// message and optional Ack method to acknowledge receipt of the message.
type Handler func(Event) error
type Handler func(*Message) error
type ErrorHandler func(*Message, error)
type Message struct {
Header map[string]string
Body []byte
}
// Event is given to a subscription handler for processing
type Event interface {
Topic() string
Message() *Message
Ack() error
Error() error
}
// Subscriber is a convenience return type for the Subscribe method
type Subscriber interface {
Options() SubscribeOptions
Topic() string
Unsubscribe() error
}
var (
DefaultBroker Broker = NewBroker()
)
func Init(opts ...Option) error {
return DefaultBroker.Init(opts...)
}
func Connect() error {
return DefaultBroker.Connect()
}
func Disconnect() error {
return DefaultBroker.Disconnect()
}
func Publish(topic string, msg *Message, opts ...PublishOption) error {
return DefaultBroker.Publish(topic, msg, opts...)
}
func Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
return DefaultBroker.Subscribe(topic, handler, opts...)
}
func String() string {
return DefaultBroker.String()
}

View File

@@ -1,711 +0,0 @@
// Package http provides a http based message broker
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/v2/codec/json"
merr "github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/cache"
maddr "github.com/micro/go-micro/v2/util/addr"
mnet "github.com/micro/go-micro/v2/util/net"
mls "github.com/micro/go-micro/v2/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
err error
}
var (
DefaultPath = "/"
DefaultAddress = "127.0.0.1:0"
serviceName = "micro.http.broker"
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(),
Registry: registry.DefaultRegistry,
}
for _, o := range opts {
o(&options)
}
// set address
addr := DefaultAddress
if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
addr = options.Addrs[0]
}
h := &httpBroker{
id: uuid.New().String(),
address: addr,
opts: options,
r: options.Registry,
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(DefaultPath, 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) Error() error {
return h.err
}
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()
//nolint:prealloc
var subscribers []*httpSubscriber
// look for subscriber
for _, sub := range h.subscribers[s.topic] {
// deregister and skip forward
if sub == s {
_ = 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["Micro-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")
//nolint:prealloc
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 {
p.err = 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 := h.opts.Registry
if reg == nil {
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 := h.opts.Registry
if reg == nil {
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["Micro-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(serviceName)
if err != nil {
h.RUnlock()
return err
}
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, DefaultPath, 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: topic + "-" + 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: serviceName,
Version: version,
Nodes: []*registry.Node{node},
}
// generate subscriber
subscriber := &httpSubscriber{
opts: options,
hb: h,
id: node.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"
}
// NewBroker returns a new http broker
func NewBroker(opts ...Option) Broker {
return newHttpBroker(opts...)
}

View File

@@ -2,10 +2,688 @@
package http
import (
"github.com/micro/go-micro/v2/broker"
"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/v3/broker"
"github.com/micro/go-micro/v3/codec/json"
merr "github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/cache"
"github.com/micro/go-micro/v3/registry/mdns"
maddr "github.com/micro/go-micro/v3/util/addr"
mnet "github.com/micro/go-micro/v3/util/net"
mls "github.com/micro/go-micro/v3/util/tls"
"golang.org/x/net/http2"
)
// HTTP Broker is a point to point async broker
type httpBroker struct {
id string
address string
opts broker.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 broker.SubscribeOptions
id string
topic string
fn broker.Handler
svc *registry.Service
hb *httpBroker
}
var (
DefaultPath = "/"
DefaultAddress = "127.0.0.1:0"
serviceName = "micro.http.broker"
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 ...broker.Option) broker.Broker {
options := broker.Options{
Codec: json.Marshaler{},
Context: context.TODO(),
Registry: mdns.NewRegistry(),
}
for _, o := range opts {
o(&options)
}
// set address
addr := DefaultAddress
if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
addr = options.Addrs[0]
}
h := &httpBroker{
id: uuid.New().String(),
address: addr,
opts: options,
r: options.Registry,
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(DefaultPath, 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 *httpSubscriber) Options() broker.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()
//nolint:prealloc
var subscribers []*httpSubscriber
// look for subscriber
for _, sub := range h.subscribers[s.topic] {
// deregister and skip forward
if sub == s {
_ = 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 msg *broker.Message
if err = h.opts.Codec.Unmarshal(b, &msg); err != nil {
errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err)
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
topic := msg.Header["Micro-Topic"]
if len(topic) == 0 {
errr := merr.InternalServerError("go.micro.broker", "Topic not found")
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
id := req.Form.Get("id")
//nolint:prealloc
var subs []broker.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(msg)
}
}
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 := h.opts.Registry
if reg == nil {
reg = mdns.NewRegistry()
}
// 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 ...broker.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 := h.opts.Registry
if reg == nil {
reg = mdns.NewRegistry()
}
// 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() broker.Options {
return h.opts
}
func (h *httpBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
// create the message first
m := &broker.Message{
Header: make(map[string]string),
Body: msg.Body,
}
for k, v := range msg.Header {
m.Header[k] = v
}
m.Header["Micro-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(serviceName)
if err != nil {
h.RUnlock()
return err
}
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, DefaultPath, 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 broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
var err error
var host, port string
options := broker.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: topic + "-" + 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: serviceName,
Version: version,
Nodes: []*registry.Node{node},
}
// generate subscriber
subscriber := &httpSubscriber{
opts: options,
hb: h,
id: node.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"
}
// NewBroker returns a new http broker
func NewBroker(opts ...broker.Option) broker.Broker {
return broker.NewBroker(opts...)
return newHttpBroker(opts...)
}

View File

@@ -1,4 +1,4 @@
package broker_test
package http
import (
"sync"
@@ -6,9 +6,9 @@ import (
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/memory"
"github.com/micro/go-micro/v3/broker"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/memory"
)
var (
@@ -61,7 +61,7 @@ func sub(be *testing.B, c int) {
be.StopTimer()
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
topic := uuid.New().String()
if err := b.Init(); err != nil {
@@ -83,9 +83,8 @@ func sub(be *testing.B, c int) {
done := make(chan bool, c)
for i := 0; i < c; i++ {
sub, err := b.Subscribe(topic, func(p broker.Event) error {
sub, err := b.Subscribe(topic, func(m *broker.Message) 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))
@@ -120,7 +119,7 @@ func sub(be *testing.B, c int) {
func pub(be *testing.B, c int) {
be.StopTimer()
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
topic := uuid.New().String()
if err := b.Init(); err != nil {
@@ -140,9 +139,8 @@ func pub(be *testing.B, c int) {
done := make(chan bool, c*4)
sub, err := b.Subscribe(topic, func(p broker.Event) error {
sub, err := b.Subscribe(topic, func(m *broker.Message) 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))
}
@@ -189,7 +187,7 @@ func pub(be *testing.B, c int) {
func TestBroker(t *testing.T) {
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
@@ -208,8 +206,7 @@ func TestBroker(t *testing.T) {
done := make(chan bool)
sub, err := b.Subscribe("test", func(p broker.Event) error {
m := p.Message()
sub, err := b.Subscribe("test", func(m *broker.Message) error {
if string(m.Body) != string(msg.Body) {
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
@@ -236,7 +233,7 @@ func TestBroker(t *testing.T) {
func TestConcurrentSubBroker(t *testing.T) {
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
@@ -257,11 +254,9 @@ func TestConcurrentSubBroker(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
sub, err := b.Subscribe("test", func(p broker.Event) error {
sub, err := b.Subscribe("test", func(m *broker.Message) 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))
}
@@ -293,7 +288,7 @@ func TestConcurrentSubBroker(t *testing.T) {
func TestConcurrentPubBroker(t *testing.T) {
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
@@ -312,11 +307,9 @@ func TestConcurrentPubBroker(t *testing.T) {
var wg sync.WaitGroup
sub, err := b.Subscribe("test", func(p broker.Event) error {
sub, err := b.Subscribe("test", func(m *broker.Message) 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))
}

View File

@@ -4,7 +4,7 @@ import (
"context"
"net/http"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v3/broker"
)
// Handle registers the handler for the given pattern.

View File

@@ -9,10 +9,9 @@ import (
"time"
"github.com/google/uuid"
"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"
"github.com/micro/go-micro/v3/broker"
maddr "github.com/micro/go-micro/v3/util/addr"
mnet "github.com/micro/go-micro/v3/util/net"
)
type memoryBroker struct {
@@ -24,13 +23,6 @@ type memoryBroker struct {
Subscribers map[string][]*memorySubscriber
}
type memoryEvent struct {
opts broker.Options
topic string
err error
message interface{}
}
type memorySubscriber struct {
id string
topic string
@@ -103,31 +95,12 @@ func (m *memoryBroker) Publish(topic string, msg *broker.Message, opts ...broker
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: 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
if err := sub.handler(msg); err != nil {
if eh := sub.opts.ErrorHandler; eh != nil {
eh(msg, err)
}
return err
continue
}
}
@@ -180,36 +153,6 @@ func (m *memoryBroker) String() string {
return "memory"
}
func (m *memoryEvent) Topic() string {
return m.topic
}
func (m *memoryEvent) Message() *broker.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
}

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v3/broker"
)
func TestMemoryBroker(t *testing.T) {
@@ -17,7 +17,7 @@ func TestMemoryBroker(t *testing.T) {
topic := "test"
count := 10
fn := func(p broker.Event) error {
fn := func(m *broker.Message) error {
return nil
}

View File

@@ -1,17 +0,0 @@
package nats
import (
"context"
"github.com/micro/go-micro/v2/broker"
)
// setBrokerOption returns a function to setup a context with given value
func setBrokerOption(k, v interface{}) broker.Option {
return func(o *broker.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}

View File

@@ -1,318 +0,0 @@
// Package nats provides a NATS broker
package nats
import (
"context"
"errors"
"strings"
"sync"
"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"
nats "github.com/nats-io/nats.go"
)
type natsBroker struct {
sync.Once
sync.RWMutex
// 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)
}
type subscriber struct {
s *nats.Subscription
opts broker.SubscribeOptions
}
type publication struct {
t string
err error
m *broker.Message
}
func (p *publication) Topic() string {
return p.t
}
func (p *publication) Message() *broker.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() broker.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 {
if n.conn != nil && n.conn.IsConnected() {
return n.conn.ConnectedUrl()
}
if len(n.addrs) > 0 {
return n.addrs[0]
}
return ""
}
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 len(cAddrs) == 0 {
cAddrs = []string{nats.DefaultURL}
}
return cAddrs
}
func (n *natsBroker) Connect() error {
n.Lock()
defer n.Unlock()
if n.connected {
return nil
}
status := nats.CLOSED
if n.conn != nil {
status = n.conn.Status()
}
switch status {
case nats.CONNECTED, nats.RECONNECTING, nats.CONNECTING:
n.connected = true
return nil
default: // DISCONNECTED or CLOSED or DRAINING
opts := n.nopts
opts.Servers = n.addrs
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.Lock()
defer n.Unlock()
// drain the connection if specified
if n.drain {
n.conn.Drain()
n.closeCh <- nil
}
// close the client connection
n.conn.Close()
// set not connected
n.connected = false
return nil
}
func (n *natsBroker) Init(opts ...broker.Option) error {
n.setOption(opts...)
return nil
}
func (n *natsBroker) Options() broker.Options {
return n.opts
}
func (n *natsBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
n.RLock()
defer n.RUnlock()
if n.conn == nil {
return errors.New("not connected")
}
b, err := n.opts.Codec.Marshal(msg)
if err != nil {
return err
}
return n.conn.Publish(topic, b)
}
func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
n.RLock()
if n.conn == nil {
n.RUnlock()
return nil, errors.New("not connected")
}
n.RUnlock()
opt := broker.SubscribeOptions{
AutoAck: true,
Context: context.Background(),
}
for _, o := range opts {
o(&opt)
}
fn := func(msg *nats.Msg) {
var m broker.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 "nats"
}
func (n *natsBroker) setOption(opts ...broker.Option) {
for _, o := range opts {
o(&n.opts)
}
n.Once.Do(func() {
n.nopts = nats.GetDefaultOptions()
})
if nopts, ok := n.opts.Context.Value(optionsKey{}).(nats.Options); ok {
n.nopts = nopts
}
// broker.Options have higher priority than nats.Options
// only if Addrs, Secure or TLSConfig were not set through a broker.Option
// we read them from nats.Option
if len(n.opts.Addrs) == 0 {
n.opts.Addrs = n.nopts.Servers
}
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)
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
}
}
func (n *natsBroker) onClose(conn *nats.Conn) {
n.closeCh <- nil
}
func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err error) {
// There are kinds of different async error nats might callback, but we are interested
// in ErrDrainTimeout only here.
if err == nats.ErrDrainTimeout {
n.closeCh <- err
}
}
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

@@ -1,98 +0,0 @@
package nats
import (
"fmt"
"testing"
"github.com/micro/go-micro/v2/broker"
nats "github.com/nats-io/nats.go"
)
var addrTestCases = []struct {
name string
description string
addrs map[string]string // expected address : set address
}{
{
"brokerOpts",
"set broker addresses through a broker.Option in constructor",
map[string]string{
"nats://192.168.10.1:5222": "192.168.10.1:5222",
"nats://10.20.10.0:4222": "10.20.10.0:4222"},
},
{
"brokerInit",
"set broker addresses through a broker.Option in broker.Init()",
map[string]string{
"nats://192.168.10.1:5222": "192.168.10.1:5222",
"nats://10.20.10.0:4222": "10.20.10.0:4222"},
},
{
"natsOpts",
"set broker addresses through the nats.Option in constructor",
map[string]string{
"nats://192.168.10.1:5222": "192.168.10.1:5222",
"nats://10.20.10.0:4222": "10.20.10.0:4222"},
},
{
"default",
"check if default Address is set correctly",
map[string]string{
"nats://127.0.0.1:4222": "",
},
},
}
// TestInitAddrs tests issue #100. Ensures that if the addrs is set by an option in init it will be used.
func TestInitAddrs(t *testing.T) {
for _, tc := range addrTestCases {
t.Run(fmt.Sprintf("%s: %s", tc.name, tc.description), func(t *testing.T) {
var br broker.Broker
var addrs []string
for _, addr := range tc.addrs {
addrs = append(addrs, addr)
}
switch tc.name {
case "brokerOpts":
// we know that there are just two addrs in the dict
br = NewBroker(broker.Addrs(addrs[0], addrs[1]))
br.Init()
case "brokerInit":
br = NewBroker()
// we know that there are just two addrs in the dict
br.Init(broker.Addrs(addrs[0], addrs[1]))
case "natsOpts":
nopts := nats.GetDefaultOptions()
nopts.Servers = addrs
br = NewBroker(Options(nopts))
br.Init()
case "default":
br = NewBroker()
br.Init()
}
natsBroker, ok := br.(*natsBroker)
if !ok {
t.Fatal("Expected broker to be of types *natsBroker")
}
// check if the same amount of addrs we set has actually been set, default
// have only 1 address nats://127.0.0.1:4222 (current nats code) or
// nats://localhost:4222 (older code version)
if len(natsBroker.addrs) != len(tc.addrs) && tc.name != "default" {
t.Errorf("Expected Addr count = %d, Actual Addr count = %d",
len(natsBroker.addrs), len(tc.addrs))
}
for _, addr := range natsBroker.addrs {
_, ok := tc.addrs[addr]
if !ok {
t.Errorf("Expected '%s' has not been set", addr)
}
}
})
}
}

View File

@@ -1,19 +0,0 @@
package nats
import (
"github.com/micro/go-micro/v2/broker"
nats "github.com/nats-io/nats.go"
)
type optionsKey struct{}
type drainConnectionKey struct{}
// Options accepts nats.Options
func Options(opts nats.Options) broker.Option {
return setBrokerOption(optionsKey{}, opts)
}
// DrainConnection will drain subscription on close
func DrainConnection() broker.Option {
return setBrokerOption(drainConnectionKey{}, struct{}{})
}

View File

@@ -4,8 +4,8 @@ import (
"context"
"crypto/tls"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v3/codec"
"github.com/micro/go-micro/v3/registry"
)
type Options struct {
@@ -13,10 +13,6 @@ type Options struct {
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
@@ -32,9 +28,9 @@ type PublishOptions struct {
}
type SubscribeOptions struct {
// AutoAck defaults to true. When a handler returns
// with a nil error the message is acked.
AutoAck bool
// Handler executed when errors occur processing messages
ErrorHandler ErrorHandler
// Subscribers with the same queue name
// will create a shared subscription where each
// receives a subset of messages.
@@ -59,9 +55,7 @@ func PublishContext(ctx context.Context) PublishOption {
type SubscribeOption func(*SubscribeOptions)
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
opt := SubscribeOptions{
AutoAck: true,
}
opt := SubscribeOptions{}
for _, o := range opts {
o(&opt)
@@ -85,18 +79,10 @@ func Codec(c codec.Marshaler) Option {
}
}
// DisableAutoAck will disable auto acking of messages
// after they have been handled.
func DisableAutoAck() SubscribeOption {
return func(o *SubscribeOptions) {
o.AutoAck = false
}
}
// 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) {
func HandleError(h ErrorHandler) SubscribeOption {
return func(o *SubscribeOptions) {
o.ErrorHandler = h
}
}

View File

@@ -1,374 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// 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"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Empty struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) {
return fileDescriptor_df4d8f04292cf3fe, []int{0}
}
func (m *Empty) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Empty.Unmarshal(m, b)
}
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
}
func (m *Empty) XXX_Merge(src proto.Message) {
xxx_messageInfo_Empty.Merge(m, src)
}
func (m *Empty) XXX_Size() int {
return xxx_messageInfo_Empty.Size(m)
}
func (m *Empty) XXX_DiscardUnknown() {
xxx_messageInfo_Empty.DiscardUnknown(m)
}
var xxx_messageInfo_Empty proto.InternalMessageInfo
type PublishRequest struct {
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
Message *Message `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PublishRequest) Reset() { *m = PublishRequest{} }
func (m *PublishRequest) String() string { return proto.CompactTextString(m) }
func (*PublishRequest) ProtoMessage() {}
func (*PublishRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_df4d8f04292cf3fe, []int{1}
}
func (m *PublishRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PublishRequest.Unmarshal(m, b)
}
func (m *PublishRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PublishRequest.Marshal(b, m, deterministic)
}
func (m *PublishRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PublishRequest.Merge(m, src)
}
func (m *PublishRequest) XXX_Size() int {
return xxx_messageInfo_PublishRequest.Size(m)
}
func (m *PublishRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PublishRequest.DiscardUnknown(m)
}
var xxx_messageInfo_PublishRequest proto.InternalMessageInfo
func (m *PublishRequest) GetTopic() string {
if m != nil {
return m.Topic
}
return ""
}
func (m *PublishRequest) GetMessage() *Message {
if m != nil {
return m.Message
}
return nil
}
type SubscribeRequest struct {
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
Queue string `protobuf:"bytes,2,opt,name=queue,proto3" json:"queue,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} }
func (m *SubscribeRequest) String() string { return proto.CompactTextString(m) }
func (*SubscribeRequest) ProtoMessage() {}
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_df4d8f04292cf3fe, []int{2}
}
func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SubscribeRequest.Unmarshal(m, b)
}
func (m *SubscribeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SubscribeRequest.Marshal(b, m, deterministic)
}
func (m *SubscribeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SubscribeRequest.Merge(m, src)
}
func (m *SubscribeRequest) XXX_Size() int {
return xxx_messageInfo_SubscribeRequest.Size(m)
}
func (m *SubscribeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SubscribeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SubscribeRequest proto.InternalMessageInfo
func (m *SubscribeRequest) GetTopic() string {
if m != nil {
return m.Topic
}
return ""
}
func (m *SubscribeRequest) GetQueue() string {
if m != nil {
return m.Queue
}
return ""
}
type Message struct {
Header map[string]string `protobuf:"bytes,1,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) {
return fileDescriptor_df4d8f04292cf3fe, []int{3}
}
func (m *Message) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Message.Unmarshal(m, b)
}
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
}
func (m *Message) XXX_Merge(src proto.Message) {
xxx_messageInfo_Message.Merge(m, src)
}
func (m *Message) XXX_Size() int {
return xxx_messageInfo_Message.Size(m)
}
func (m *Message) XXX_DiscardUnknown() {
xxx_messageInfo_Message.DiscardUnknown(m)
}
var xxx_messageInfo_Message proto.InternalMessageInfo
func (m *Message) GetHeader() map[string]string {
if m != nil {
return m.Header
}
return nil
}
func (m *Message) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
func init() {
proto.RegisterType((*Empty)(nil), "go.micro.broker.Empty")
proto.RegisterType((*PublishRequest)(nil), "go.micro.broker.PublishRequest")
proto.RegisterType((*SubscribeRequest)(nil), "go.micro.broker.SubscribeRequest")
proto.RegisterType((*Message)(nil), "go.micro.broker.Message")
proto.RegisterMapType((map[string]string)(nil), "go.micro.broker.Message.HeaderEntry")
}
func init() { proto.RegisterFile("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,
}
// 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,185 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: broker/service/proto/broker.proto
package go_micro_broker
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
api "github.com/micro/go-micro/v2/api"
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 _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Broker service
func NewBrokerEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Broker service
type BrokerService interface {
Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error)
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error)
}
type brokerService struct {
c client.Client
name string
}
func NewBrokerService(name string, c client.Client) BrokerService {
return &brokerService{
c: c,
name: name,
}
}
func (c *brokerService) Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error) {
req := c.c.NewRequest(c.name, "Broker.Publish", in)
out := new(Empty)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *brokerService) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error) {
req := c.c.NewRequest(c.name, "Broker.Subscribe", &SubscribeRequest{})
stream, err := c.c.Stream(ctx, req, opts...)
if err != nil {
return nil, err
}
if err := stream.Send(in); err != nil {
return nil, err
}
return &brokerServiceSubscribe{stream}, nil
}
type Broker_SubscribeService interface {
Context() context.Context
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Recv() (*Message, error)
}
type brokerServiceSubscribe struct {
stream client.Stream
}
func (x *brokerServiceSubscribe) Close() error {
return x.stream.Close()
}
func (x *brokerServiceSubscribe) Context() context.Context {
return x.stream.Context()
}
func (x *brokerServiceSubscribe) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *brokerServiceSubscribe) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *brokerServiceSubscribe) Recv() (*Message, error) {
m := new(Message)
err := x.stream.Recv(m)
if err != nil {
return nil, err
}
return m, nil
}
// Server API for Broker service
type BrokerHandler interface {
Publish(context.Context, *PublishRequest, *Empty) error
Subscribe(context.Context, *SubscribeRequest, Broker_SubscribeStream) error
}
func RegisterBrokerHandler(s server.Server, hdlr BrokerHandler, opts ...server.HandlerOption) error {
type broker interface {
Publish(ctx context.Context, in *PublishRequest, out *Empty) error
Subscribe(ctx context.Context, stream server.Stream) error
}
type Broker struct {
broker
}
h := &brokerHandler{hdlr}
return s.Handle(s.NewHandler(&Broker{h}, opts...))
}
type brokerHandler struct {
BrokerHandler
}
func (h *brokerHandler) Publish(ctx context.Context, in *PublishRequest, out *Empty) error {
return h.BrokerHandler.Publish(ctx, in, out)
}
func (h *brokerHandler) Subscribe(ctx context.Context, stream server.Stream) error {
m := new(SubscribeRequest)
if err := stream.Recv(m); err != nil {
return err
}
return h.BrokerHandler.Subscribe(ctx, m, &brokerSubscribeStream{stream})
}
type Broker_SubscribeStream interface {
Context() context.Context
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*Message) error
}
type brokerSubscribeStream struct {
stream server.Stream
}
func (x *brokerSubscribeStream) Close() error {
return x.stream.Close()
}
func (x *brokerSubscribeStream) Context() context.Context {
return x.stream.Context()
}
func (x *brokerSubscribeStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *brokerSubscribeStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *brokerSubscribeStream) Send(m *Message) error {
return x.stream.Send(m)
}

View File

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

View File

@@ -1,146 +0,0 @@
// Package service provides the broker service client
package service
import (
"context"
"time"
"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 {
Addrs []string
Client pb.BrokerService
options broker.Options
}
var (
DefaultName = "go.micro.broker"
)
func (b *serviceBroker) Address() string {
return b.Addrs[0]
}
func (b *serviceBroker) Connect() error {
return nil
}
func (b *serviceBroker) Disconnect() error {
return nil
}
func (b *serviceBroker) Init(opts ...broker.Option) error {
for _, o := range opts {
o(&b.options)
}
return nil
}
func (b *serviceBroker) Options() broker.Options {
return b.options
}
func (b *serviceBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
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{
Header: msg.Header,
Body: msg.Body,
},
}, client.WithAddress(b.Addrs...))
return err
}
func (b *serviceBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
var options broker.SubscribeOptions
for _, o := range opts {
o(&options)
}
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,
}, client.WithAddress(b.Addrs...), client.WithRequestTimeout(time.Hour))
if err != nil {
return nil, err
}
sub := &serviceSub{
topic: topic,
queue: options.Queue,
handler: handler,
stream: stream,
closed: make(chan bool),
options: options,
}
go func() {
for {
select {
case <-sub.closed:
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Unsubscribed from topic %s", topic)
}
return
default:
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 {
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 {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Failed to resubscribe to topic %s: %v", topic, err)
}
time.Sleep(time.Second)
continue
}
// new stream
sub.stream = stream
}
}
}
}()
return sub, nil
}
func (b *serviceBroker) String() string {
return "service"
}
func NewBroker(opts ...broker.Option) broker.Broker {
var options broker.Options
for _, o := range opts {
o(&options)
}
addrs := options.Addrs
if len(addrs) == 0 {
addrs = []string{"127.0.0.1:8001"}
}
cli := client.DefaultClient
return &serviceBroker{
Addrs: addrs,
Client: pb.NewBrokerService(DefaultName, cli),
options: options,
}
}

View File

@@ -1,108 +0,0 @@
package service
import (
"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 {
topic string
queue string
handler broker.Handler
stream pb.Broker_SubscribeService
closed chan bool
options broker.SubscribeOptions
}
type serviceEvent struct {
topic string
err error
message *broker.Message
}
func (s *serviceEvent) Topic() string {
return s.topic
}
func (s *serviceEvent) Message() *broker.Message {
return s.message
}
func (s *serviceEvent) Ack() error {
return nil
}
func (s *serviceEvent) Error() error {
return s.err
}
func (s *serviceSub) isClosed() bool {
select {
case <-s.closed:
return true
default:
return false
}
}
func (s *serviceSub) run() error {
exit := make(chan bool)
go func() {
select {
case <-exit:
case <-s.closed:
}
// close the stream
s.stream.Close()
}()
for {
// TODO: do not fail silently
msg, err := s.stream.Recv()
if err != nil {
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)
// don't return an error if we unsubscribed
if s.isClosed() {
return nil
}
// return stream error
return err
}
p := &serviceEvent{
topic: s.topic,
message: &broker.Message{
Header: msg.Header,
Body: msg.Body,
},
}
p.err = s.handler(p)
}
}
func (s *serviceSub) Options() broker.SubscribeOptions {
return s.options
}
func (s *serviceSub) Topic() string {
return s.topic
}
func (s *serviceSub) Unsubscribe() error {
select {
case <-s.closed:
return nil
default:
close(s.closed)
}
return nil
}

32
build/build.go Normal file
View File

@@ -0,0 +1,32 @@
// Package build is for building source into a package
package build
// Build is an interface for building packages
type Build interface {
// Package builds a package
Package(name string, src *Source) (*Package, error)
// Remove removes the package
Remove(*Package) error
}
// Source is the source of a build
type Source struct {
// Path to the source if local
Path string
// Language is the language of code
Language string
// Location of the source
Repository string
}
// Package is packaged format for source
type Package struct {
// Name of the package
Name string
// Location of the package
Path string
// Type of package e.g tarball, binary, docker
Type string
// Source of the package
Source *Source
}

View File

@@ -9,17 +9,17 @@ import (
"path/filepath"
docker "github.com/fsouza/go-dockerclient"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/runtime/local/build"
"github.com/micro/go-micro/v3/build"
"github.com/micro/go-micro/v3/logger"
)
type Builder struct {
type dockerBuild struct {
Options build.Options
Client *docker.Client
}
func (d *Builder) Build(s *build.Source) (*build.Package, error) {
image := filepath.Join(s.Repository.Path, s.Repository.Name)
func (d *dockerBuild) Package(name string, s *build.Source) (*build.Package, error) {
image := name
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
@@ -28,7 +28,7 @@ func (d *Builder) Build(s *build.Source) (*build.Package, error) {
dockerFile := "Dockerfile"
// open docker file
f, err := os.Open(filepath.Join(s.Repository.Path, s.Repository.Name, dockerFile))
f, err := os.Open(filepath.Join(s.Path, dockerFile))
if err != nil {
return nil, err
}
@@ -71,12 +71,15 @@ func (d *Builder) Build(s *build.Source) (*build.Package, error) {
}, nil
}
func (d *Builder) Clean(b *build.Package) error {
image := filepath.Join(b.Path, b.Name)
return d.Client.RemoveImage(image)
func (d *dockerBuild) Remove(b *build.Package) error {
return d.Client.RemoveImage(b.Name)
}
func NewBuilder(opts ...build.Option) build.Builder {
func (d *dockerBuild) String() string {
return "docker"
}
func NewBuild(opts ...build.Option) build.Build {
options := build.Options{}
for _, o := range opts {
o(&options)
@@ -86,7 +89,7 @@ func NewBuilder(opts ...build.Option) build.Builder {
if err != nil {
logger.Fatal(err)
}
return &Builder{
return &dockerBuild{
Options: options,
Client: client,
}

View File

@@ -6,10 +6,10 @@ import (
"os/exec"
"path/filepath"
"github.com/micro/go-micro/v2/runtime/local/build"
"github.com/micro/go-micro/v3/build"
)
type Builder struct {
type goBuild struct {
Options build.Options
Cmd string
Path string
@@ -34,35 +34,40 @@ func whichGo() string {
return "go"
}
func (g *Builder) Build(s *build.Source) (*build.Package, error) {
binary := filepath.Join(g.Path, s.Repository.Name)
source := filepath.Join(s.Repository.Path, s.Repository.Name)
func (g *goBuild) Package(name string, src *build.Source) (*build.Package, error) {
binary := filepath.Join(g.Path, name)
source := src.Path
cmd := exec.Command(g.Cmd, "build", "-o", binary, source)
if err := cmd.Run(); err != nil {
return nil, err
}
return &build.Package{
Name: s.Repository.Name,
Name: name,
Path: binary,
Type: "go",
Source: s,
Type: g.String(),
Source: src,
}, nil
}
func (g *Builder) Clean(b *build.Package) error {
func (g *goBuild) Remove(b *build.Package) error {
binary := filepath.Join(b.Path, b.Name)
return os.Remove(binary)
}
func NewBuild(opts ...build.Option) build.Builder {
func (g *goBuild) String() string {
return "golang"
}
func NewBuild(opts ...build.Option) build.Build {
options := build.Options{
Path: os.TempDir(),
}
for _, o := range opts {
o(&options)
}
return &Builder{
return &goBuild{
Options: options,
Cmd: whichGo(),
Path: options.Path,

45
build/tar/tar.go Normal file
View File

@@ -0,0 +1,45 @@
// Package tar basically tarballs source code
package tar
import (
"os"
"path/filepath"
"github.com/micro/go-micro/v3/build"
)
type tarBuild struct{}
func (t *tarBuild) Package(name string, src *build.Source) (*build.Package, error) {
pkg := name + ".tar.gz"
// path to the tarball
path := filepath.Join(os.TempDir(), src.Path, pkg)
// create a temp directory
if err := os.MkdirAll(filepath.Join(os.TempDir(), src.Path), 0755); err != nil {
return nil, err
}
if err := Compress(src.Path, path); err != nil {
return nil, err
}
return &build.Package{
Name: name,
Path: path,
Type: t.String(),
Source: src,
}, nil
}
func (t *tarBuild) Remove(b *build.Package) error {
return os.Remove(b.Path)
}
func (t *tarBuild) String() string {
return "tar.gz"
}
func NewBuild(opts ...build.Option) build.Build {
return new(tarBuild)
}

92
build/tar/util.go Normal file
View File

@@ -0,0 +1,92 @@
package tar
import (
"archive/tar"
"bytes"
"compress/gzip"
"io"
"os"
"path/filepath"
"strings"
)
func Compress(source, dest string) error {
// tar + gzip
var buf bytes.Buffer
_ = compress(source, &buf)
// write the .tar.gzip
fileToWrite, err := os.OpenFile(dest, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return err
}
_, err = io.Copy(fileToWrite, &buf)
return err
}
func compress(src string, buf io.Writer) error {
// tar > gzip > buf
zr := gzip.NewWriter(buf)
tw := tar.NewWriter(zr)
// walk through every file in the folder
filepath.Walk(src, func(file string, fi os.FileInfo, err error) error {
// generate tar header
header, err := tar.FileInfoHeader(fi, file)
if err != nil {
return err
}
// must provide real name
// (see https://golang.org/src/archive/tar/common.go?#L626)
srcWithSlash := src
if !strings.HasSuffix(src, string(filepath.Separator)) {
srcWithSlash = src + string(filepath.Separator)
}
header.Name = strings.ReplaceAll(file, srcWithSlash, "")
if header.Name == src || len(strings.TrimSpace(header.Name)) == 0 {
return nil
}
// @todo This is a quick hack to speed up whole repo uploads
// https://github.com/micro/micro/pull/956
if !fi.IsDir() && !strings.HasSuffix(header.Name, ".go") &&
!strings.HasSuffix(header.Name, ".mod") &&
!strings.HasSuffix(header.Name, ".sum") {
return nil
}
// write header
if err := tw.WriteHeader(header); err != nil {
return err
}
if fi.IsDir() {
return nil
}
// if not a dir, write file content
data, err := os.Open(file)
if err != nil {
return err
}
if _, err := io.Copy(tw, data); err != nil {
return err
}
return nil
})
// produce tar
if err := tw.Close(); err != nil {
return err
}
// produce gzip
if err := zr.Close(); err != nil {
return err
}
//
return nil
}

29
cache/cache.go vendored Normal file
View File

@@ -0,0 +1,29 @@
// Package cache is a caching interface
package cache
// Cache is an interface for caching
type Cache interface {
// Initialise options
Init(...Option) error
// Get a value
Get(key string) (interface{}, error)
// Set a value
Set(key string, val interface{}) error
// Delete a value
Delete(key string) error
// Name of the implementation
String() string
}
type Options struct {
Nodes []string
}
type Option func(o *Options)
// Nodes sets the nodes for the cache
func Nodes(v ...string) Option {
return func(o *Options) {
o.Nodes = v
}
}

56
cache/memory/memory.go vendored Normal file
View File

@@ -0,0 +1,56 @@
// Package memory is an in memory cache
package memory
import (
"sync"
"github.com/micro/go-micro/v3/cache"
"github.com/micro/go-micro/v3/errors"
)
type memoryCache struct {
// TODO: use a decent caching library
sync.RWMutex
values map[string]interface{}
}
func (m *memoryCache) Init(opts ...cache.Option) error {
// TODO: implement
return nil
}
func (m *memoryCache) Get(key string) (interface{}, error) {
m.RLock()
defer m.RUnlock()
v, ok := m.values[key]
if !ok {
return nil, errors.NotFound("go.micro.cache", key+" not found")
}
return v, nil
}
func (m *memoryCache) Set(key string, val interface{}) error {
m.Lock()
m.values[key] = val
m.Unlock()
return nil
}
func (m *memoryCache) Delete(key string) error {
m.Lock()
delete(m.values, key)
m.Unlock()
return nil
}
func (m *memoryCache) String() string {
return "memory"
}
func NewCache(opts ...cache.Option) cache.Cache {
return &memoryCache{
values: make(map[string]interface{}),
}
}

View File

@@ -4,7 +4,7 @@ import (
"context"
"time"
"github.com/micro/go-micro/v2/util/backoff"
"github.com/micro/go-micro/v3/util/backoff"
)
type BackoffFunc func(ctx context.Context, req Request, attempts int) (time.Duration, error)

View File

@@ -4,6 +4,8 @@ import (
"context"
"testing"
"time"
"github.com/micro/go-micro/v3/codec"
)
func TestBackoff(t *testing.T) {
@@ -16,10 +18,13 @@ func TestBackoff(t *testing.T) {
7900 * time.Millisecond,
}
c := NewClient()
r := &testRequest{
service: "test",
method: "test",
}
for i := 0; i < 5; i++ {
d, err := exponentialBackoff(context.TODO(), c.NewRequest("test", "test", nil), i)
d, err := exponentialBackoff(context.TODO(), r, i)
if err != nil {
t.Fatal(err)
}
@@ -29,3 +34,63 @@ func TestBackoff(t *testing.T) {
}
}
}
type testRequest struct {
service string
method string
endpoint string
contentType string
codec codec.Codec
body interface{}
opts RequestOptions
}
func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...RequestOption) Request {
var opts RequestOptions
for _, o := range reqOpts {
o(&opts)
}
// set the content-type specified
if len(opts.ContentType) > 0 {
contentType = opts.ContentType
}
return &testRequest{
service: service,
method: endpoint,
endpoint: endpoint,
body: request,
contentType: contentType,
opts: opts,
}
}
func (r *testRequest) ContentType() string {
return r.contentType
}
func (r *testRequest) Service() string {
return r.service
}
func (r *testRequest) Method() string {
return r.method
}
func (r *testRequest) Endpoint() string {
return r.endpoint
}
func (r *testRequest) Body() interface{} {
return r.body
}
func (r *testRequest) Codec() codec.Writer {
return r.codec
}
func (r *testRequest) Stream() bool {
return r.opts.Stream
}

View File

@@ -1,66 +0,0 @@
package client
import (
"context"
"encoding/json"
"fmt"
"hash/fnv"
"time"
"github.com/micro/go-micro/v2/metadata"
cache "github.com/patrickmn/go-cache"
)
// NewCache returns an initialised cache.
func NewCache() *Cache {
return &Cache{
cache: cache.New(cache.NoExpiration, 30*time.Second),
}
}
// Cache for responses
type Cache struct {
cache *cache.Cache
}
// Get a response from the cache
func (c *Cache) Get(ctx context.Context, req *Request) (interface{}, bool) {
return c.cache.Get(key(ctx, req))
}
// Set a response in the cache
func (c *Cache) Set(ctx context.Context, req *Request, rsp interface{}, expiry time.Duration) {
c.cache.Set(key(ctx, req), rsp, expiry)
}
// List the key value pairs in the cache
func (c *Cache) List() map[string]string {
items := c.cache.Items()
rsp := make(map[string]string, len(items))
for k, v := range items {
bytes, _ := json.Marshal(v.Object)
rsp[k] = string(bytes)
}
return rsp
}
// key returns a hash for the context and request
func key(ctx context.Context, req *Request) string {
ns, _ := metadata.Get(ctx, "Micro-Namespace")
bytes, _ := json.Marshal(map[string]interface{}{
"namespace": ns,
"request": map[string]interface{}{
"service": (*req).Service(),
"endpoint": (*req).Endpoint(),
"method": (*req).Method(),
"body": (*req).Body(),
},
})
h := fnv.New64()
h.Write(bytes)
return fmt.Sprintf("%x", h.Sum(nil))
}

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