Compare commits
65 Commits
Author | SHA1 | Date | |
---|---|---|---|
9830cb48a9 | |||
92d7ab2105 | |||
d2935ef399 | |||
ce4c96ae0a | |||
|
14026d15be | ||
|
2df0c7643e | ||
e13c2c48fd | |||
8db55d2e55 | |||
ed61cad961 | |||
040fc4548f | |||
6189a1b980 | |||
eb2a450a7b | |||
|
d2a30a5da1 | ||
65889c66f6 | |||
dcdf133d5b | |||
8742b55305 | |||
4a64ee72f7 | |||
881d7afeea | |||
8c95448535 | |||
c1dc041d8c | |||
|
25be0ac0f0 | ||
|
86f73cac4e | ||
485eda6ce9 | |||
dbbdb24631 | |||
723ceb4f32 | |||
bac9869bb3 | |||
610427445f | |||
|
c84a66c713 | ||
00eaae717b | |||
a102e95433 | |||
39f66cc86c | |||
bbbcb22565 | |||
cb70dfa664 | |||
1f0482fbd5 | |||
a862562284 | |||
c320c23913 | |||
|
ae848ba8bb | ||
|
8e264cbb3e | ||
|
54e523ab3f | ||
09973af099 | |||
3247da3dd0 | |||
b505455f7c | |||
293949f081 | |||
8d7e442b3a | |||
|
f7b5211af3 | ||
7eb6d030dc | |||
47e75c31c7 | |||
20ff5eed22 | |||
d23ca8db73 | |||
4dd28ac720 | |||
240b6016df | |||
cf2aa827e4 | |||
5596345382 | |||
67748a2132 | |||
c2333a9f35 | |||
4ec4c277b7 | |||
a11dd00174 | |||
cc7ebedf22 | |||
e5bf1448f4 | |||
f182bba6ff | |||
1f8810599b | |||
82248eb3b0 | |||
abb9937787 | |||
fd5ed64729 | |||
6751060d05 |
1
.github/renovate.json
vendored
1
.github/renovate.json
vendored
@@ -2,6 +2,7 @@
|
|||||||
"extends": [
|
"extends": [
|
||||||
"config:base"
|
"config:base"
|
||||||
],
|
],
|
||||||
|
"postUpdateOptions": ["gomodTidy"],
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
|
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
|
||||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
- name: setup
|
- name: setup
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15
|
go-version: 1.16
|
||||||
- name: cache
|
- name: cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
|
4
.github/workflows/pr.yml
vendored
4
.github/workflows/pr.yml
vendored
@@ -11,11 +11,11 @@ jobs:
|
|||||||
- name: setup
|
- name: setup
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15
|
go-version: 1.16
|
||||||
- name: cache
|
- name: cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/go/pkg/mod
|
path: ~/go/pkg
|
||||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: ${{ runner.os }}-go-
|
restore-keys: ${{ runner.os }}-go-
|
||||||
- name: sdk checkout
|
- name: sdk checkout
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
run:
|
run:
|
||||||
deadline: 5m
|
deadline: 5m
|
||||||
modules-download-mode: readonly
|
modules-download-mode: readonly
|
||||||
skip-dirs:
|
|
||||||
- util/mdns.new
|
|
||||||
skip-files:
|
skip-files:
|
||||||
- ".*\\.pb\\.go$"
|
- ".*\\.pb\\.go$"
|
||||||
- ".*\\.pb\\.micro\\.go$"
|
- ".*\\.pb\\.micro\\.go$"
|
||||||
@@ -30,11 +28,3 @@ linters:
|
|||||||
- interfacer
|
- interfacer
|
||||||
- typecheck
|
- typecheck
|
||||||
- dupl
|
- dupl
|
||||||
output:
|
|
||||||
format: colored-line-number
|
|
||||||
# print lines of code with issue, default is true
|
|
||||||
print-issued-lines: true
|
|
||||||
# print linter name in the end of issue text, default is true
|
|
||||||
print-linter-name: true
|
|
||||||
# make issues output unique by line, default is true
|
|
||||||
uniq-by-line: true
|
|
||||||
|
41
api/api.go
41
api/api.go
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/server"
|
"github.com/unistack-org/micro/v3/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Api interface
|
||||||
type Api interface {
|
type Api interface {
|
||||||
// Initialise options
|
// Initialise options
|
||||||
Init(...Option) error
|
Init(...Option) error
|
||||||
@@ -23,28 +24,30 @@ type Api interface {
|
|||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Options holds the options
|
||||||
type Options struct{}
|
type Options struct{}
|
||||||
|
|
||||||
|
// Option func signature
|
||||||
type Option func(*Options) error
|
type Option func(*Options) error
|
||||||
|
|
||||||
// Endpoint is a mapping between an RPC method and HTTP endpoint
|
// Endpoint is a mapping between an RPC method and HTTP endpoint
|
||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
// RPC Method e.g. Greeter.Hello
|
// Name Greeter.Hello
|
||||||
Name string
|
Name string
|
||||||
// Description e.g what's this endpoint for
|
// Desciption for endpoint
|
||||||
Description string
|
Description string
|
||||||
// API Handler e.g rpc, proxy
|
// Handler e.g rpc, proxy
|
||||||
Handler string
|
Handler string
|
||||||
// HTTP Host e.g example.com
|
|
||||||
Host []string
|
|
||||||
// HTTP Methods e.g GET, POST
|
|
||||||
Method []string
|
|
||||||
// HTTP Path e.g /greeter. Expect POSIX regex
|
|
||||||
Path []string
|
|
||||||
// Body destination
|
// Body destination
|
||||||
// "*" or "" - top level message value
|
// "*" or "" - top level message value
|
||||||
// "string" - inner message value
|
// "string" - inner message value
|
||||||
Body string
|
Body string
|
||||||
|
// Host e.g example.com
|
||||||
|
Host []string
|
||||||
|
// Method e.g GET, POST
|
||||||
|
Method []string
|
||||||
|
// Path e.g /greeter. Expect POSIX regex
|
||||||
|
Path []string
|
||||||
// Stream flag
|
// Stream flag
|
||||||
Stream bool
|
Stream bool
|
||||||
}
|
}
|
||||||
@@ -53,28 +56,12 @@ type Endpoint struct {
|
|||||||
type Service struct {
|
type Service struct {
|
||||||
// Name of service
|
// Name of service
|
||||||
Name string
|
Name string
|
||||||
// The endpoint for this service
|
// Endpoint for this service
|
||||||
Endpoint *Endpoint
|
Endpoint *Endpoint
|
||||||
// Versions of this service
|
// Services that provides service
|
||||||
Services []*register.Service
|
Services []*register.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func strip(s string) string {
|
|
||||||
return strings.TrimSpace(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func slice(s string) []string {
|
|
||||||
var sl []string
|
|
||||||
|
|
||||||
for _, p := range strings.Split(s, ",") {
|
|
||||||
if str := strip(p); len(str) > 0 {
|
|
||||||
sl = append(sl, strip(p))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sl
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encodes an endpoint to endpoint metadata
|
// Encode encodes an endpoint to endpoint metadata
|
||||||
func Encode(e *Endpoint) map[string]string {
|
func Encode(e *Endpoint) map[string]string {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
func TestEncoding(t *testing.T) {
|
func TestEncoding(t *testing.T) {
|
||||||
testData := []*Endpoint{
|
testData := []*Endpoint{
|
||||||
nil,
|
nil,
|
||||||
|
@@ -1,121 +0,0 @@
|
|||||||
// Package api provides an http-rpc handler which provides the entire http request over rpc
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
goapi "github.com/unistack-org/micro/v3/api"
|
|
||||||
"github.com/unistack-org/micro/v3/api/handler"
|
|
||||||
api "github.com/unistack-org/micro/v3/api/proto"
|
|
||||||
"github.com/unistack-org/micro/v3/client"
|
|
||||||
"github.com/unistack-org/micro/v3/errors"
|
|
||||||
"github.com/unistack-org/micro/v3/util/ctx"
|
|
||||||
"github.com/unistack-org/micro/v3/util/router"
|
|
||||||
)
|
|
||||||
|
|
||||||
type apiHandler struct {
|
|
||||||
opts handler.Options
|
|
||||||
s *goapi.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
Handler = "api"
|
|
||||||
)
|
|
||||||
|
|
||||||
// API handler is the default handler which takes api.Request and returns api.Response
|
|
||||||
func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
bsize := handler.DefaultMaxRecvSize
|
|
||||||
if a.opts.MaxRecvSize > 0 {
|
|
||||||
bsize = a.opts.MaxRecvSize
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Body = http.MaxBytesReader(w, r.Body, bsize)
|
|
||||||
request, err := requestToProto(r)
|
|
||||||
if err != nil {
|
|
||||||
er := errors.InternalServerError("go.micro.api", err.Error())
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(er.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var service *goapi.Service
|
|
||||||
|
|
||||||
if a.s != nil {
|
|
||||||
// we were given the service
|
|
||||||
service = a.s
|
|
||||||
} else if a.opts.Router != nil {
|
|
||||||
// try get service from router
|
|
||||||
s, err := a.opts.Router.Route(r)
|
|
||||||
if err != nil {
|
|
||||||
er := errors.InternalServerError("go.micro.api", err.Error())
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(er.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
service = s
|
|
||||||
} else {
|
|
||||||
// we have no way of routing the request
|
|
||||||
er := errors.InternalServerError("go.micro.api", "no route found")
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(er.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// create request and response
|
|
||||||
c := a.opts.Client
|
|
||||||
req := c.NewRequest(service.Name, service.Endpoint.Name, request)
|
|
||||||
rsp := &api.Response{}
|
|
||||||
|
|
||||||
// create the context from headers
|
|
||||||
cx := ctx.FromRequest(r)
|
|
||||||
|
|
||||||
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 {
|
|
||||||
case 0:
|
|
||||||
w.WriteHeader(500)
|
|
||||||
default:
|
|
||||||
w.WriteHeader(int(ce.Code))
|
|
||||||
}
|
|
||||||
w.Write([]byte(ce.Error()))
|
|
||||||
return
|
|
||||||
} else if rsp.StatusCode == 0 {
|
|
||||||
rsp.StatusCode = http.StatusOK
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, header := range rsp.GetHeader() {
|
|
||||||
for _, val := range header.Values {
|
|
||||||
w.Header().Add(header.Key, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(w.Header().Get("Content-Type")) == 0 {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(int(rsp.StatusCode))
|
|
||||||
w.Write([]byte(rsp.Body))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *apiHandler) String() string {
|
|
||||||
return "api"
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
|
||||||
options := handler.NewOptions(opts...)
|
|
||||||
return &apiHandler{
|
|
||||||
opts: options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithService(s *goapi.Service, opts ...handler.Option) handler.Handler {
|
|
||||||
options := handler.NewOptions(opts...)
|
|
||||||
return &apiHandler{
|
|
||||||
opts: options,
|
|
||||||
s: s,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,109 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"mime"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/oxtoacart/bpool"
|
|
||||||
api "github.com/unistack-org/micro/v3/api/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// need to calculate later to specify useful defaults
|
|
||||||
bufferPool = bpool.NewSizedBufferPool(1024, 8)
|
|
||||||
)
|
|
||||||
|
|
||||||
func requestToProto(r *http.Request) (*api.Request, error) {
|
|
||||||
if err := r.ParseForm(); err != nil {
|
|
||||||
return nil, fmt.Errorf("Error parsing form: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &api.Request{
|
|
||||||
Path: r.URL.Path,
|
|
||||||
Method: r.Method,
|
|
||||||
Header: make(map[string]*api.Pair),
|
|
||||||
Get: make(map[string]*api.Pair),
|
|
||||||
Post: make(map[string]*api.Pair),
|
|
||||||
Url: r.URL.String(),
|
|
||||||
}
|
|
||||||
|
|
||||||
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
|
||||||
if err != nil {
|
|
||||||
ct = "text/plain; charset=UTF-8" //default CT is text/plain
|
|
||||||
r.Header.Set("Content-Type", ct)
|
|
||||||
}
|
|
||||||
|
|
||||||
//set the body:
|
|
||||||
if r.Body != nil {
|
|
||||||
switch ct {
|
|
||||||
case "application/x-www-form-urlencoded":
|
|
||||||
// expect form vals in Post data
|
|
||||||
default:
|
|
||||||
buf := bufferPool.Get()
|
|
||||||
defer bufferPool.Put(buf)
|
|
||||||
if _, err = buf.ReadFrom(r.Body); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Body = buf.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set X-Forwarded-For if it does not exist
|
|
||||||
if ip, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
|
|
||||||
if prior, ok := r.Header["X-Forwarded-For"]; ok {
|
|
||||||
ip = strings.Join(prior, ", ") + ", " + ip
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the header
|
|
||||||
req.Header["X-Forwarded-For"] = &api.Pair{
|
|
||||||
Key: "X-Forwarded-For",
|
|
||||||
Values: []string{ip},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Host is stripped from net/http Headers so let's add it
|
|
||||||
req.Header["Host"] = &api.Pair{
|
|
||||||
Key: "Host",
|
|
||||||
Values: []string{r.Host},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get data
|
|
||||||
for key, vals := range r.URL.Query() {
|
|
||||||
header, ok := req.Get[key]
|
|
||||||
if !ok {
|
|
||||||
header = &api.Pair{
|
|
||||||
Key: key,
|
|
||||||
}
|
|
||||||
req.Get[key] = header
|
|
||||||
}
|
|
||||||
header.Values = vals
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post data
|
|
||||||
for key, vals := range r.PostForm {
|
|
||||||
header, ok := req.Post[key]
|
|
||||||
if !ok {
|
|
||||||
header = &api.Pair{
|
|
||||||
Key: key,
|
|
||||||
}
|
|
||||||
req.Post[key] = header
|
|
||||||
}
|
|
||||||
header.Values = vals
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, vals := range r.Header {
|
|
||||||
header, ok := req.Header[key]
|
|
||||||
if !ok {
|
|
||||||
header = &api.Pair{
|
|
||||||
Key: key,
|
|
||||||
}
|
|
||||||
req.Header[key] = header
|
|
||||||
}
|
|
||||||
header.Values = vals
|
|
||||||
}
|
|
||||||
|
|
||||||
return req, nil
|
|
||||||
}
|
|
@@ -1,46 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRequestToProto(t *testing.T) {
|
|
||||||
testData := []*http.Request{
|
|
||||||
{
|
|
||||||
Method: "GET",
|
|
||||||
Header: http.Header{
|
|
||||||
"Header": []string{"test"},
|
|
||||||
},
|
|
||||||
URL: &url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "localhost",
|
|
||||||
Path: "/foo/bar",
|
|
||||||
RawQuery: "param1=value1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, d := range testData {
|
|
||||||
p, err := requestToProto(d)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if p.Path != d.URL.Path {
|
|
||||||
t.Fatalf("Expected path %s got %s", d.URL.Path, p.Path)
|
|
||||||
}
|
|
||||||
if p.Method != d.Method {
|
|
||||||
t.Fatalf("Expected method %s got %s", d.Method, p.Method)
|
|
||||||
}
|
|
||||||
for k, v := range d.Header {
|
|
||||||
if val, ok := p.Header[k]; !ok {
|
|
||||||
t.Fatalf("Expected header %s", k)
|
|
||||||
} else {
|
|
||||||
if val.Values[0] != v[0] {
|
|
||||||
t.Fatalf("Expected val %s, got %s", val.Values[0], v[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,141 +0,0 @@
|
|||||||
// Package event provides a handler which publishes an event
|
|
||||||
package event
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/oxtoacart/bpool"
|
|
||||||
"github.com/unistack-org/micro/v3/api/handler"
|
|
||||||
proto "github.com/unistack-org/micro/v3/api/proto"
|
|
||||||
"github.com/unistack-org/micro/v3/util/ctx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
bufferPool = bpool.NewSizedBufferPool(1024, 8)
|
|
||||||
)
|
|
||||||
|
|
||||||
type event struct {
|
|
||||||
opts handler.Options
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
Handler = "event"
|
|
||||||
versionRe = regexp.MustCompilePOSIX("^v[0-9]+$")
|
|
||||||
)
|
|
||||||
|
|
||||||
func eventName(parts []string) string {
|
|
||||||
return strings.Join(parts, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
func evRoute(ns, p string) (string, string) {
|
|
||||||
p = path.Clean(p)
|
|
||||||
p = strings.TrimPrefix(p, "/")
|
|
||||||
|
|
||||||
if len(p) == 0 {
|
|
||||||
return ns, "event"
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := strings.Split(p, "/")
|
|
||||||
|
|
||||||
// no path
|
|
||||||
if len(parts) == 0 {
|
|
||||||
// topic: namespace
|
|
||||||
// action: event
|
|
||||||
return strings.Trim(ns, "."), "event"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Treat /v[0-9]+ as versioning
|
|
||||||
// /v1/foo/bar => topic: v1.foo action: bar
|
|
||||||
if len(parts) >= 2 && versionRe.Match([]byte(parts[0])) {
|
|
||||||
topic := ns + "." + strings.Join(parts[:2], ".")
|
|
||||||
action := eventName(parts[1:])
|
|
||||||
return topic, action
|
|
||||||
}
|
|
||||||
|
|
||||||
// /foo => topic: ns.foo action: foo
|
|
||||||
// /foo/bar => topic: ns.foo action: bar
|
|
||||||
topic := ns + "." + strings.Join(parts[:1], ".")
|
|
||||||
action := eventName(parts[1:])
|
|
||||||
|
|
||||||
return topic, action
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
bsize := handler.DefaultMaxRecvSize
|
|
||||||
if e.opts.MaxRecvSize > 0 {
|
|
||||||
bsize = e.opts.MaxRecvSize
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Body = http.MaxBytesReader(w, r.Body, bsize)
|
|
||||||
|
|
||||||
// request to topic:event
|
|
||||||
// create event
|
|
||||||
// publish to topic
|
|
||||||
|
|
||||||
topic, action := evRoute(e.opts.Namespace, r.URL.Path)
|
|
||||||
|
|
||||||
// create event
|
|
||||||
ev := &proto.Event{
|
|
||||||
Name: action,
|
|
||||||
// TODO: dedupe event
|
|
||||||
Id: fmt.Sprintf("%s-%s-%s", topic, action, uuid.New().String()),
|
|
||||||
Header: make(map[string]*proto.Pair),
|
|
||||||
Timestamp: time.Now().Unix(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// set headers
|
|
||||||
for key, vals := range r.Header {
|
|
||||||
header, ok := ev.Header[key]
|
|
||||||
if !ok {
|
|
||||||
header = &proto.Pair{
|
|
||||||
Key: key,
|
|
||||||
}
|
|
||||||
ev.Header[key] = header
|
|
||||||
}
|
|
||||||
header.Values = vals
|
|
||||||
}
|
|
||||||
|
|
||||||
// set body
|
|
||||||
if r.Method == "GET" {
|
|
||||||
bytes, _ := json.Marshal(r.URL.Query())
|
|
||||||
ev.Data = string(bytes)
|
|
||||||
} else {
|
|
||||||
// Read body
|
|
||||||
buf := bufferPool.Get()
|
|
||||||
defer bufferPool.Put(buf)
|
|
||||||
if _, err := buf.ReadFrom(r.Body); err != nil {
|
|
||||||
http.Error(w, err.Error(), 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ev.Data = buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// get client
|
|
||||||
c := e.opts.Client
|
|
||||||
|
|
||||||
// create publication
|
|
||||||
p := c.NewMessage(topic, ev)
|
|
||||||
|
|
||||||
// publish event
|
|
||||||
if err := c.Publish(ctx.FromRequest(r), p); err != nil {
|
|
||||||
http.Error(w, err.Error(), 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *event) String() string {
|
|
||||||
return "event"
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
|
||||||
return &event{
|
|
||||||
opts: handler.NewOptions(opts...),
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,105 +0,0 @@
|
|||||||
// Package http is a http reverse proxy handler
|
|
||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/api"
|
|
||||||
"github.com/unistack-org/micro/v3/api/handler"
|
|
||||||
"github.com/unistack-org/micro/v3/register"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
Handler = "http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type httpHandler struct {
|
|
||||||
options handler.Options
|
|
||||||
|
|
||||||
// set with different initializer
|
|
||||||
s *api.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
service, err := h.getService(r)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(service) == 0 {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rp, err := url.Parse(service)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httputil.NewSingleHostReverseProxy(rp).ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getService returns the service for this request from the selector
|
|
||||||
func (h *httpHandler) getService(r *http.Request) (string, error) {
|
|
||||||
var service *api.Service
|
|
||||||
|
|
||||||
if h.s != nil {
|
|
||||||
// we were given the service
|
|
||||||
service = h.s
|
|
||||||
} else if h.options.Router != nil {
|
|
||||||
// try get service from router
|
|
||||||
s, err := h.options.Router.Route(r)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
service = s
|
|
||||||
} else {
|
|
||||||
// we have no way of routing the request
|
|
||||||
return "", errors.New("no route found")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(service.Services) == 0 {
|
|
||||||
return "", errors.New("no route found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the nodes for this service
|
|
||||||
nodes := make([]*register.Node, 0, len(service.Services))
|
|
||||||
for _, srv := range service.Services {
|
|
||||||
nodes = append(nodes, srv.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// select a random node
|
|
||||||
node := nodes[rand.Int()%len(nodes)]
|
|
||||||
|
|
||||||
return fmt.Sprintf("http://%s", node.Address), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *httpHandler) String() string {
|
|
||||||
return "http"
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHandler returns a http proxy handler
|
|
||||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
|
||||||
options := handler.NewOptions(opts...)
|
|
||||||
|
|
||||||
return &httpHandler{
|
|
||||||
options: options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithService creates a handler with a service
|
|
||||||
func WithService(s *api.Service, opts ...handler.Option) handler.Handler {
|
|
||||||
options := handler.NewOptions(opts...)
|
|
||||||
|
|
||||||
return &httpHandler{
|
|
||||||
options: options,
|
|
||||||
s: s,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,129 +0,0 @@
|
|||||||
// +build ignore
|
|
||||||
|
|
||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/api/handler"
|
|
||||||
"github.com/unistack-org/micro/v3/api/resolver"
|
|
||||||
"github.com/unistack-org/micro/v3/api/resolver/vpath"
|
|
||||||
"github.com/unistack-org/micro/v3/api/router"
|
|
||||||
regRouter "github.com/unistack-org/micro/v3/api/router/register"
|
|
||||||
"github.com/unistack-org/micro/v3/register"
|
|
||||||
"github.com/unistack-org/micro/v3/register/memory"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testHttp(t *testing.T, path, service, ns string) {
|
|
||||||
r := memory.NewRegister()
|
|
||||||
|
|
||||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
s := ®ister.Service{
|
|
||||||
Name: service,
|
|
||||||
Nodes: []*register.Node{
|
|
||||||
{
|
|
||||||
Id: service + "-1",
|
|
||||||
Address: l.Addr().String(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Register(s)
|
|
||||||
defer r.Deregister(s)
|
|
||||||
|
|
||||||
// setup the test handler
|
|
||||||
m := http.NewServeMux()
|
|
||||||
m.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Write([]byte(`you got served`))
|
|
||||||
})
|
|
||||||
|
|
||||||
// start http test serve
|
|
||||||
go http.Serve(l, m)
|
|
||||||
|
|
||||||
// create new request and writer
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest("POST", path, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialise the handler
|
|
||||||
rt := regRouter.NewRouter(
|
|
||||||
router.WithHandler("http"),
|
|
||||||
router.WithRegister(r),
|
|
||||||
router.WithResolver(vpath.NewResolver(
|
|
||||||
resolver.WithServicePrefix(ns),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
|
|
||||||
p := NewHandler(handler.WithRouter(rt))
|
|
||||||
|
|
||||||
// execute the handler
|
|
||||||
p.ServeHTTP(w, req)
|
|
||||||
|
|
||||||
if w.Code != 200 {
|
|
||||||
t.Fatalf("Expected 200 response got %d %s", w.Code, w.Body.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.Body.String() != "you got served" {
|
|
||||||
t.Fatalf("Expected body: you got served. Got: %s", w.Body.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHttpHandler(t *testing.T) {
|
|
||||||
testData := []struct {
|
|
||||||
path string
|
|
||||||
service string
|
|
||||||
namespace string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"/test/foo",
|
|
||||||
"go.micro.api.test",
|
|
||||||
"go.micro.api",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"/test/foo/baz",
|
|
||||||
"go.micro.api.test",
|
|
||||||
"go.micro.api",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"/v1/foo",
|
|
||||||
"go.micro.api.v1.foo",
|
|
||||||
"go.micro.api",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"/v1/foo/bar",
|
|
||||||
"go.micro.api.v1.foo",
|
|
||||||
"go.micro.api",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"/v2/baz",
|
|
||||||
"go.micro.api.v2.baz",
|
|
||||||
"go.micro.api",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"/v2/baz/bar",
|
|
||||||
"go.micro.api.v2.baz",
|
|
||||||
"go.micro.api",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"/v2/baz/bar",
|
|
||||||
"v2.baz",
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, d := range testData {
|
|
||||||
t.Run(d.service, func(t *testing.T) {
|
|
||||||
testHttp(t, d.path, d.service, d.namespace)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,24 +3,34 @@ package handler
|
|||||||
import (
|
import (
|
||||||
"github.com/unistack-org/micro/v3/api/router"
|
"github.com/unistack-org/micro/v3/api/router"
|
||||||
"github.com/unistack-org/micro/v3/client"
|
"github.com/unistack-org/micro/v3/client"
|
||||||
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// DefaultMaxRecvSize specifies max recv size for handler
|
||||||
DefaultMaxRecvSize int64 = 1024 * 1024 * 100 // 10Mb
|
DefaultMaxRecvSize int64 = 1024 * 1024 * 100 // 10Mb
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Options struct holds handler options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
MaxRecvSize int64
|
|
||||||
Namespace string
|
|
||||||
Router router.Router
|
Router router.Router
|
||||||
Client client.Client
|
Client client.Client
|
||||||
|
Logger logger.Logger
|
||||||
|
Namespace string
|
||||||
|
MaxRecvSize int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Option func signature
|
||||||
type Option func(o *Options)
|
type Option func(o *Options)
|
||||||
|
|
||||||
// NewOptions fills in the blanks
|
// NewOptions creates new options struct and fills it
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
var options Options
|
options := Options{
|
||||||
|
Client: client.DefaultClient,
|
||||||
|
Router: router.DefaultRouter,
|
||||||
|
Logger: logger.DefaultLogger,
|
||||||
|
MaxRecvSize: DefaultMaxRecvSize,
|
||||||
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
@@ -30,10 +40,6 @@ func NewOptions(opts ...Option) Options {
|
|||||||
WithNamespace("go.micro.api")(&options)
|
WithNamespace("go.micro.api")(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.MaxRecvSize == 0 {
|
|
||||||
options.MaxRecvSize = DefaultMaxRecvSize
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +57,7 @@ func WithRouter(r router.Router) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithClient specifies client to be used by the handler
|
||||||
func WithClient(c client.Client) Option {
|
func WithClient(c client.Client) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Client = c
|
o.Client = c
|
||||||
|
@@ -1,182 +0,0 @@
|
|||||||
// Package web contains the web handler including websocket support
|
|
||||||
package web
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/api"
|
|
||||||
"github.com/unistack-org/micro/v3/api/handler"
|
|
||||||
"github.com/unistack-org/micro/v3/register"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
Handler = "web"
|
|
||||||
)
|
|
||||||
|
|
||||||
type webHandler struct {
|
|
||||||
opts handler.Options
|
|
||||||
s *api.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wh *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
service, err := wh.getService(r)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(service) == 0 {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rp, err := url.Parse(service)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isWebSocket(r) {
|
|
||||||
wh.serveWebSocket(rp.Host, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httputil.NewSingleHostReverseProxy(rp).ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getService returns the service for this request from the selector
|
|
||||||
func (wh *webHandler) getService(r *http.Request) (string, error) {
|
|
||||||
var service *api.Service
|
|
||||||
|
|
||||||
if wh.s != nil {
|
|
||||||
// we were given the service
|
|
||||||
service = wh.s
|
|
||||||
} else if wh.opts.Router != nil {
|
|
||||||
// try get service from router
|
|
||||||
s, err := wh.opts.Router.Route(r)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
service = s
|
|
||||||
} else {
|
|
||||||
// we have no way of routing the request
|
|
||||||
return "", errors.New("no route found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the nodes
|
|
||||||
nodes := make([]*register.Node, 0, len(service.Services))
|
|
||||||
for _, srv := range service.Services {
|
|
||||||
nodes = append(nodes, srv.Nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(nodes) == 0 {
|
|
||||||
return "", errors.New("no route found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
func (wh *webHandler) serveWebSocket(host string, w http.ResponseWriter, r *http.Request) {
|
|
||||||
req := new(http.Request)
|
|
||||||
*req = *r
|
|
||||||
|
|
||||||
if len(host) == 0 {
|
|
||||||
http.Error(w, "invalid host", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// set x-forward-for
|
|
||||||
if clientIP, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
|
|
||||||
if ips, ok := req.Header["X-Forwarded-For"]; ok {
|
|
||||||
clientIP = strings.Join(ips, ", ") + ", " + clientIP
|
|
||||||
}
|
|
||||||
req.Header.Set("X-Forwarded-For", clientIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect to the backend host
|
|
||||||
conn, err := net.Dial("tcp", host)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// hijack the connection
|
|
||||||
hj, ok := w.(http.Hijacker)
|
|
||||||
if !ok {
|
|
||||||
http.Error(w, "failed to connect", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nc, _, err := hj.Hijack()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer nc.Close()
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
if err = req.Write(conn); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
errCh := make(chan error, 2)
|
|
||||||
|
|
||||||
cp := func(dst io.Writer, src io.Reader) {
|
|
||||||
_, err := io.Copy(dst, src)
|
|
||||||
errCh <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
go cp(conn, nc)
|
|
||||||
go cp(nc, conn)
|
|
||||||
|
|
||||||
<-errCh
|
|
||||||
}
|
|
||||||
|
|
||||||
func isWebSocket(r *http.Request) bool {
|
|
||||||
contains := func(key, val string) bool {
|
|
||||||
vv := strings.Split(r.Header.Get(key), ",")
|
|
||||||
for _, v := range vv {
|
|
||||||
if val == strings.ToLower(strings.TrimSpace(v)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if contains("Connection", "upgrade") && contains("Upgrade", "websocket") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wh *webHandler) String() string {
|
|
||||||
return "web"
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
|
||||||
return &webHandler{
|
|
||||||
opts: handler.NewOptions(opts...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithService(s *api.Service, opts ...handler.Option) handler.Handler {
|
|
||||||
options := handler.NewOptions(opts...)
|
|
||||||
|
|
||||||
return &webHandler{
|
|
||||||
opts: options,
|
|
||||||
s: s,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,28 +0,0 @@
|
|||||||
package proto
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (m *Message) Reset() {
|
|
||||||
*m = Message{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) String() string {
|
|
||||||
return string(m.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) Marshal() ([]byte, error) {
|
|
||||||
return m.data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) Unmarshal(data []byte) error {
|
|
||||||
m.data = data
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMessage(data []byte) *Message {
|
|
||||||
return &Message{data}
|
|
||||||
}
|
|
@@ -1,511 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.25.0
|
|
||||||
// protoc v3.6.1
|
|
||||||
// source: api/proto/api.proto
|
|
||||||
|
|
||||||
package go_api
|
|
||||||
|
|
||||||
import (
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
|
||||||
// of the legacy proto package is being used.
|
|
||||||
const _ = proto.ProtoPackageIsVersion4
|
|
||||||
|
|
||||||
type Pair struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
|
||||||
Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Pair) Reset() {
|
|
||||||
*x = Pair{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_api_proto_api_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Pair) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Pair) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Pair) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_api_proto_api_proto_msgTypes[0]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Pair.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Pair) Descriptor() ([]byte, []int) {
|
|
||||||
return file_api_proto_api_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Pair) GetKey() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Key
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Pair) GetValues() []string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Values
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A HTTP request as RPC
|
|
||||||
// Forward by the api handler
|
|
||||||
type Request struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
|
|
||||||
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
|
||||||
Header map[string]*Pair `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
|
||||||
Get map[string]*Pair `protobuf:"bytes,4,rep,name=get,proto3" json:"get,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
|
||||||
Post map[string]*Pair `protobuf:"bytes,5,rep,name=post,proto3" json:"post,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
|
||||||
Body string `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"` // raw request body; if not application/x-www-form-urlencoded
|
|
||||||
Url string `protobuf:"bytes,7,opt,name=url,proto3" json:"url,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) Reset() {
|
|
||||||
*x = Request{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_api_proto_api_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Request) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Request) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_api_proto_api_proto_msgTypes[1]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Request.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Request) Descriptor() ([]byte, []int) {
|
|
||||||
return file_api_proto_api_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) GetMethod() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Method
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) GetPath() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Path
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) GetHeader() map[string]*Pair {
|
|
||||||
if x != nil {
|
|
||||||
return x.Header
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) GetGet() map[string]*Pair {
|
|
||||||
if x != nil {
|
|
||||||
return x.Get
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) GetPost() map[string]*Pair {
|
|
||||||
if x != nil {
|
|
||||||
return x.Post
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) GetBody() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Body
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) GetUrl() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Url
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// A HTTP response as RPC
|
|
||||||
// Expected response for the api handler
|
|
||||||
type Response struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
StatusCode int32 `protobuf:"varint,1,opt,name=statusCode,proto3" json:"statusCode,omitempty"`
|
|
||||||
Header map[string]*Pair `protobuf:"bytes,2,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
|
||||||
Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Response) Reset() {
|
|
||||||
*x = Response{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_api_proto_api_proto_msgTypes[2]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Response) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Response) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Response) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_api_proto_api_proto_msgTypes[2]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Response.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Response) Descriptor() ([]byte, []int) {
|
|
||||||
return file_api_proto_api_proto_rawDescGZIP(), []int{2}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Response) GetStatusCode() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.StatusCode
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Response) GetHeader() map[string]*Pair {
|
|
||||||
if x != nil {
|
|
||||||
return x.Header
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Response) GetBody() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Body
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// A HTTP event as RPC
|
|
||||||
// Forwarded by the event handler
|
|
||||||
type Event struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
// e.g login
|
|
||||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
|
||||||
// uuid
|
|
||||||
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
|
|
||||||
// unix timestamp of event
|
|
||||||
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
|
||||||
// event headers
|
|
||||||
Header map[string]*Pair `protobuf:"bytes,4,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
|
||||||
// the event data
|
|
||||||
Data string `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Event) Reset() {
|
|
||||||
*x = Event{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_api_proto_api_proto_msgTypes[3]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Event) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Event) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Event) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_api_proto_api_proto_msgTypes[3]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Event.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Event) Descriptor() ([]byte, []int) {
|
|
||||||
return file_api_proto_api_proto_rawDescGZIP(), []int{3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Event) GetName() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Event) GetId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Id
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Event) GetTimestamp() int64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Timestamp
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Event) GetHeader() map[string]*Pair {
|
|
||||||
if x != nil {
|
|
||||||
return x.Header
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Event) GetData() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Data
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_api_proto_api_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_api_proto_api_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x13, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2e,
|
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x22, 0x30, 0x0a,
|
|
||||||
0x04, 0x50, 0x61, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
|
||||||
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22,
|
|
||||||
0xc1, 0x03, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d,
|
|
||||||
0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74,
|
|
||||||
0x68, 0x6f, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
|
|
||||||
0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x33, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65,
|
|
||||||
0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69,
|
|
||||||
0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45,
|
|
||||||
0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x03,
|
|
||||||
0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x2e, 0x61,
|
|
||||||
0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e,
|
|
||||||
0x74, 0x72, 0x79, 0x52, 0x03, 0x67, 0x65, 0x74, 0x12, 0x2d, 0x0a, 0x04, 0x70, 0x6f, 0x73, 0x74,
|
|
||||||
0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e,
|
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72,
|
|
||||||
0x79, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
|
|
||||||
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75,
|
|
||||||
0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x1a, 0x47, 0x0a,
|
|
||||||
0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
|
|
||||||
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22,
|
|
||||||
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
|
|
||||||
0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c,
|
|
||||||
0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x44, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74,
|
|
||||||
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
|
||||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69,
|
|
||||||
0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x45, 0x0a, 0x09,
|
|
||||||
0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76,
|
|
||||||
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e,
|
|
||||||
0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
|
||||||
0x02, 0x38, 0x01, 0x22, 0xbd, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
|
||||||
0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x01,
|
|
||||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65,
|
|
||||||
0x12, 0x34, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
|
|
||||||
0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
|
||||||
0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06,
|
|
||||||
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03,
|
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x47, 0x0a, 0x0b, 0x48, 0x65,
|
|
||||||
0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76,
|
|
||||||
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e,
|
|
||||||
0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
|
||||||
0x02, 0x38, 0x01, 0x22, 0xd9, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a,
|
|
||||||
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
|
|
||||||
0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
|
|
||||||
0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03,
|
|
||||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12,
|
|
||||||
0x31, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
|
||||||
0x19, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48,
|
|
||||||
0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64,
|
|
||||||
0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
|
||||||
0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x47, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
|
|
||||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e,
|
|
||||||
0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x62,
|
|
||||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_api_proto_api_proto_rawDescOnce sync.Once
|
|
||||||
file_api_proto_api_proto_rawDescData = file_api_proto_api_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_api_proto_api_proto_rawDescGZIP() []byte {
|
|
||||||
file_api_proto_api_proto_rawDescOnce.Do(func() {
|
|
||||||
file_api_proto_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_proto_api_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_api_proto_api_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_api_proto_api_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
|
|
||||||
var file_api_proto_api_proto_goTypes = []interface{}{
|
|
||||||
(*Pair)(nil), // 0: go.api.Pair
|
|
||||||
(*Request)(nil), // 1: go.api.Request
|
|
||||||
(*Response)(nil), // 2: go.api.Response
|
|
||||||
(*Event)(nil), // 3: go.api.Event
|
|
||||||
nil, // 4: go.api.Request.HeaderEntry
|
|
||||||
nil, // 5: go.api.Request.GetEntry
|
|
||||||
nil, // 6: go.api.Request.PostEntry
|
|
||||||
nil, // 7: go.api.Response.HeaderEntry
|
|
||||||
nil, // 8: go.api.Event.HeaderEntry
|
|
||||||
}
|
|
||||||
var file_api_proto_api_proto_depIdxs = []int32{
|
|
||||||
4, // 0: go.api.Request.header:type_name -> go.api.Request.HeaderEntry
|
|
||||||
5, // 1: go.api.Request.get:type_name -> go.api.Request.GetEntry
|
|
||||||
6, // 2: go.api.Request.post:type_name -> go.api.Request.PostEntry
|
|
||||||
7, // 3: go.api.Response.header:type_name -> go.api.Response.HeaderEntry
|
|
||||||
8, // 4: go.api.Event.header:type_name -> go.api.Event.HeaderEntry
|
|
||||||
0, // 5: go.api.Request.HeaderEntry.value:type_name -> go.api.Pair
|
|
||||||
0, // 6: go.api.Request.GetEntry.value:type_name -> go.api.Pair
|
|
||||||
0, // 7: go.api.Request.PostEntry.value:type_name -> go.api.Pair
|
|
||||||
0, // 8: go.api.Response.HeaderEntry.value:type_name -> go.api.Pair
|
|
||||||
0, // 9: go.api.Event.HeaderEntry.value:type_name -> go.api.Pair
|
|
||||||
10, // [10:10] is the sub-list for method output_type
|
|
||||||
10, // [10:10] is the sub-list for method input_type
|
|
||||||
10, // [10:10] is the sub-list for extension type_name
|
|
||||||
10, // [10:10] is the sub-list for extension extendee
|
|
||||||
0, // [0:10] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_api_proto_api_proto_init() }
|
|
||||||
func file_api_proto_api_proto_init() {
|
|
||||||
if File_api_proto_api_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !protoimpl.UnsafeEnabled {
|
|
||||||
file_api_proto_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Pair); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_api_proto_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Request); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_api_proto_api_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Response); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_api_proto_api_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Event); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_api_proto_api_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 9,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 0,
|
|
||||||
},
|
|
||||||
GoTypes: file_api_proto_api_proto_goTypes,
|
|
||||||
DependencyIndexes: file_api_proto_api_proto_depIdxs,
|
|
||||||
MessageInfos: file_api_proto_api_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_api_proto_api_proto = out.File
|
|
||||||
file_api_proto_api_proto_rawDesc = nil
|
|
||||||
file_api_proto_api_proto_goTypes = nil
|
|
||||||
file_api_proto_api_proto_depIdxs = nil
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
|
||||||
// source: api/proto/api.proto
|
|
||||||
|
|
||||||
package go_api
|
|
||||||
|
|
||||||
import (
|
|
||||||
fmt "fmt"
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
math "math"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
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
|
|
@@ -1,43 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package go.api;
|
|
||||||
|
|
||||||
message Pair {
|
|
||||||
string key = 1;
|
|
||||||
repeated string values = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A HTTP request as RPC
|
|
||||||
// Forward by the api handler
|
|
||||||
message Request {
|
|
||||||
string method = 1;
|
|
||||||
string path = 2;
|
|
||||||
map<string, Pair> header = 3;
|
|
||||||
map<string, Pair> get = 4;
|
|
||||||
map<string, Pair> post = 5;
|
|
||||||
string body = 6; // raw request body; if not application/x-www-form-urlencoded
|
|
||||||
string url = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A HTTP response as RPC
|
|
||||||
// Expected response for the api handler
|
|
||||||
message Response {
|
|
||||||
int32 statusCode = 1;
|
|
||||||
map<string, Pair> header = 2;
|
|
||||||
string body = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A HTTP event as RPC
|
|
||||||
// Forwarded by the event handler
|
|
||||||
message Event {
|
|
||||||
// e.g login
|
|
||||||
string name = 1;
|
|
||||||
// uuid
|
|
||||||
string id = 2;
|
|
||||||
// unix timestamp of event
|
|
||||||
int64 timestamp = 3;
|
|
||||||
// event headers
|
|
||||||
map<string, Pair> header = 4;
|
|
||||||
// the event data
|
|
||||||
string data = 5;
|
|
||||||
}
|
|
@@ -7,11 +7,12 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/api/resolver"
|
"github.com/unistack-org/micro/v3/api/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Resolver struct {
|
type hostResolver struct {
|
||||||
opts resolver.Options
|
opts resolver.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
// Resolve endpoint
|
||||||
|
func (r *hostResolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
||||||
// parse options
|
// parse options
|
||||||
options := resolver.NewResolveOptions(opts...)
|
options := resolver.NewResolveOptions(opts...)
|
||||||
|
|
||||||
@@ -24,10 +25,11 @@ func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) String() string {
|
func (r *hostResolver) String() string {
|
||||||
return "host"
|
return "host"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewResolver creates new host api resolver
|
||||||
func NewResolver(opts ...resolver.Option) resolver.Resolver {
|
func NewResolver(opts ...resolver.Option) resolver.Resolver {
|
||||||
return &Resolver{opts: resolver.NewOptions(opts...)}
|
return &hostResolver{opts: resolver.NewOptions(opts...)}
|
||||||
}
|
}
|
||||||
|
@@ -8,9 +8,12 @@ import (
|
|||||||
|
|
||||||
// Options struct
|
// Options struct
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Handler string
|
// Context is for external defined options
|
||||||
|
Context context.Context
|
||||||
|
// Handler name
|
||||||
|
Handler string
|
||||||
|
// ServicePrefix is the prefix
|
||||||
ServicePrefix string
|
ServicePrefix string
|
||||||
Context context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func
|
// Option func
|
||||||
|
@@ -8,10 +8,12 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/api/resolver"
|
"github.com/unistack-org/micro/v3/api/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Resolver the path resolver
|
||||||
type Resolver struct {
|
type Resolver struct {
|
||||||
opts resolver.Options
|
opts resolver.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve resolves endpoint
|
||||||
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
||||||
// parse options
|
// parse options
|
||||||
options := resolver.NewResolveOptions(opts...)
|
options := resolver.NewResolveOptions(opts...)
|
||||||
@@ -31,10 +33,12 @@ func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String retruns the string representation
|
||||||
func (r *Resolver) String() string {
|
func (r *Resolver) String() string {
|
||||||
return "path"
|
return "path"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewResolver returns new path resolver
|
||||||
func NewResolver(opts ...resolver.Option) resolver.Resolver {
|
func NewResolver(opts ...resolver.Option) resolver.Resolver {
|
||||||
return &Resolver{opts: resolver.NewOptions(opts...)}
|
return &Resolver{opts: resolver.NewOptions(opts...)}
|
||||||
}
|
}
|
||||||
|
@@ -12,17 +12,19 @@ import (
|
|||||||
"golang.org/x/net/publicsuffix"
|
"golang.org/x/net/publicsuffix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewResolver creates new subdomain api resolver
|
||||||
func NewResolver(parent resolver.Resolver, opts ...resolver.Option) resolver.Resolver {
|
func NewResolver(parent resolver.Resolver, opts ...resolver.Option) resolver.Resolver {
|
||||||
options := resolver.NewOptions(opts...)
|
options := resolver.NewOptions(opts...)
|
||||||
return &Resolver{options, parent}
|
return &subdomainResolver{options, parent}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Resolver struct {
|
type subdomainResolver struct {
|
||||||
opts resolver.Options
|
opts resolver.Options
|
||||||
resolver.Resolver
|
resolver.Resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
// Resolve resolve endpoint based on subdomain
|
||||||
|
func (r *subdomainResolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
||||||
if dom := r.Domain(req); len(dom) > 0 {
|
if dom := r.Domain(req); len(dom) > 0 {
|
||||||
opts = append(opts, resolver.Domain(dom))
|
opts = append(opts, resolver.Domain(dom))
|
||||||
}
|
}
|
||||||
@@ -30,7 +32,8 @@ func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*
|
|||||||
return r.Resolver.Resolve(req, opts...)
|
return r.Resolver.Resolve(req, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) Domain(req *http.Request) string {
|
// Domain returns domain
|
||||||
|
func (r *subdomainResolver) Domain(req *http.Request) string {
|
||||||
// determine the host, e.g. foobar.m3o.app
|
// determine the host, e.g. foobar.m3o.app
|
||||||
host := req.URL.Hostname()
|
host := req.URL.Hostname()
|
||||||
if len(host) == 0 {
|
if len(host) == 0 {
|
||||||
@@ -82,6 +85,6 @@ func (r *Resolver) Domain(req *http.Request) string {
|
|||||||
return strings.Join(comps, "-")
|
return strings.Join(comps, "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) String() string {
|
func (r *subdomainResolver) String() string {
|
||||||
return "subdomain"
|
return "subdomain"
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/api/resolver/vpath"
|
"github.com/unistack-org/micro/v3/api/resolver/vpath"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResolve(t *testing.T) {
|
func TestResolve(t *testing.T) {
|
||||||
@@ -62,9 +60,13 @@ func TestResolve(t *testing.T) {
|
|||||||
t.Run(tc.Name, func(t *testing.T) {
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
r := NewResolver(vpath.NewResolver())
|
r := NewResolver(vpath.NewResolver())
|
||||||
result, err := r.Resolve(&http.Request{URL: &url.URL{Host: tc.Host, Path: "foo/bar"}})
|
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 err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
if result != nil {
|
if result != nil {
|
||||||
assert.Equal(t, tc.Result, result.Domain, "Expected %v but got %v", tc.Result, result.Domain)
|
if tc.Result != result.Domain {
|
||||||
|
t.Fatalf("Expected %v but got %v", tc.Result, result.Domain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -10,11 +10,12 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/api/resolver"
|
"github.com/unistack-org/micro/v3/api/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewResolver creates new vpath api resolver
|
||||||
func NewResolver(opts ...resolver.Option) resolver.Resolver {
|
func NewResolver(opts ...resolver.Option) resolver.Resolver {
|
||||||
return &Resolver{opts: resolver.NewOptions(opts...)}
|
return &vpathResolver{opts: resolver.NewOptions(opts...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Resolver struct {
|
type vpathResolver struct {
|
||||||
opts resolver.Options
|
opts resolver.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +23,8 @@ var (
|
|||||||
re = regexp.MustCompile("^v[0-9]+$")
|
re = regexp.MustCompile("^v[0-9]+$")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
// Resolve endpoint
|
||||||
|
func (r *vpathResolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
||||||
if req.URL.Path == "/" {
|
if req.URL.Path == "/" {
|
||||||
return nil, errors.New("unknown name")
|
return nil, errors.New("unknown name")
|
||||||
}
|
}
|
||||||
@@ -60,12 +62,12 @@ func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) String() string {
|
func (r *vpathResolver) String() string {
|
||||||
return "path"
|
return "vpath"
|
||||||
}
|
}
|
||||||
|
|
||||||
// withPrefix transforms "foo" into "go.micro.api.foo"
|
// withPrefix transforms "foo" into "go.micro.api.foo"
|
||||||
func (r *Resolver) withPrefix(parts ...string) string {
|
func (r *vpathResolver) withPrefix(parts ...string) string {
|
||||||
p := r.opts.ServicePrefix
|
p := r.opts.ServicePrefix
|
||||||
if len(p) > 0 {
|
if len(p) > 0 {
|
||||||
parts = append([]string{p}, parts...)
|
parts = append([]string{p}, parts...)
|
||||||
|
@@ -9,16 +9,24 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/register"
|
"github.com/unistack-org/micro/v3/register"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Options holds the options for api router
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Handler string
|
// Register for service lookup
|
||||||
Register register.Register
|
Register register.Register
|
||||||
|
// Resolver to use
|
||||||
Resolver resolver.Resolver
|
Resolver resolver.Resolver
|
||||||
Logger logger.Logger
|
// Logger micro logger
|
||||||
Context context.Context
|
Logger logger.Logger
|
||||||
|
// Context is for external options
|
||||||
|
Context context.Context
|
||||||
|
// Handler name
|
||||||
|
Handler string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Option func signature
|
||||||
type Option func(o *Options)
|
type Option func(o *Options)
|
||||||
|
|
||||||
|
// NewOptions returns options struct filled by opts
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
|
@@ -7,6 +7,11 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/api"
|
"github.com/unistack-org/micro/v3/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultRouter contains default router implementation
|
||||||
|
DefaultRouter Router
|
||||||
|
)
|
||||||
|
|
||||||
// Router is used to determine an endpoint for a request
|
// Router is used to determine an endpoint for a request
|
||||||
type Router interface {
|
type Router interface {
|
||||||
// Returns options
|
// Returns options
|
||||||
@@ -23,6 +28,6 @@ type Router interface {
|
|||||||
Deregister(ep *api.Endpoint) error
|
Deregister(ep *api.Endpoint) error
|
||||||
// Route returns an api.Service route
|
// Route returns an api.Service route
|
||||||
Route(r *http.Request) (*api.Service, error)
|
Route(r *http.Request) (*api.Service, error)
|
||||||
// String represenation of router
|
// String representation of router
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
37
auth/auth.go
37
auth/auth.go
@@ -19,6 +19,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// DefaultAuth holds default auth implementation
|
||||||
DefaultAuth Auth = NewAuth()
|
DefaultAuth Auth = NewAuth()
|
||||||
// ErrInvalidToken is when the token provided is not valid
|
// ErrInvalidToken is when the token provided is not valid
|
||||||
ErrInvalidToken = errors.New("invalid token provided")
|
ErrInvalidToken = errors.New("invalid token provided")
|
||||||
@@ -29,7 +30,7 @@ var (
|
|||||||
// Auth provides authentication and authorization
|
// Auth provides authentication and authorization
|
||||||
type Auth interface {
|
type Auth interface {
|
||||||
// Init the auth
|
// Init the auth
|
||||||
Init(opts ...Option)
|
Init(opts ...Option) error
|
||||||
// Options set for auth
|
// Options set for auth
|
||||||
Options() Options
|
Options() Options
|
||||||
// Generate a new account
|
// Generate a new account
|
||||||
@@ -52,30 +53,30 @@ type Auth interface {
|
|||||||
|
|
||||||
// Account provided by an auth provider
|
// Account provided by an auth provider
|
||||||
type Account struct {
|
type Account struct {
|
||||||
// ID of the account e.g. email
|
// Metadata any other associated metadata
|
||||||
|
Metadata metadata.Metadata `json:"metadata"`
|
||||||
|
// ID of the account e.g. email or uuid
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
// Type of the account, e.g. service
|
// Type of the account, e.g. service
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
// Issuer of the account
|
// Issuer of the account
|
||||||
Issuer string `json:"issuer"`
|
Issuer string `json:"issuer"`
|
||||||
// Any other associated metadata
|
|
||||||
Metadata metadata.Metadata `json:"metadata"`
|
|
||||||
// Scopes the account has access to
|
|
||||||
Scopes []string `json:"scopes"`
|
|
||||||
// Secret for the account, e.g. the password
|
// Secret for the account, e.g. the password
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
|
// Scopes the account has access to
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Token can be short or long lived
|
// Token can be short or long lived
|
||||||
type Token struct {
|
type Token struct {
|
||||||
// The token to be used for accessing resources
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
// RefreshToken to be used to generate a new token
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
// Time of token creation
|
// Time of token creation
|
||||||
Created time.Time `json:"created"`
|
Created time.Time `json:"created"`
|
||||||
// Time of token expiry
|
// Time of token expiry
|
||||||
Expiry time.Time `json:"expiry"`
|
Expiry time.Time `json:"expiry"`
|
||||||
|
// The token to be used for accessing resources
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
// RefreshToken to be used to generate a new token
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expired returns a boolean indicating if the token needs to be refreshed
|
// Expired returns a boolean indicating if the token needs to be refreshed
|
||||||
@@ -105,17 +106,15 @@ const (
|
|||||||
|
|
||||||
// Rule is used to verify access to a resource
|
// Rule is used to verify access to a resource
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
// ID of the rule, e.g. "public"
|
// Resource that rule belongs to
|
||||||
ID string
|
|
||||||
// Scope the rule requires, a blank scope indicates open to the public and * indicates the rule
|
|
||||||
// applies to any valid account
|
|
||||||
Scope string
|
|
||||||
// Resource the rule applies to
|
|
||||||
Resource *Resource
|
Resource *Resource
|
||||||
// Access determines if the rule grants or denies access to the resource
|
// ID of the rule
|
||||||
|
ID string
|
||||||
|
// Scope of the rule
|
||||||
|
Scope string
|
||||||
|
// Access flag allow/deny
|
||||||
Access Access
|
Access Access
|
||||||
// Priority the rule should take when verifying a request, the higher the value the sooner the
|
// Priority holds the rule priority
|
||||||
// rule will be applied
|
|
||||||
Priority int32
|
Priority int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,10 +14,11 @@ func (n *noopAuth) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init the auth
|
// Init the auth
|
||||||
func (n *noopAuth) Init(opts ...Option) {
|
func (n *noopAuth) Init(opts ...Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&n.opts)
|
o(&n.opts)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options set for auth
|
// Options set for auth
|
||||||
|
@@ -24,34 +24,36 @@ func NewOptions(opts ...Option) Options {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Options struct holds auth options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// Context holds the external options
|
||||||
// Issuer of the service's account
|
Context context.Context
|
||||||
Issuer string
|
// Meter used for metrics
|
||||||
// ID is the services auth ID
|
Meter meter.Meter
|
||||||
ID string
|
// Logger used for logging
|
||||||
// Secret is used to authenticate the service
|
Logger logger.Logger
|
||||||
Secret string
|
// Tracer used for tracing
|
||||||
|
Tracer tracer.Tracer
|
||||||
|
// Store used for stre data
|
||||||
|
Store store.Store
|
||||||
// Token is the services token used to authenticate itself
|
// Token is the services token used to authenticate itself
|
||||||
Token *Token
|
Token *Token
|
||||||
// PublicKey for decoding JWTs
|
|
||||||
PublicKey string
|
|
||||||
// PrivateKey for encoding JWTs
|
|
||||||
PrivateKey string
|
|
||||||
// LoginURL is the relative url path where a user can login
|
// LoginURL is the relative url path where a user can login
|
||||||
LoginURL string
|
LoginURL string
|
||||||
// Store to back auth
|
// PrivateKey for encoding JWTs
|
||||||
Store store.Store
|
PrivateKey string
|
||||||
|
// PublicKey for decoding JWTs
|
||||||
|
PublicKey string
|
||||||
|
// Secret is used to authenticate the service
|
||||||
|
Secret string
|
||||||
|
// ID is the services auth ID
|
||||||
|
ID string
|
||||||
|
// Issuer of the service's account
|
||||||
|
Issuer string
|
||||||
|
// Name holds the auth name
|
||||||
|
Name string
|
||||||
// Addrs sets the addresses of auth
|
// Addrs sets the addresses of auth
|
||||||
Addrs []string
|
Addrs []string
|
||||||
// Logger sets the logger
|
|
||||||
Logger logger.Logger
|
|
||||||
// Meter sets tht meter
|
|
||||||
Meter meter.Meter
|
|
||||||
// Tracer
|
|
||||||
Tracer tracer.Tracer
|
|
||||||
// Context to store other options
|
|
||||||
Context context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func
|
// Option func
|
||||||
@@ -123,18 +125,12 @@ func LoginURL(url string) Option {
|
|||||||
|
|
||||||
// GenerateOptions struct
|
// GenerateOptions struct
|
||||||
type GenerateOptions struct {
|
type GenerateOptions struct {
|
||||||
// Metadata associated with the account
|
|
||||||
Metadata metadata.Metadata
|
Metadata metadata.Metadata
|
||||||
// Scopes the account has access too
|
|
||||||
Scopes []string
|
|
||||||
// Provider of the account, e.g. oauth
|
|
||||||
Provider string
|
Provider string
|
||||||
// Type of the account, e.g. user
|
Type string
|
||||||
Type string
|
Secret string
|
||||||
// Secret used to authenticate the account
|
Issuer string
|
||||||
Secret string
|
Scopes []string
|
||||||
// Issuer of the account, e.g. micro
|
|
||||||
Issuer string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateOption func
|
// GenerateOption func
|
||||||
@@ -193,16 +189,11 @@ func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
|
|||||||
|
|
||||||
// TokenOptions struct
|
// TokenOptions struct
|
||||||
type TokenOptions struct {
|
type TokenOptions struct {
|
||||||
// ID for the account
|
ID string
|
||||||
ID string
|
Secret string
|
||||||
// Secret for the account
|
|
||||||
Secret string
|
|
||||||
// RefreshToken is used to refesh a token
|
|
||||||
RefreshToken string
|
RefreshToken string
|
||||||
// Expiry is the time the token should live for
|
Issuer string
|
||||||
Expiry time.Duration
|
Expiry time.Duration
|
||||||
// Issuer of the account
|
|
||||||
Issuer string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenOption func
|
// TokenOption func
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
// VerifyAccess 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
|
// access an error will be returned. If there are no rules provided which match the resource, an error
|
||||||
// will be returned
|
// will be returned
|
||||||
|
//nolint:gocyclo
|
||||||
func VerifyAccess(rules []*Rule, acc *Account, res *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 (*)
|
// the rule is only to be applied if the type matches the resource or is catch-all (*)
|
||||||
validTypes := []string{"*", res.Type}
|
validTypes := []string{"*", res.Type}
|
||||||
|
243
broker/memory.go
Normal file
243
broker/memory.go
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
package broker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
|
maddr "github.com/unistack-org/micro/v3/util/addr"
|
||||||
|
mnet "github.com/unistack-org/micro/v3/util/net"
|
||||||
|
"github.com/unistack-org/micro/v3/util/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
type memoryBroker struct {
|
||||||
|
opts Options
|
||||||
|
Subscribers map[string][]*memorySubscriber
|
||||||
|
addr string
|
||||||
|
sync.RWMutex
|
||||||
|
connected bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type memoryEvent struct {
|
||||||
|
opts Options
|
||||||
|
err error
|
||||||
|
message interface{}
|
||||||
|
topic string
|
||||||
|
}
|
||||||
|
|
||||||
|
type memorySubscriber struct {
|
||||||
|
opts SubscribeOptions
|
||||||
|
ctx context.Context
|
||||||
|
exit chan bool
|
||||||
|
handler Handler
|
||||||
|
id string
|
||||||
|
topic string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) Options() Options {
|
||||||
|
return m.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) Address() string {
|
||||||
|
return m.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) Connect(ctx context.Context) error {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if m.connected {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// use 127.0.0.1 to avoid scan of all network interfaces
|
||||||
|
addr, err := maddr.Extract("127.0.0.1")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var rng rand.Rand
|
||||||
|
i := rng.Intn(20000)
|
||||||
|
// set addr with port
|
||||||
|
addr = mnet.HostPort(addr, 10000+i)
|
||||||
|
|
||||||
|
m.addr = addr
|
||||||
|
m.connected = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) Disconnect(ctx context.Context) error {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if !m.connected {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.connected = false
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) Init(opts ...Option) error {
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&m.opts)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error {
|
||||||
|
m.RLock()
|
||||||
|
if !m.connected {
|
||||||
|
m.RUnlock()
|
||||||
|
return errors.New("not connected")
|
||||||
|
}
|
||||||
|
|
||||||
|
subs, ok := m.Subscribers[topic]
|
||||||
|
m.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
eh := m.opts.ErrorHandler
|
||||||
|
|
||||||
|
for _, sub := range subs {
|
||||||
|
if err := sub.handler(p); err != nil {
|
||||||
|
p.err = err
|
||||||
|
if sub.opts.ErrorHandler != nil {
|
||||||
|
eh = sub.opts.ErrorHandler
|
||||||
|
}
|
||||||
|
if eh != nil {
|
||||||
|
eh(p)
|
||||||
|
} else if m.opts.Logger.V(logger.ErrorLevel) {
|
||||||
|
m.opts.Logger.Error(m.opts.Context, err.Error())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
|
||||||
|
m.RLock()
|
||||||
|
if !m.connected {
|
||||||
|
m.RUnlock()
|
||||||
|
return nil, errors.New("not connected")
|
||||||
|
}
|
||||||
|
m.RUnlock()
|
||||||
|
|
||||||
|
options := NewSubscribeOptions(opts...)
|
||||||
|
|
||||||
|
id, err := uuid.NewRandom()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sub := &memorySubscriber{
|
||||||
|
exit: make(chan bool, 1),
|
||||||
|
id: id.String(),
|
||||||
|
topic: topic,
|
||||||
|
handler: handler,
|
||||||
|
opts: options,
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Lock()
|
||||||
|
m.Subscribers[topic] = append(m.Subscribers[topic], sub)
|
||||||
|
m.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sub.exit
|
||||||
|
m.Lock()
|
||||||
|
var newSubscribers []*memorySubscriber
|
||||||
|
for _, sb := range m.Subscribers[topic] {
|
||||||
|
if sb.id == sub.id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newSubscribers = append(newSubscribers, sb)
|
||||||
|
}
|
||||||
|
m.Subscribers[topic] = newSubscribers
|
||||||
|
m.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return sub, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) String() string {
|
||||||
|
return "memory"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) Name() string {
|
||||||
|
return m.opts.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryEvent) Topic() string {
|
||||||
|
return m.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryEvent) Message() *Message {
|
||||||
|
switch v := m.message.(type) {
|
||||||
|
case *Message:
|
||||||
|
return v
|
||||||
|
case []byte:
|
||||||
|
msg := &Message{}
|
||||||
|
if err := m.opts.Codec.Unmarshal(v, msg); err != nil {
|
||||||
|
if m.opts.Logger.V(logger.ErrorLevel) {
|
||||||
|
m.opts.Logger.Error(m.opts.Context, "[memory]: failed to unmarshal: %v", 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() SubscribeOptions {
|
||||||
|
return m.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memorySubscriber) Topic() string {
|
||||||
|
return m.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memorySubscriber) Unsubscribe(ctx context.Context) error {
|
||||||
|
m.exit <- true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBroker return new memory broker
|
||||||
|
func NewBroker(opts ...Option) Broker {
|
||||||
|
return &memoryBroker{
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
Subscribers: make(map[string][]*memorySubscriber),
|
||||||
|
}
|
||||||
|
}
|
50
broker/memory_test.go
Normal file
50
broker/memory_test.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package broker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMemoryBroker(t *testing.T) {
|
||||||
|
b := NewBroker()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if err := b.Connect(ctx); err != nil {
|
||||||
|
t.Fatalf("Unexpected connect error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
topic := "test"
|
||||||
|
count := 10
|
||||||
|
|
||||||
|
fn := func(p Event) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sub, err := b.Subscribe(ctx, topic, fn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error subscribing %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
message := &Message{
|
||||||
|
Header: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"id": fmt.Sprintf("%d", i),
|
||||||
|
},
|
||||||
|
Body: []byte(`hello world`),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.Publish(ctx, topic, message); err != nil {
|
||||||
|
t.Fatalf("Unexpected error publishing %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sub.Unsubscribe(ctx); err != nil {
|
||||||
|
t.Fatalf("Unexpected error unsubscribing from %s: %v", topic, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.Disconnect(ctx); err != nil {
|
||||||
|
t.Fatalf("Unexpected connect error %v", err)
|
||||||
|
}
|
||||||
|
}
|
@@ -1,81 +0,0 @@
|
|||||||
package broker
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
type noopBroker struct {
|
|
||||||
opts Options
|
|
||||||
}
|
|
||||||
|
|
||||||
type noopSubscriber struct {
|
|
||||||
topic string
|
|
||||||
opts SubscribeOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBroker returns new noop broker
|
|
||||||
func NewBroker(opts ...Option) Broker {
|
|
||||||
return &noopBroker{opts: NewOptions(opts...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopBroker) Name() string {
|
|
||||||
return n.opts.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init initialize broker
|
|
||||||
func (n *noopBroker) Init(opts ...Option) error {
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&n.opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options returns broker Options
|
|
||||||
func (n *noopBroker) Options() Options {
|
|
||||||
return n.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
// Address returns broker address
|
|
||||||
func (n *noopBroker) Address() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect connects to broker
|
|
||||||
func (n *noopBroker) Connect(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect disconnects from broker
|
|
||||||
func (n *noopBroker) Disconnect(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish publishes message to broker
|
|
||||||
func (n *noopBroker) Publish(ctx context.Context, topic string, m *Message, opts ...PublishOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe subscribes to broker topic
|
|
||||||
func (n *noopBroker) Subscribe(ctx context.Context, topic string, h Handler, opts ...SubscribeOption) (Subscriber, error) {
|
|
||||||
options := NewSubscribeOptions(opts...)
|
|
||||||
return &noopSubscriber{topic: topic, opts: options}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String return broker string representation
|
|
||||||
func (n *noopBroker) String() string {
|
|
||||||
return "noop"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options returns subscriber options
|
|
||||||
func (n *noopSubscriber) Options() SubscribeOptions {
|
|
||||||
return n.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
// TOpic returns subscriber topic
|
|
||||||
func (n *noopSubscriber) Topic() string {
|
|
||||||
return n.topic
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe unsbscribes from broker topic
|
|
||||||
func (n *noopSubscriber) Unsubscribe(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -13,25 +13,26 @@ import (
|
|||||||
|
|
||||||
// Options struct
|
// Options struct
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// Tracer used for tracing
|
||||||
// Addrs useed by broker
|
|
||||||
Addrs []string
|
|
||||||
// ErrorHandler executed when errors occur processing messages
|
|
||||||
ErrorHandler Handler
|
|
||||||
// Codec used to marshal/unmarshal messages
|
|
||||||
Codec codec.Codec
|
|
||||||
// Logger the used logger
|
|
||||||
Logger logger.Logger
|
|
||||||
// Meter the used for metrics
|
|
||||||
Meter meter.Meter
|
|
||||||
// Tracer used for trace
|
|
||||||
Tracer tracer.Tracer
|
Tracer tracer.Tracer
|
||||||
// TLSConfig for secure communication
|
// Register can be used for clustering
|
||||||
TLSConfig *tls.Config
|
|
||||||
// Register used for clustering
|
|
||||||
Register register.Register
|
Register register.Register
|
||||||
// Context is used for non default options
|
// Codec holds the codec for marshal/unmarshal
|
||||||
|
Codec codec.Codec
|
||||||
|
// Logger used for logging
|
||||||
|
Logger logger.Logger
|
||||||
|
// Meter used for metrics
|
||||||
|
Meter meter.Meter
|
||||||
|
// Context holds external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// TLSConfig holds tls.TLSConfig options
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
// ErrorHandler used when broker can't unmarshal incoming message
|
||||||
|
ErrorHandler Handler
|
||||||
|
// Name holds the broker name
|
||||||
|
Name string
|
||||||
|
// Addrs holds the broker address
|
||||||
|
Addrs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions create new Options
|
// NewOptions create new Options
|
||||||
@@ -59,10 +60,10 @@ func Context(ctx context.Context) Option {
|
|||||||
|
|
||||||
// PublishOptions struct
|
// PublishOptions struct
|
||||||
type PublishOptions struct {
|
type PublishOptions struct {
|
||||||
// BodyOnly says that only body of the message must be published
|
// Context holds external options
|
||||||
BodyOnly bool
|
|
||||||
// Context for non default options
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// BodyOnly flag says the message contains raw body bytes
|
||||||
|
BodyOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublishOptions creates PublishOptions struct
|
// NewPublishOptions creates PublishOptions struct
|
||||||
@@ -80,22 +81,16 @@ func NewPublishOptions(opts ...PublishOption) PublishOptions {
|
|||||||
|
|
||||||
// SubscribeOptions struct
|
// SubscribeOptions struct
|
||||||
type SubscribeOptions struct {
|
type SubscribeOptions struct {
|
||||||
// AutoAck ack messages if handler returns nil err
|
// Context holds external options
|
||||||
AutoAck bool
|
|
||||||
|
|
||||||
// ErrorHandler executed when errors occur processing messages
|
|
||||||
ErrorHandler Handler
|
|
||||||
|
|
||||||
// Group for subscriber, Subscribers with the same group name
|
|
||||||
// will create a shared subscription where each
|
|
||||||
// receives a subset of messages.
|
|
||||||
Group string
|
|
||||||
|
|
||||||
// BodyOnly says that consumed only body of the message
|
|
||||||
BodyOnly bool
|
|
||||||
|
|
||||||
// Context is used for non default options
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// ErrorHandler used when broker can't unmarshal incoming message
|
||||||
|
ErrorHandler Handler
|
||||||
|
// Group holds consumer group
|
||||||
|
Group string
|
||||||
|
// AutoAck flag specifies auto ack of incoming message when no error happens
|
||||||
|
AutoAck bool
|
||||||
|
// BodyOnly flag specifies that message contains only body bytes without header
|
||||||
|
BodyOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func
|
// Option func
|
||||||
|
@@ -21,12 +21,12 @@ type Source struct {
|
|||||||
|
|
||||||
// Package is packaged format for source
|
// Package is packaged format for source
|
||||||
type Package struct {
|
type Package struct {
|
||||||
|
// Source of the package
|
||||||
|
Source *Source
|
||||||
// Name of the package
|
// Name of the package
|
||||||
Name string
|
Name string
|
||||||
// Location of the package
|
// Location of the package
|
||||||
Path string
|
Path string
|
||||||
// Type of package e.g tarball, binary, docker
|
// Type of package e.g tarball, binary, docker
|
||||||
Type string
|
Type string
|
||||||
// Source of the package
|
|
||||||
Source *Source
|
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,20 @@ import (
|
|||||||
var (
|
var (
|
||||||
// DefaultClient is the global default client
|
// DefaultClient is the global default client
|
||||||
DefaultClient Client = NewClient()
|
DefaultClient Client = NewClient()
|
||||||
|
// DefaultContentType is the default content-type if not specified
|
||||||
|
DefaultContentType = "application/json"
|
||||||
|
// DefaultBackoff is the default backoff function for retries
|
||||||
|
DefaultBackoff = exponentialBackoff
|
||||||
|
// DefaultRetry is the default check-for-retry function for retries
|
||||||
|
DefaultRetry = RetryNever
|
||||||
|
// DefaultRetries is the default number of times a request is tried
|
||||||
|
DefaultRetries = 0
|
||||||
|
// DefaultRequestTimeout is the default request timeout
|
||||||
|
DefaultRequestTimeout = time.Second * 5
|
||||||
|
// DefaultPoolSize sets the connection pool size
|
||||||
|
DefaultPoolSize = 100
|
||||||
|
// DefaultPoolTTL sets the connection pool ttl
|
||||||
|
DefaultPoolTTL = time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is the interface used to make requests to services.
|
// Client is the interface used to make requests to services.
|
||||||
@@ -19,10 +33,10 @@ var (
|
|||||||
// It also supports bidirectional streaming of requests.
|
// It also supports bidirectional streaming of requests.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
Name() string
|
Name() string
|
||||||
Init(...Option) error
|
Init(opts ...Option) error
|
||||||
Options() Options
|
Options() Options
|
||||||
NewMessage(topic string, msg interface{}, opts ...MessageOption) Message
|
NewMessage(topic string, msg interface{}, opts ...MessageOption) Message
|
||||||
NewRequest(service, endpoint string, req interface{}, reqOpts ...RequestOption) Request
|
NewRequest(service string, endpoint string, req interface{}, opts ...RequestOption) Request
|
||||||
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
|
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
|
||||||
Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
|
Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
|
||||||
Publish(ctx context.Context, msg Message, opts ...PublishOption) error
|
Publish(ctx context.Context, msg Message, opts ...PublishOption) error
|
||||||
@@ -73,9 +87,9 @@ type Stream interface {
|
|||||||
// The response read
|
// The response read
|
||||||
Response() Response
|
Response() Response
|
||||||
// Send will encode and send a request
|
// Send will encode and send a request
|
||||||
Send(interface{}) error
|
Send(msg interface{}) error
|
||||||
// Recv will decode and read a response
|
// Recv will decode and read a response
|
||||||
Recv(interface{}) error
|
Recv(msg interface{}) error
|
||||||
// Error returns the stream error
|
// Error returns the stream error
|
||||||
Error() error
|
Error() error
|
||||||
// Close closes the stream
|
// Close closes the stream
|
||||||
@@ -96,18 +110,3 @@ type MessageOption func(*MessageOptions)
|
|||||||
|
|
||||||
// RequestOption used by NewRequest
|
// RequestOption used by NewRequest
|
||||||
type RequestOption func(*RequestOptions)
|
type RequestOption func(*RequestOptions)
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultBackoff is the default backoff function for retries
|
|
||||||
DefaultBackoff = exponentialBackoff
|
|
||||||
// DefaultRetry is the default check-for-retry function for retries
|
|
||||||
DefaultRetry = RetryNever
|
|
||||||
// DefaultRetries is the default number of times a request is tried
|
|
||||||
DefaultRetries = 0
|
|
||||||
// DefaultRequestTimeout is the default request timeout
|
|
||||||
DefaultRequestTimeout = time.Second * 5
|
|
||||||
// DefaultPoolSize sets the connection pool size
|
|
||||||
DefaultPoolSize = 100
|
|
||||||
// DefaultPoolTTL sets the connection pool ttl
|
|
||||||
DefaultPoolTTL = time.Minute
|
|
||||||
)
|
|
||||||
|
@@ -20,10 +20,6 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
defaultContentType = "application/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type noopClient struct {
|
type noopClient struct {
|
||||||
opts Options
|
opts Options
|
||||||
}
|
}
|
||||||
@@ -35,18 +31,27 @@ type noopMessage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type noopRequest struct {
|
type noopRequest struct {
|
||||||
|
body interface{}
|
||||||
|
codec codec.Codec
|
||||||
service string
|
service string
|
||||||
method string
|
method string
|
||||||
endpoint string
|
endpoint string
|
||||||
contentType string
|
contentType string
|
||||||
body interface{}
|
|
||||||
codec codec.Codec
|
|
||||||
stream bool
|
stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient returns new noop client
|
// NewClient returns new noop client
|
||||||
func NewClient(opts ...Option) Client {
|
func NewClient(opts ...Option) Client {
|
||||||
return &noopClient{opts: NewOptions(opts...)}
|
nc := &noopClient{opts: NewOptions(opts...)}
|
||||||
|
// wrap in reverse
|
||||||
|
|
||||||
|
c := Client(nc)
|
||||||
|
|
||||||
|
for i := len(nc.opts.Wrappers); i > 0; i-- {
|
||||||
|
c = nc.opts.Wrappers[i-1](c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Name() string {
|
func (n *noopClient) Name() string {
|
||||||
@@ -170,7 +175,7 @@ func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...RequestOption) Request {
|
func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...RequestOption) Request {
|
||||||
return &noopRequest{}
|
return &noopRequest{service: service, endpoint: endpoint}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOption) Message {
|
func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOption) Message {
|
||||||
|
@@ -2,6 +2,7 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/broker"
|
"github.com/unistack-org/micro/v3/broker"
|
||||||
@@ -18,35 +19,42 @@ import (
|
|||||||
|
|
||||||
// Options holds client options
|
// Options holds client options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// CallOptions contains default CallOptions
|
||||||
// Used to select codec
|
|
||||||
ContentType string
|
|
||||||
// Proxy address to send requests via
|
|
||||||
Proxy string
|
|
||||||
|
|
||||||
// Plugged interfaces
|
|
||||||
Broker broker.Broker
|
|
||||||
Codecs map[string]codec.Codec
|
|
||||||
Router router.Router
|
|
||||||
Selector selector.Selector
|
|
||||||
Transport transport.Transport
|
|
||||||
Logger logger.Logger
|
|
||||||
Meter meter.Meter
|
|
||||||
// Lookup used for looking up routes
|
|
||||||
Lookup LookupFunc
|
|
||||||
|
|
||||||
// Connection Pool
|
|
||||||
PoolSize int
|
|
||||||
PoolTTL time.Duration
|
|
||||||
Tracer tracer.Tracer
|
|
||||||
// Wrapper that used client
|
|
||||||
Wrappers []Wrapper
|
|
||||||
|
|
||||||
// CallOptions that used by default
|
|
||||||
CallOptions CallOptions
|
CallOptions CallOptions
|
||||||
|
// Logger used to log messages
|
||||||
// Context is used for non default options
|
Logger logger.Logger
|
||||||
|
// Tracer used for tracing
|
||||||
|
Tracer tracer.Tracer
|
||||||
|
// Broker used to publish messages
|
||||||
|
Broker broker.Broker
|
||||||
|
// Meter used for metrics
|
||||||
|
Meter meter.Meter
|
||||||
|
// Router used to get route
|
||||||
|
Router router.Router
|
||||||
|
// Selector used to select needed address
|
||||||
|
Selector selector.Selector
|
||||||
|
// Transport used for transfer messages
|
||||||
|
Transport transport.Transport
|
||||||
|
// Context is used for external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Codecs map
|
||||||
|
Codecs map[string]codec.Codec
|
||||||
|
// Lookup func used to get destination addr
|
||||||
|
Lookup LookupFunc
|
||||||
|
// Proxy is used for proxy requests
|
||||||
|
Proxy string
|
||||||
|
// ContentType is Used to select codec
|
||||||
|
ContentType string
|
||||||
|
// Name is the client name
|
||||||
|
Name string
|
||||||
|
// Wrappers contains wrappers
|
||||||
|
Wrappers []Wrapper
|
||||||
|
// PoolSize connection pool size
|
||||||
|
PoolSize int
|
||||||
|
// PoolTTL connection pool ttl
|
||||||
|
PoolTTL time.Duration
|
||||||
|
// TLSConfig specifies tls.Config for secure connection
|
||||||
|
TLSConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCallOptions creates new call options struct
|
// NewCallOptions creates new call options struct
|
||||||
@@ -60,34 +68,34 @@ func NewCallOptions(opts ...CallOption) CallOptions {
|
|||||||
|
|
||||||
// CallOptions holds client call options
|
// CallOptions holds client call options
|
||||||
type CallOptions struct {
|
type CallOptions struct {
|
||||||
// Address of remote hosts
|
// Router used for route
|
||||||
Address []string
|
|
||||||
// Backoff func
|
|
||||||
Backoff BackoffFunc
|
|
||||||
// Transport Dial Timeout
|
|
||||||
DialTimeout time.Duration
|
|
||||||
// Number of Call attempts
|
|
||||||
Retries int
|
|
||||||
// Check if retriable func
|
|
||||||
Retry RetryFunc
|
|
||||||
// Request/Response timeout
|
|
||||||
RequestTimeout time.Duration
|
|
||||||
// Router to use for this call
|
|
||||||
Router router.Router
|
Router router.Router
|
||||||
// Selector to use for the call
|
// Selector selects addr
|
||||||
Selector selector.Selector
|
Selector selector.Selector
|
||||||
// SelectOptions to use when selecting a route
|
// Context used for deadline
|
||||||
SelectOptions []selector.SelectOption
|
|
||||||
// Stream timeout for the stream
|
|
||||||
StreamTimeout time.Duration
|
|
||||||
// Use the auth token as the authorization header
|
|
||||||
AuthToken bool
|
|
||||||
// Network to lookup the route within
|
|
||||||
Network string
|
|
||||||
// Middleware for low level call func
|
|
||||||
CallWrappers []CallWrapper
|
|
||||||
// Context is uded for non default options
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Retry func used for retries
|
||||||
|
Retry RetryFunc
|
||||||
|
// Backoff func used for backoff when retry
|
||||||
|
Backoff BackoffFunc
|
||||||
|
// Network name
|
||||||
|
Network string
|
||||||
|
// CallWrappers call wrappers
|
||||||
|
CallWrappers []CallWrapper
|
||||||
|
// SelectOptions selector options
|
||||||
|
SelectOptions []selector.SelectOption
|
||||||
|
// Address specifies static addr list
|
||||||
|
Address []string
|
||||||
|
// Retries specifies retries num
|
||||||
|
Retries int
|
||||||
|
// StreamTimeout stream timeout
|
||||||
|
StreamTimeout time.Duration
|
||||||
|
// RequestTimeout request timeout
|
||||||
|
RequestTimeout time.Duration
|
||||||
|
// DialTimeout dial timeout
|
||||||
|
DialTimeout time.Duration
|
||||||
|
// AuthToken flag
|
||||||
|
AuthToken bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context pass context to client
|
// Context pass context to client
|
||||||
@@ -108,11 +116,10 @@ func NewPublishOptions(opts ...PublishOption) PublishOptions {
|
|||||||
|
|
||||||
// PublishOptions holds publish options
|
// PublishOptions holds publish options
|
||||||
type PublishOptions struct {
|
type PublishOptions struct {
|
||||||
// Exchange is the routing exchange for the message
|
// Context used for external options
|
||||||
Exchange string
|
|
||||||
// Other options for implementations of the interface
|
|
||||||
// can be stored in a context
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Exchange topic exchange name
|
||||||
|
Exchange string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessageOptions creates message options struct
|
// NewMessageOptions creates message options struct
|
||||||
@@ -126,6 +133,7 @@ func NewMessageOptions(opts ...MessageOption) MessageOptions {
|
|||||||
|
|
||||||
// MessageOptions holds client message options
|
// MessageOptions holds client message options
|
||||||
type MessageOptions struct {
|
type MessageOptions struct {
|
||||||
|
// ContentType specify content-type of message
|
||||||
ContentType string
|
ContentType string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,34 +148,38 @@ func NewRequestOptions(opts ...RequestOption) RequestOptions {
|
|||||||
|
|
||||||
// RequestOptions holds client request options
|
// RequestOptions holds client request options
|
||||||
type RequestOptions struct {
|
type RequestOptions struct {
|
||||||
ContentType string
|
// Context used for external options
|
||||||
Stream bool
|
|
||||||
// Other options for implementations of the interface
|
|
||||||
// can be stored in a context
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// ContentType specify content-type of message
|
||||||
|
ContentType string
|
||||||
|
// Stream flag
|
||||||
|
Stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions creates new options struct
|
// NewOptions creates new options struct
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
ContentType: "application/json",
|
ContentType: DefaultContentType,
|
||||||
Codecs: make(map[string]codec.Codec),
|
Codecs: make(map[string]codec.Codec),
|
||||||
CallOptions: CallOptions{
|
CallOptions: CallOptions{
|
||||||
|
Context: context.Background(),
|
||||||
Backoff: DefaultBackoff,
|
Backoff: DefaultBackoff,
|
||||||
Retry: DefaultRetry,
|
Retry: DefaultRetry,
|
||||||
Retries: DefaultRetries,
|
Retries: DefaultRetries,
|
||||||
RequestTimeout: DefaultRequestTimeout,
|
RequestTimeout: DefaultRequestTimeout,
|
||||||
DialTimeout: transport.DefaultDialTimeout,
|
DialTimeout: transport.DefaultDialTimeout,
|
||||||
},
|
},
|
||||||
Lookup: LookupRoute,
|
Lookup: LookupRoute,
|
||||||
PoolSize: DefaultPoolSize,
|
PoolSize: DefaultPoolSize,
|
||||||
PoolTTL: DefaultPoolTTL,
|
PoolTTL: DefaultPoolTTL,
|
||||||
Selector: random.NewSelector(),
|
Selector: random.NewSelector(),
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
Broker: broker.DefaultBroker,
|
Broker: broker.DefaultBroker,
|
||||||
Meter: meter.DefaultMeter,
|
Meter: meter.DefaultMeter,
|
||||||
Tracer: tracer.DefaultTracer,
|
Tracer: tracer.DefaultTracer,
|
||||||
|
Router: router.DefaultRouter,
|
||||||
|
Transport: transport.DefaultTransport,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
@@ -212,7 +224,7 @@ func Codec(contentType string, c codec.Codec) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default content type of the client
|
// ContentType used by default if not specified
|
||||||
func ContentType(ct string) Option {
|
func ContentType(ct string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.ContentType = ct
|
o.ContentType = ct
|
||||||
@@ -270,22 +282,21 @@ func Selector(s selector.Selector) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a Wrapper to a list of options passed into the client
|
// Wrap adds a wrapper to the list of options passed into the client
|
||||||
func Wrap(w Wrapper) Option {
|
func Wrap(w Wrapper) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Wrappers = append(o.Wrappers, w)
|
o.Wrappers = append(o.Wrappers, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a Wrapper to the list of CallFunc wrappers
|
// WrapCall adds a wrapper to the list of CallFunc wrappers
|
||||||
func WrapCall(cw ...CallWrapper) Option {
|
func WrapCall(cw ...CallWrapper) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.CallOptions.CallWrappers = append(o.CallOptions.CallWrappers, cw...)
|
o.CallOptions.CallWrappers = append(o.CallOptions.CallWrappers, cw...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backoff is used to set the backoff function used
|
// Backoff is used to set the backoff function used when retrying Calls
|
||||||
// when retrying Calls
|
|
||||||
func Backoff(fn BackoffFunc) Option {
|
func Backoff(fn BackoffFunc) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.CallOptions.Backoff = fn
|
o.CallOptions.Backoff = fn
|
||||||
@@ -306,8 +317,23 @@ func Lookup(l LookupFunc) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSConfig specifies a *tls.Config
|
||||||
|
func TLSConfig(t *tls.Config) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
// set the internal tls
|
||||||
|
o.TLSConfig = t
|
||||||
|
|
||||||
|
// set the default transport if one is not
|
||||||
|
// already set. Required for Init call below.
|
||||||
|
|
||||||
|
// set the transport tls
|
||||||
|
o.Transport.Init(
|
||||||
|
transport.TLSConfig(t),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Retries sets the retry count when making the request.
|
// Retries sets the retry count when making the request.
|
||||||
// Should this be a Call Option?
|
|
||||||
func Retries(i int) Option {
|
func Retries(i int) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.CallOptions.Retries = i
|
o.CallOptions.Retries = i
|
||||||
@@ -322,7 +348,6 @@ func Retry(fn RetryFunc) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RequestTimeout is the request timeout.
|
// RequestTimeout is the request timeout.
|
||||||
// Should this be a Call Option?
|
|
||||||
func RequestTimeout(d time.Duration) Option {
|
func RequestTimeout(d time.Duration) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.CallOptions.RequestTimeout = d
|
o.CallOptions.RequestTimeout = d
|
||||||
@@ -336,15 +361,13 @@ func StreamTimeout(d time.Duration) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transport dial timeout
|
// DialTimeout sets the dial timeout
|
||||||
func DialTimeout(d time.Duration) Option {
|
func DialTimeout(d time.Duration) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.CallOptions.DialTimeout = d
|
o.CallOptions.DialTimeout = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Options
|
|
||||||
|
|
||||||
// WithExchange sets the exchange to route a message through
|
// WithExchange sets the exchange to route a message through
|
||||||
func WithExchange(e string) PublishOption {
|
func WithExchange(e string) PublishOption {
|
||||||
return func(o *PublishOptions) {
|
return func(o *PublishOptions) {
|
||||||
@@ -463,8 +486,6 @@ func WithMessageContentType(ct string) MessageOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request Options
|
|
||||||
|
|
||||||
// WithContentType specifies request content type
|
// WithContentType specifies request content type
|
||||||
func WithContentType(ct string) RequestOption {
|
func WithContentType(ct string) RequestOption {
|
||||||
return func(o *RequestOptions) {
|
return func(o *RequestOptions) {
|
||||||
|
@@ -5,13 +5,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type testRequest struct {
|
type testRequest struct {
|
||||||
service string
|
opts RequestOptions
|
||||||
|
codec codec.Codec
|
||||||
|
body interface{}
|
||||||
method string
|
method string
|
||||||
endpoint string
|
endpoint string
|
||||||
contentType string
|
contentType string
|
||||||
codec codec.Codec
|
service string
|
||||||
body interface{}
|
|
||||||
opts RequestOptions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *testRequest) ContentType() string {
|
func (r *testRequest) ContentType() string {
|
||||||
|
@@ -8,8 +8,8 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/metadata"
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Message types
|
||||||
const (
|
const (
|
||||||
// Message types
|
|
||||||
Error MessageType = iota
|
Error MessageType = iota
|
||||||
Request
|
Request
|
||||||
Response
|
Response
|
||||||
@@ -25,11 +25,12 @@ var (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultMaxMsgSize specifies how much data codec can handle
|
// DefaultMaxMsgSize specifies how much data codec can handle
|
||||||
DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb
|
DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb
|
||||||
DefaultCodec Codec = NewCodec()
|
// DefaultCodec is the global default codec
|
||||||
|
DefaultCodec Codec = NewCodec()
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageType
|
// MessageType specifies message type for codec
|
||||||
type MessageType int
|
type MessageType int
|
||||||
|
|
||||||
// Codec encodes/decodes various types of messages used within micro.
|
// Codec encodes/decodes various types of messages used within micro.
|
||||||
@@ -50,16 +51,14 @@ type Codec interface {
|
|||||||
// the communication, likely followed by the body.
|
// the communication, likely followed by the body.
|
||||||
// In the case of an error, body may be nil.
|
// In the case of an error, body may be nil.
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Id string
|
Header metadata.Metadata
|
||||||
Type MessageType
|
|
||||||
Target string
|
Target string
|
||||||
Method string
|
Method string
|
||||||
Endpoint string
|
Endpoint string
|
||||||
Error string
|
Error string
|
||||||
|
Id string
|
||||||
// The values read from the socket
|
Body []byte
|
||||||
Header metadata.Metadata
|
Type MessageType
|
||||||
Body []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessage creates new codec message
|
// NewMessage creates new codec message
|
||||||
|
6
codec/frame.go
Normal file
6
codec/frame.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package codec
|
||||||
|
|
||||||
|
// Frame gives us the ability to define raw data to send over the pipes
|
||||||
|
type Frame struct {
|
||||||
|
Data []byte
|
||||||
|
}
|
28
codec/frame.proto
Normal file
28
codec/frame.proto
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2021 Unistack LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package micro.codec;
|
||||||
|
|
||||||
|
option cc_enable_arenas = true;
|
||||||
|
option go_package = "github.com/unistack-org/micro/v3/codec;codec";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
option java_outer_classname = "MicroCodec";
|
||||||
|
option java_package = "micro.codec";
|
||||||
|
option objc_class_prefix = "MCODEC";
|
||||||
|
|
||||||
|
message Frame {
|
||||||
|
bytes data = 1;
|
||||||
|
}
|
@@ -3,24 +3,18 @@ package codec
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type noopCodec struct {
|
type noopCodec struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frame gives us the ability to define raw data to send over the pipes
|
|
||||||
type Frame struct {
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *noopCodec) ReadHeader(conn io.Reader, m *Message, t MessageType) error {
|
func (c *noopCodec) ReadHeader(conn io.Reader, m *Message, t MessageType) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error {
|
func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error {
|
||||||
// read bytes
|
// read bytes
|
||||||
buf, err := ioutil.ReadAll(conn)
|
buf, err := io.ReadAll(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -30,12 +24,8 @@ func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch v := b.(type) {
|
switch v := b.(type) {
|
||||||
case string:
|
|
||||||
v = string(buf)
|
|
||||||
case *string:
|
case *string:
|
||||||
*v = string(buf)
|
*v = string(buf)
|
||||||
case []byte:
|
|
||||||
v = buf
|
|
||||||
case *[]byte:
|
case *[]byte:
|
||||||
*v = buf
|
*v = buf
|
||||||
case *Frame:
|
case *Frame:
|
||||||
@@ -112,15 +102,9 @@ func (c *noopCodec) Unmarshal(d []byte, v interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
switch ve := v.(type) {
|
switch ve := v.(type) {
|
||||||
case string:
|
|
||||||
ve = string(d)
|
|
||||||
return nil
|
|
||||||
case *string:
|
case *string:
|
||||||
*ve = string(d)
|
*ve = string(d)
|
||||||
return nil
|
return nil
|
||||||
case []byte:
|
|
||||||
ve = d
|
|
||||||
return nil
|
|
||||||
case *[]byte:
|
case *[]byte:
|
||||||
*ve = d
|
*ve = d
|
||||||
return nil
|
return nil
|
||||||
|
34
codec/noop_test.go
Normal file
34
codec/noop_test.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNoopBytes(t *testing.T) {
|
||||||
|
req := []byte("test req")
|
||||||
|
rsp := make([]byte, len(req))
|
||||||
|
|
||||||
|
nc := NewCodec()
|
||||||
|
if err := nc.Unmarshal(req, &rsp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(req, rsp) {
|
||||||
|
t.Fatalf("req not eq rsp: %s != %s", req, rsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoopString(t *testing.T) {
|
||||||
|
req := []byte("test req")
|
||||||
|
var rsp string
|
||||||
|
|
||||||
|
nc := NewCodec()
|
||||||
|
if err := nc.Unmarshal(req, &rsp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(req, []byte(rsp)) {
|
||||||
|
t.Fatalf("req not eq rsp: %s != %s", req, rsp)
|
||||||
|
}
|
||||||
|
}
|
@@ -11,10 +11,14 @@ type Option func(*Options)
|
|||||||
|
|
||||||
// Options contains codec options
|
// Options contains codec options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
// Meter used for metrics
|
||||||
|
Meter meter.Meter
|
||||||
|
// Logger used for logging
|
||||||
|
Logger logger.Logger
|
||||||
|
// Tracer used for tracing
|
||||||
|
Tracer tracer.Tracer
|
||||||
|
// MaxMsgSize specifies max messages size that reads by codec
|
||||||
MaxMsgSize int
|
MaxMsgSize int
|
||||||
Meter meter.Meter
|
|
||||||
Logger logger.Logger
|
|
||||||
Tracer tracer.Tracer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxMsgSize sets the max message size
|
// MaxMsgSize sets the max message size
|
||||||
@@ -45,6 +49,7 @@ func Meter(m meter.Meter) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewOptions returns new options
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
type configKey struct{}
|
type configKey struct{}
|
||||||
|
|
||||||
|
// FromContext returns store from context
|
||||||
func FromContext(ctx context.Context) (Config, bool) {
|
func FromContext(ctx context.Context) (Config, bool) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
@@ -14,6 +15,7 @@ func FromContext(ctx context.Context) (Config, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewContext put store in context
|
||||||
func NewContext(ctx context.Context, c Config) context.Context {
|
func NewContext(ctx context.Context, c Config) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
|
@@ -53,6 +53,7 @@ func (c *defaultConfig) Load(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val string) error {
|
func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val string) error {
|
||||||
if !rutil.IsEmpty(value) {
|
if !rutil.IsEmpty(value) {
|
||||||
return nil
|
return nil
|
||||||
@@ -107,7 +108,7 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
value.Set(reflect.ValueOf(float64(v)))
|
value.Set(reflect.ValueOf(v))
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
v, err := strconv.ParseInt(val, 10, 0)
|
v, err := strconv.ParseInt(val, 10, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -137,7 +138,7 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
value.Set(reflect.ValueOf(int64(v)))
|
value.Set(reflect.ValueOf(v))
|
||||||
case reflect.Uint:
|
case reflect.Uint:
|
||||||
v, err := strconv.ParseUint(val, 10, 0)
|
v, err := strconv.ParseUint(val, 10, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -167,7 +168,7 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
value.Set(reflect.ValueOf(uint64(v)))
|
value.Set(reflect.ValueOf(v))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -255,6 +256,7 @@ func (c *defaultConfig) Name() string {
|
|||||||
return c.opts.Name
|
return c.opts.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewConfig returns new default config source
|
||||||
func NewConfig(opts ...Option) Config {
|
func NewConfig(opts ...Option) Config {
|
||||||
options := NewOptions(opts...)
|
options := NewOptions(opts...)
|
||||||
if len(options.StructTag) == 0 {
|
if len(options.StructTag) == 0 {
|
||||||
|
@@ -9,31 +9,40 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/tracer"
|
"github.com/unistack-org/micro/v3/tracer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Options hold the config options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// Struct holds the destination config struct
|
||||||
AllowFail bool
|
|
||||||
BeforeLoad []func(context.Context, Config) error
|
|
||||||
AfterLoad []func(context.Context, Config) error
|
|
||||||
BeforeSave []func(context.Context, Config) error
|
|
||||||
AfterSave []func(context.Context, Config) error
|
|
||||||
// Struct that holds config data
|
|
||||||
Struct interface{}
|
Struct interface{}
|
||||||
// StructTag name
|
|
||||||
StructTag string
|
|
||||||
// Logger that will be used
|
|
||||||
Logger logger.Logger
|
|
||||||
// Meter that will be used
|
|
||||||
Meter meter.Meter
|
|
||||||
// Tracer used for trace
|
|
||||||
Tracer tracer.Tracer
|
|
||||||
// Codec that used for load/save
|
// Codec that used for load/save
|
||||||
Codec codec.Codec
|
Codec codec.Codec
|
||||||
// Context for alternative data
|
// Tracer that will be used
|
||||||
|
Tracer tracer.Tracer
|
||||||
|
// Meter that will be used
|
||||||
|
Meter meter.Meter
|
||||||
|
// Logger that will be used
|
||||||
|
Logger logger.Logger
|
||||||
|
// Context used for external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Name of the config
|
||||||
|
Name string
|
||||||
|
// StructTag name
|
||||||
|
StructTag string
|
||||||
|
// BeforeSave contains slice of funcs that runs before save
|
||||||
|
BeforeSave []func(context.Context, Config) error
|
||||||
|
// AfterLoad contains slice of funcs that runs after load
|
||||||
|
AfterLoad []func(context.Context, Config) error
|
||||||
|
// BeforeLoad contains slice of funcs that runs before load
|
||||||
|
BeforeLoad []func(context.Context, Config) error
|
||||||
|
// AfterSave contains slice of funcs that runs after save
|
||||||
|
AfterSave []func(context.Context, Config) error
|
||||||
|
// AllowFail flag to allow fail in config source
|
||||||
|
AllowFail bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Option function signature
|
||||||
type Option func(o *Options)
|
type Option func(o *Options)
|
||||||
|
|
||||||
|
// NewOptions new options struct with filed values
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
@@ -48,36 +57,42 @@ func NewOptions(opts ...Option) Options {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllowFail allows config source to fail
|
||||||
func AllowFail(b bool) Option {
|
func AllowFail(b bool) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.AllowFail = b
|
o.AllowFail = b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeLoad run funcs before config load
|
||||||
func BeforeLoad(fn ...func(context.Context, Config) error) Option {
|
func BeforeLoad(fn ...func(context.Context, Config) error) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.BeforeLoad = fn
|
o.BeforeLoad = fn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AfterLoad run funcs after config load
|
||||||
func AfterLoad(fn ...func(context.Context, Config) error) Option {
|
func AfterLoad(fn ...func(context.Context, Config) error) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.AfterLoad = fn
|
o.AfterLoad = fn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeSave run funcs before save
|
||||||
func BeforeSave(fn ...func(context.Context, Config) error) Option {
|
func BeforeSave(fn ...func(context.Context, Config) error) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.BeforeSave = fn
|
o.BeforeSave = fn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AfterSave run fncs after save
|
||||||
func AfterSave(fn ...func(context.Context, Config) error) Option {
|
func AfterSave(fn ...func(context.Context, Config) error) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.AfterSave = fn
|
o.AfterSave = fn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Context pass context
|
||||||
func Context(ctx context.Context) Option {
|
func Context(ctx context.Context) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Context = ctx
|
o.Context = ctx
|
||||||
@@ -91,6 +106,7 @@ func Codec(c codec.Codec) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logger sets the logger
|
||||||
func Logger(l logger.Logger) Option {
|
func Logger(l logger.Logger) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Logger = l
|
o.Logger = l
|
||||||
@@ -111,7 +127,7 @@ func Struct(v interface{}) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StructTag
|
// StructTag sets the struct tag that used for filling
|
||||||
func StructTag(name string) Option {
|
func StructTag(name string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.StructTag = name
|
o.StructTag = name
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
// Package debug provides interfaces for service debugging
|
|
||||||
package debug
|
|
@@ -1,190 +0,0 @@
|
|||||||
// Package kubernetes is a logger implementing (github.com/unistack-org/micro/v3/debug/log).Log
|
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/debug/log"
|
|
||||||
"github.com/unistack-org/micro/v3/metadata"
|
|
||||||
"github.com/unistack-org/micro/v3/util/kubernetes/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
type klog struct {
|
|
||||||
client client.Client
|
|
||||||
|
|
||||||
log.Options
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *klog) podLogStream(podName string, stream *kubeStream) {
|
|
||||||
p := make(map[string]string)
|
|
||||||
p["follow"] = "true"
|
|
||||||
|
|
||||||
// get the logs for the pod
|
|
||||||
body, err := k.client.Log(&client.Resource{
|
|
||||||
Name: podName,
|
|
||||||
Kind: "pod",
|
|
||||||
}, client.LogParams(p))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s := bufio.NewScanner(body)
|
|
||||||
defer body.Close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-stream.stop:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
if s.Scan() {
|
|
||||||
record := k.parse(s.Text())
|
|
||||||
stream.stream <- record
|
|
||||||
} else {
|
|
||||||
// TODO: is there a blocking call
|
|
||||||
// rather than a sleep loop?
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *klog) getMatchingPods() ([]string, error) {
|
|
||||||
r := &client.Resource{
|
|
||||||
Kind: "pod",
|
|
||||||
Value: new(client.PodList),
|
|
||||||
}
|
|
||||||
|
|
||||||
l := make(map[string]string)
|
|
||||||
|
|
||||||
l["name"] = client.Format(k.Options.Name)
|
|
||||||
// TODO: specify micro:service
|
|
||||||
// l["micro"] = "service"
|
|
||||||
|
|
||||||
if err := k.client.Get(r, client.GetLabels(l)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var matches []string
|
|
||||||
|
|
||||||
for _, p := range r.Value.(*client.PodList).Items {
|
|
||||||
// find labels that match the name
|
|
||||||
if p.Metadata.Labels["name"] == client.Format(k.Options.Name) {
|
|
||||||
matches = append(matches, p.Metadata.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matches, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *klog) parse(line string) log.Record {
|
|
||||||
record := log.Record{}
|
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte(line), &record); err != nil {
|
|
||||||
record.Timestamp = time.Now().UTC()
|
|
||||||
record.Message = line
|
|
||||||
record.Metadata = metadata.New(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
record.Metadata["service"] = k.Options.Name
|
|
||||||
|
|
||||||
return record
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *klog) Read(options ...log.ReadOption) ([]log.Record, error) {
|
|
||||||
opts := &log.ReadOptions{}
|
|
||||||
for _, o := range options {
|
|
||||||
o(opts)
|
|
||||||
}
|
|
||||||
pods, err := k.getMatchingPods()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var records []log.Record
|
|
||||||
|
|
||||||
for _, pod := range pods {
|
|
||||||
logParams := make(map[string]string)
|
|
||||||
|
|
||||||
if !opts.Since.Equal(time.Time{}) {
|
|
||||||
logParams["sinceSeconds"] = strconv.Itoa(int(time.Since(opts.Since).Seconds()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Count != 0 {
|
|
||||||
logParams["tailLines"] = strconv.Itoa(opts.Count)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Stream {
|
|
||||||
logParams["follow"] = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
logs, err := k.client.Log(&client.Resource{
|
|
||||||
Name: pod,
|
|
||||||
Kind: "pod",
|
|
||||||
}, client.LogParams(logParams))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer logs.Close()
|
|
||||||
|
|
||||||
s := bufio.NewScanner(logs)
|
|
||||||
|
|
||||||
for s.Scan() {
|
|
||||||
record := k.parse(s.Text())
|
|
||||||
record.Metadata["pod"] = pod
|
|
||||||
records = append(records, record)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort the records
|
|
||||||
sort.Slice(records, func(i, j int) bool { return records[i].Timestamp.Before(records[j].Timestamp) })
|
|
||||||
|
|
||||||
return records, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *klog) Write(l log.Record) error {
|
|
||||||
return write(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *klog) Stream() (log.Stream, error) {
|
|
||||||
// find the matching pods
|
|
||||||
pods, err := k.getMatchingPods()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stream := &kubeStream{
|
|
||||||
stream: make(chan log.Record),
|
|
||||||
stop: make(chan bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
// stream from the individual pods
|
|
||||||
for _, pod := range pods {
|
|
||||||
go k.podLogStream(pod, stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stream, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLog returns a configured Kubernetes logger
|
|
||||||
func NewLog(opts ...log.Option) log.Log {
|
|
||||||
klog := &klog{}
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&klog.Options)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(os.Getenv("KUBERNETES_SERVICE_HOST")) > 0 {
|
|
||||||
klog.client = client.NewClusterClient()
|
|
||||||
} else {
|
|
||||||
klog.client = client.NewLocalClient()
|
|
||||||
}
|
|
||||||
return klog
|
|
||||||
}
|
|
@@ -1,71 +0,0 @@
|
|||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/unistack-org/micro/v3/debug/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestKubernetes(t *testing.T) {
|
|
||||||
if len(os.Getenv("INTEGRATION_TESTS")) > 0 {
|
|
||||||
t.Skip()
|
|
||||||
}
|
|
||||||
|
|
||||||
k := NewLog(log.Name("micro-network"))
|
|
||||||
|
|
||||||
r, w, err := os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := os.Stderr
|
|
||||||
os.Stderr = w
|
|
||||||
meta := make(map[string]string)
|
|
||||||
|
|
||||||
write := log.Record{
|
|
||||||
Timestamp: time.Unix(0, 0).UTC(),
|
|
||||||
Message: "Test log entry",
|
|
||||||
Metadata: meta,
|
|
||||||
}
|
|
||||||
|
|
||||||
meta["foo"] = "bar"
|
|
||||||
|
|
||||||
k.Write(write)
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
w.Close()
|
|
||||||
io.Copy(b, r)
|
|
||||||
os.Stderr = s
|
|
||||||
|
|
||||||
var read log.Record
|
|
||||||
|
|
||||||
if err := json.Unmarshal(b.Bytes(), &read); err != nil {
|
|
||||||
t.Fatalf("json.Unmarshal failed: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, write, read, "Write was not equal")
|
|
||||||
|
|
||||||
records, err := k.Read()
|
|
||||||
assert.Nil(t, err, "Read should not error")
|
|
||||||
assert.NotNil(t, records, "Read should return records")
|
|
||||||
|
|
||||||
stream, err := k.Stream()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
records = nil
|
|
||||||
|
|
||||||
go stream.Stop()
|
|
||||||
|
|
||||||
for s := range stream.Chan() {
|
|
||||||
records = append(records, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, 0, len(records), "Stream should return nothing")
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/debug/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func write(l log.Record) error {
|
|
||||||
m, err := json.Marshal(l)
|
|
||||||
if err == nil {
|
|
||||||
_, err := fmt.Fprintf(os.Stderr, "%s", m)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type kubeStream struct {
|
|
||||||
// the k8s log stream
|
|
||||||
stream chan log.Record
|
|
||||||
sync.Mutex
|
|
||||||
// the stop chan
|
|
||||||
stop chan bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *kubeStream) Chan() <-chan log.Record {
|
|
||||||
return k.stream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *kubeStream) Stop() error {
|
|
||||||
k.Lock()
|
|
||||||
defer k.Unlock()
|
|
||||||
select {
|
|
||||||
case <-k.stop:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
close(k.stop)
|
|
||||||
close(k.stream)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,58 +0,0 @@
|
|||||||
// Package log provides debug logging
|
|
||||||
package log
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Default buffer size if any
|
|
||||||
DefaultSize = 256
|
|
||||||
// Default formatter
|
|
||||||
DefaultFormat = TextFormat
|
|
||||||
)
|
|
||||||
|
|
||||||
// Log is debug log interface for reading and writing logs
|
|
||||||
type Log interface {
|
|
||||||
// Read reads log entries from the logger
|
|
||||||
Read(...ReadOption) ([]Record, error)
|
|
||||||
// Write writes records to log
|
|
||||||
Write(Record) error
|
|
||||||
// Stream log records
|
|
||||||
Stream() (Stream, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record is log record entry
|
|
||||||
type Record struct {
|
|
||||||
// Timestamp of logged event
|
|
||||||
Timestamp time.Time `json:"timestamp"`
|
|
||||||
// Metadata to enrich log record
|
|
||||||
Metadata metadata.Metadata `json:"metadata"`
|
|
||||||
// Value contains log entry
|
|
||||||
Message interface{} `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream returns a log stream
|
|
||||||
type Stream interface {
|
|
||||||
Chan() <-chan Record
|
|
||||||
Stop() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format is a function which formats the output
|
|
||||||
type FormatFunc func(Record) string
|
|
||||||
|
|
||||||
// TextFormat returns text format
|
|
||||||
func TextFormat(r Record) string {
|
|
||||||
t := r.Timestamp.Format("2006-01-02 15:04:05")
|
|
||||||
return fmt.Sprintf("%s %v", t, r.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONFormat is a json Format func
|
|
||||||
func JSONFormat(r Record) string {
|
|
||||||
b, _ := json.Marshal(r)
|
|
||||||
return string(b)
|
|
||||||
}
|
|
@@ -1,115 +0,0 @@
|
|||||||
// Package memory provides an in memory log buffer
|
|
||||||
package memory
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/debug/log"
|
|
||||||
"github.com/unistack-org/micro/v3/metadata"
|
|
||||||
"github.com/unistack-org/micro/v3/util/ring"
|
|
||||||
)
|
|
||||||
|
|
||||||
// memoryLog is default micro log
|
|
||||||
type memoryLog struct {
|
|
||||||
*ring.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLog returns default Logger with
|
|
||||||
func NewLog(opts ...log.Option) log.Log {
|
|
||||||
// get default options
|
|
||||||
options := log.DefaultOptions()
|
|
||||||
|
|
||||||
// apply requested options
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &memoryLog{
|
|
||||||
Buffer: ring.New(options.Size),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes logs into logger
|
|
||||||
func (l *memoryLog) Write(r log.Record) error {
|
|
||||||
l.Buffer.Put(fmt.Sprint(r.Message))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads logs and returns them
|
|
||||||
func (l *memoryLog) Read(opts ...log.ReadOption) ([]log.Record, error) {
|
|
||||||
options := log.ReadOptions{}
|
|
||||||
// initialize the read options
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
var entries []*ring.Entry
|
|
||||||
// if Since options ha sbeen specified we honor it
|
|
||||||
if !options.Since.IsZero() {
|
|
||||||
entries = l.Buffer.Since(options.Since)
|
|
||||||
}
|
|
||||||
|
|
||||||
// only if we specified valid count constraint
|
|
||||||
// do we end up doing some serious if-else kung-fu
|
|
||||||
// if since constraint has been provided
|
|
||||||
// we return *count* number of logs since the given timestamp;
|
|
||||||
// otherwise we return last count number of logs
|
|
||||||
if options.Count > 0 {
|
|
||||||
switch len(entries) > 0 {
|
|
||||||
case true:
|
|
||||||
// if we request fewer logs than what since constraint gives us
|
|
||||||
if options.Count < len(entries) {
|
|
||||||
entries = entries[0:options.Count]
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
entries = l.Buffer.Get(options.Count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
records := make([]log.Record, 0, len(entries))
|
|
||||||
for _, entry := range entries {
|
|
||||||
record := log.Record{
|
|
||||||
Timestamp: entry.Timestamp,
|
|
||||||
Message: entry.Value,
|
|
||||||
}
|
|
||||||
records = append(records, record)
|
|
||||||
}
|
|
||||||
|
|
||||||
return records, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream returns channel for reading log records
|
|
||||||
// along with a stop channel, close it when done
|
|
||||||
func (l *memoryLog) Stream() (log.Stream, error) {
|
|
||||||
// get stream channel from ring buffer
|
|
||||||
stream, stop := l.Buffer.Stream()
|
|
||||||
// make a buffered channel
|
|
||||||
records := make(chan log.Record, 128)
|
|
||||||
// get last 10 records
|
|
||||||
last10 := l.Buffer.Get(10)
|
|
||||||
|
|
||||||
// stream the log records
|
|
||||||
go func() {
|
|
||||||
// first send last 10 records
|
|
||||||
for _, entry := range last10 {
|
|
||||||
records <- log.Record{
|
|
||||||
Timestamp: entry.Timestamp,
|
|
||||||
Message: entry.Value,
|
|
||||||
Metadata: metadata.New(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// now stream continuously
|
|
||||||
for entry := range stream {
|
|
||||||
records <- log.Record{
|
|
||||||
Timestamp: entry.Timestamp,
|
|
||||||
Message: entry.Value,
|
|
||||||
Metadata: metadata.New(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return &logStream{
|
|
||||||
stream: records,
|
|
||||||
stop: stop,
|
|
||||||
}, nil
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
package memory
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/debug/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLogger(t *testing.T) {
|
|
||||||
// set size to some value
|
|
||||||
size := 100
|
|
||||||
// override the global logger
|
|
||||||
lg := NewLog(log.Size(size))
|
|
||||||
// make sure we have the right size of the logger ring buffer
|
|
||||||
if lg.(*memoryLog).Size() != size {
|
|
||||||
t.Errorf("expected buffer size: %d, got: %d", size, lg.(*memoryLog).Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log some cruft
|
|
||||||
lg.Write(log.Record{Message: "foobar"})
|
|
||||||
lg.Write(log.Record{Message: "foo bar"})
|
|
||||||
|
|
||||||
// Check if the logs are stored in the logger ring buffer
|
|
||||||
expected := []string{"foobar", "foo bar"}
|
|
||||||
entries, _ := lg.Read(log.Count(len(expected)))
|
|
||||||
for i, entry := range entries {
|
|
||||||
if !reflect.DeepEqual(entry.Message, expected[i]) {
|
|
||||||
t.Errorf("expected %s, got %s", expected[i], entry.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,24 +0,0 @@
|
|||||||
package memory
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/unistack-org/micro/v3/debug/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type logStream struct {
|
|
||||||
stream <-chan log.Record
|
|
||||||
stop chan bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *logStream) Chan() <-chan log.Record {
|
|
||||||
return l.stream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *logStream) Stop() error {
|
|
||||||
select {
|
|
||||||
case <-l.stop:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
close(l.stop)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
package noop
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/unistack-org/micro/v3/debug/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type noop struct{}
|
|
||||||
|
|
||||||
func (n *noop) Read(...log.ReadOption) ([]log.Record, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Write(log.Record) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Stream() (log.Stream, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLog(opts ...log.Option) log.Log {
|
|
||||||
return new(noop)
|
|
||||||
}
|
|
@@ -1,70 +0,0 @@
|
|||||||
package log
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Option used by the logger
|
|
||||||
type Option func(*Options)
|
|
||||||
|
|
||||||
// Options are logger options
|
|
||||||
type Options struct {
|
|
||||||
// Name of the log
|
|
||||||
Name string
|
|
||||||
// Size is the size of ring buffer
|
|
||||||
Size int
|
|
||||||
// Format specifies the output format
|
|
||||||
Format FormatFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name of the log
|
|
||||||
func Name(n string) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Name = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size sets the size of the ring buffer
|
|
||||||
func Size(s int) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Size = s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Format(f FormatFunc) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Format = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultOptions returns default options
|
|
||||||
func DefaultOptions() Options {
|
|
||||||
return Options{
|
|
||||||
Size: DefaultSize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadOptions for querying the logs
|
|
||||||
type ReadOptions struct {
|
|
||||||
// Since what time in past to return the logs
|
|
||||||
Since time.Time
|
|
||||||
// Count specifies number of logs to return
|
|
||||||
Count int
|
|
||||||
// Stream requests continuous log stream
|
|
||||||
Stream bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadOption used for reading the logs
|
|
||||||
type ReadOption func(*ReadOptions)
|
|
||||||
|
|
||||||
// Since sets the time since which to return the log records
|
|
||||||
func Since(s time.Time) ReadOption {
|
|
||||||
return func(o *ReadOptions) {
|
|
||||||
o.Since = s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count sets the number of log records to return
|
|
||||||
func Count(c int) ReadOption {
|
|
||||||
return func(o *ReadOptions) {
|
|
||||||
o.Count = c
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,89 +0,0 @@
|
|||||||
package stats
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/debug/stats"
|
|
||||||
"github.com/unistack-org/micro/v3/util/ring"
|
|
||||||
)
|
|
||||||
|
|
||||||
type memoryStats struct {
|
|
||||||
// used to store past stats
|
|
||||||
buffer *ring.Buffer
|
|
||||||
|
|
||||||
sync.RWMutex
|
|
||||||
started int64
|
|
||||||
requests uint64
|
|
||||||
errors uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *memoryStats) snapshot() *stats.Stat {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
var mstat runtime.MemStats
|
|
||||||
runtime.ReadMemStats(&mstat)
|
|
||||||
|
|
||||||
now := time.Now().Unix()
|
|
||||||
|
|
||||||
return &stats.Stat{
|
|
||||||
Timestamp: now,
|
|
||||||
Started: s.started,
|
|
||||||
Uptime: now - s.started,
|
|
||||||
Memory: mstat.Alloc,
|
|
||||||
GC: mstat.PauseTotalNs,
|
|
||||||
Threads: uint64(runtime.NumGoroutine()),
|
|
||||||
Requests: s.requests,
|
|
||||||
Errors: s.errors,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *memoryStats) Read() ([]*stats.Stat, error) {
|
|
||||||
buf := s.buffer.Get(s.buffer.Size())
|
|
||||||
var buffer []*stats.Stat
|
|
||||||
|
|
||||||
// get a value from the buffer if it exists
|
|
||||||
for _, b := range buf {
|
|
||||||
stat, ok := b.Value.(*stats.Stat)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buffer = append(buffer, stat)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a snapshot
|
|
||||||
buffer = append(buffer, s.snapshot())
|
|
||||||
|
|
||||||
return buffer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *memoryStats) Write(stat *stats.Stat) error {
|
|
||||||
s.buffer.Put(stat)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *memoryStats) Record(err error) error {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
// increment the total request count
|
|
||||||
s.requests++
|
|
||||||
|
|
||||||
// increment the error count
|
|
||||||
if err != nil {
|
|
||||||
s.errors++
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStats returns a new in memory stats buffer
|
|
||||||
// TODO add options
|
|
||||||
func NewStats() stats.Stats {
|
|
||||||
return &memoryStats{
|
|
||||||
started: time.Now().Unix(),
|
|
||||||
buffer: ring.New(1),
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
// Package stats provides runtime stats
|
|
||||||
package stats
|
|
||||||
|
|
||||||
// Stats provides stats interface
|
|
||||||
type Stats interface {
|
|
||||||
// Read stat snapshot
|
|
||||||
Read() ([]*Stat, error)
|
|
||||||
// Write a stat snapshot
|
|
||||||
Write(*Stat) error
|
|
||||||
// Record a request
|
|
||||||
Record(error) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// A runtime stat
|
|
||||||
type Stat struct {
|
|
||||||
// Timestamp of recording
|
|
||||||
Timestamp int64
|
|
||||||
// Start time as unix timestamp
|
|
||||||
Started int64
|
|
||||||
// Uptime in seconds
|
|
||||||
Uptime int64
|
|
||||||
// Memory usage in bytes
|
|
||||||
Memory uint64
|
|
||||||
// Threads aka go routines
|
|
||||||
Threads uint64
|
|
||||||
// Garbage collection in nanoseconds
|
|
||||||
GC uint64
|
|
||||||
// Total requests
|
|
||||||
Requests uint64
|
|
||||||
// Total errors
|
|
||||||
Errors uint64
|
|
||||||
}
|
|
@@ -9,46 +9,51 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrBadRequest
|
// ErrBadRequest returns then requests contains invalid data
|
||||||
ErrBadRequest = &Error{Code: 400}
|
ErrBadRequest = &Error{Code: 400}
|
||||||
// ErrUnauthorized
|
// ErrUnauthorized returns then user have unauthorized call
|
||||||
ErrUnauthorized = &Error{Code: 401}
|
ErrUnauthorized = &Error{Code: 401}
|
||||||
// ErrForbidden
|
// ErrForbidden returns then user have not access the resource
|
||||||
ErrForbidden = &Error{Code: 403}
|
ErrForbidden = &Error{Code: 403}
|
||||||
// ErrNotFound
|
// ErrNotFound returns then user specify invalid endpoint
|
||||||
ErrNotFound = &Error{Code: 404}
|
ErrNotFound = &Error{Code: 404}
|
||||||
// ErrMethodNotAllowed
|
// ErrMethodNotAllowed returns then user try to get invalid method
|
||||||
ErrMethodNotAllowed = &Error{Code: 405}
|
ErrMethodNotAllowed = &Error{Code: 405}
|
||||||
// ErrTimeout
|
// ErrTimeout returns then timeout exceeded
|
||||||
ErrTimeout = &Error{Code: 408}
|
ErrTimeout = &Error{Code: 408}
|
||||||
// ErrConflict
|
// ErrConflict returns then request create duplicate resource
|
||||||
ErrConflict = &Error{Code: 409}
|
ErrConflict = &Error{Code: 409}
|
||||||
// ErrInternalServerError
|
// ErrInternalServerError returns then server cant process request because of internal error
|
||||||
ErrInternalServerError = &Error{Code: 500}
|
ErrInternalServerError = &Error{Code: 500}
|
||||||
// ErNotImplemented
|
// ErNotImplemented returns then server does not have desired endpoint method
|
||||||
ErNotImplemented = &Error{Code: 501}
|
ErNotImplemented = &Error{Code: 501}
|
||||||
// ErrBadGateway
|
// ErrBadGateway returns then server cant process request
|
||||||
ErrBadGateway = &Error{Code: 502}
|
ErrBadGateway = &Error{Code: 502}
|
||||||
// ErrServiceUnavailable
|
// ErrServiceUnavailable returns then service unavailable
|
||||||
ErrServiceUnavailable = &Error{Code: 503}
|
ErrServiceUnavailable = &Error{Code: 503}
|
||||||
// ErrGatewayTimeout
|
// ErrGatewayTimeout returns then server have long time to process request
|
||||||
ErrGatewayTimeout = &Error{Code: 504}
|
ErrGatewayTimeout = &Error{Code: 504}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error tpye
|
// Error type
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Id string
|
// Id holds error id or service, usually someting like my_service or uuid
|
||||||
Code int32
|
Id string
|
||||||
|
// Detail holds some useful details about error
|
||||||
Detail string
|
Detail string
|
||||||
|
// Status usually holds text of http status
|
||||||
Status string
|
Status string
|
||||||
|
// Code holds error code
|
||||||
|
Code int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error satisfies error interface
|
||||||
func (e *Error) Error() string {
|
func (e *Error) Error() string {
|
||||||
b, _ := json.Marshal(e)
|
b, _ := json.Marshal(e)
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// New generates a custom error.
|
// New generates a custom error
|
||||||
func New(id, detail string, code int32) error {
|
func New(id, detail string, code int32) error {
|
||||||
return &Error{
|
return &Error{
|
||||||
Id: id,
|
Id: id,
|
||||||
|
@@ -1,49 +0,0 @@
|
|||||||
// Package events contains interfaces for managing events within distributed systems
|
|
||||||
package events
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrMissingTopic is returned if a blank topic was provided to publish
|
|
||||||
ErrMissingTopic = errors.New("Missing topic")
|
|
||||||
// ErrEncodingMessage is returned from publish if there was an error encoding the message option
|
|
||||||
ErrEncodingMessage = errors.New("Error encoding message")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Stream of events
|
|
||||||
type Stream interface {
|
|
||||||
Publish(ctx context.Context, topic string, msg interface{}, opts ...PublishOption) error
|
|
||||||
Subscribe(ctx context.Context, topic string, opts ...SubscribeOption) (<-chan Event, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store of events
|
|
||||||
type Store interface {
|
|
||||||
Read(ctx context.Context, opts ...ReadOption) ([]*Event, error)
|
|
||||||
Write(ctx context.Context, event *Event, opts ...WriteOption) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event is the object returned by the broker when you subscribe to a topic
|
|
||||||
type Event struct {
|
|
||||||
// ID to uniquely identify the event
|
|
||||||
ID string
|
|
||||||
// Topic of event, e.g. "register.service.created"
|
|
||||||
Topic string
|
|
||||||
// Timestamp of the event
|
|
||||||
Timestamp time.Time
|
|
||||||
// Metadata contains the encoded event was indexed by
|
|
||||||
Metadata metadata.Metadata
|
|
||||||
// Payload contains the encoded message
|
|
||||||
Payload []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal the events message into an object
|
|
||||||
func (e *Event) Unmarshal(v interface{}) error {
|
|
||||||
return json.Unmarshal(e.Payload, v)
|
|
||||||
}
|
|
@@ -1,124 +0,0 @@
|
|||||||
package events
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PublishOptions contains all the options which can be provided when publishing an event
|
|
||||||
type PublishOptions struct {
|
|
||||||
// Metadata contains any keys which can be used to query the data, for example a customer id
|
|
||||||
Metadata metadata.Metadata
|
|
||||||
// Timestamp to set for the event, if the timestamp is a zero value, the current time will be used
|
|
||||||
Timestamp time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublishOption sets attributes on PublishOptions
|
|
||||||
type PublishOption func(o *PublishOptions)
|
|
||||||
|
|
||||||
// WithMetadata sets the Metadata field on PublishOptions
|
|
||||||
func WithMetadata(md metadata.Metadata) PublishOption {
|
|
||||||
return func(o *PublishOptions) {
|
|
||||||
o.Metadata = metadata.Copy(md)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTimestamp sets the timestamp field on PublishOptions
|
|
||||||
func WithTimestamp(t time.Time) PublishOption {
|
|
||||||
return func(o *PublishOptions) {
|
|
||||||
o.Timestamp = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeOptions contains all the options which can be provided when subscribing to a topic
|
|
||||||
type SubscribeOptions struct {
|
|
||||||
// Queue is the name of the subscribers queue, if two subscribers have the same queue the message
|
|
||||||
// should only be published to one of them
|
|
||||||
Queue string
|
|
||||||
// StartAtTime is the time from which the messages should be consumed from. If not provided then
|
|
||||||
// the messages will be consumed starting from the moment the Subscription starts.
|
|
||||||
StartAtTime time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeOption sets attributes on SubscribeOptions
|
|
||||||
type SubscribeOption func(o *SubscribeOptions)
|
|
||||||
|
|
||||||
// WithQueue sets the Queue fielf on SubscribeOptions to the value provided
|
|
||||||
func WithQueue(q string) SubscribeOption {
|
|
||||||
return func(o *SubscribeOptions) {
|
|
||||||
o.Queue = q
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithStartAtTime sets the StartAtTime field on SubscribeOptions to the value provided
|
|
||||||
func WithStartAtTime(t time.Time) SubscribeOption {
|
|
||||||
return func(o *SubscribeOptions) {
|
|
||||||
o.StartAtTime = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteOptions contains all the options which can be provided when writing an event to a store
|
|
||||||
type WriteOptions struct {
|
|
||||||
// TTL is the duration the event should be recorded for, a zero value TTL indicates the event should
|
|
||||||
// be stored indefinitely
|
|
||||||
TTL time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteOption sets attributes on WriteOptions
|
|
||||||
type WriteOption func(o *WriteOptions)
|
|
||||||
|
|
||||||
// WithTTL sets the TTL attribute on WriteOptions
|
|
||||||
func WithTTL(d time.Duration) WriteOption {
|
|
||||||
return func(o *WriteOptions) {
|
|
||||||
o.TTL = d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadOptions contains all the options which can be provided when reading events from a store
|
|
||||||
type ReadOptions struct {
|
|
||||||
// Topic to read events from, if no topic is provided events from all topics will be returned
|
|
||||||
Topic string
|
|
||||||
// Query to filter the results using. The store will query the metadata provided when the event
|
|
||||||
// was written to the store
|
|
||||||
Query map[string]string
|
|
||||||
// Limit the number of results to return
|
|
||||||
Limit int
|
|
||||||
// Offset the results by this number, useful for paginated queries
|
|
||||||
Offset int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadOption sets attributes on ReadOptions
|
|
||||||
type ReadOption func(o *ReadOptions)
|
|
||||||
|
|
||||||
// ReadTopic sets the topic attribute on ReadOptions
|
|
||||||
func ReadTopic(t string) ReadOption {
|
|
||||||
return func(o *ReadOptions) {
|
|
||||||
o.Topic = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadFilter sets a key and value in the query
|
|
||||||
func ReadFilter(key, value string) ReadOption {
|
|
||||||
return func(o *ReadOptions) {
|
|
||||||
if o.Query == nil {
|
|
||||||
o.Query = map[string]string{key: value}
|
|
||||||
} else {
|
|
||||||
o.Query[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadLimit sets the limit attribute on ReadOptions
|
|
||||||
func ReadLimit(l int) ReadOption {
|
|
||||||
return func(o *ReadOptions) {
|
|
||||||
o.Limit = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadOffset sets the offset attribute on ReadOptions
|
|
||||||
func ReadOffset(l int) ReadOption {
|
|
||||||
return func(o *ReadOptions) {
|
|
||||||
o.Offset = 1
|
|
||||||
}
|
|
||||||
}
|
|
21
flow/dag.go
Normal file
21
flow/dag.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package flow
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) ID() string {
|
||||||
|
return n.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) Name() string {
|
||||||
|
return n.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) String() string {
|
||||||
|
return n.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) Hashcode() interface{} {
|
||||||
|
return n.name
|
||||||
|
}
|
68
flow/dag_test.go
Normal file
68
flow/dag_test.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package flow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/silas/dag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkErr(t *testing.T, err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDag(t *testing.T) {
|
||||||
|
d1 := &dag.AcyclicGraph{}
|
||||||
|
d2 := &dag.AcyclicGraph{}
|
||||||
|
d2v1 := d2.Add(&node{"Substep.Create"})
|
||||||
|
v1 := d1.Add(&node{"AccountService.Create"})
|
||||||
|
v2 := d1.Add(&node{"AuthzService.Create"})
|
||||||
|
v3 := d1.Add(&node{"AuthnService.Create"})
|
||||||
|
v4 := d1.Add(&node{"ProjectService.Create"})
|
||||||
|
v5 := d1.Add(&node{"ContactService.Create"})
|
||||||
|
v6 := d1.Add(&node{"NetworkService.Create"})
|
||||||
|
v7 := d1.Add(&node{"MailerService.Create"})
|
||||||
|
v8 := d1.Add(&node{"NestedService.Create"})
|
||||||
|
v9 := d1.Add(d2v1)
|
||||||
|
d1.Connect(dag.BasicEdge(v1, v2))
|
||||||
|
d1.Connect(dag.BasicEdge(v1, v3))
|
||||||
|
d1.Connect(dag.BasicEdge(v1, v4))
|
||||||
|
d1.Connect(dag.BasicEdge(v1, v5))
|
||||||
|
d1.Connect(dag.BasicEdge(v1, v6))
|
||||||
|
d1.Connect(dag.BasicEdge(v1, v7))
|
||||||
|
d1.Connect(dag.BasicEdge(v7, v8))
|
||||||
|
d1.Connect(dag.BasicEdge(v8, v9))
|
||||||
|
|
||||||
|
if err := d1.Validate(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d1.TransitiveReduction()
|
||||||
|
|
||||||
|
var steps [][]string
|
||||||
|
fn := func(n dag.Vertex, idx int) error {
|
||||||
|
if idx == 0 {
|
||||||
|
steps = make([][]string, 1, 1)
|
||||||
|
steps[0] = make([]string, 0, 1)
|
||||||
|
} else if idx >= len(steps) {
|
||||||
|
tsteps := make([][]string, idx+1, idx+1)
|
||||||
|
copy(tsteps, steps)
|
||||||
|
steps = tsteps
|
||||||
|
steps[idx] = make([]string, 0, 1)
|
||||||
|
}
|
||||||
|
steps[idx] = append(steps[idx], fmt.Sprintf("%s", n))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
start := &node{"AccountService.Create"}
|
||||||
|
err := d1.SortedDepthFirstWalk([]dag.Vertex{start}, fn)
|
||||||
|
checkErr(t, err)
|
||||||
|
if len(steps) != 4 {
|
||||||
|
t.Fatalf("invalid steps: %#+v", steps)
|
||||||
|
}
|
||||||
|
if steps[3][0] != "Substep.Create" {
|
||||||
|
t.Fatalf("invalid last step: %#+v", steps)
|
||||||
|
}
|
||||||
|
}
|
7
flow/flow.go
Normal file
7
flow/flow.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Package flow is an interface used for saga pattern messaging
|
||||||
|
package flow
|
||||||
|
|
||||||
|
type Step interface {
|
||||||
|
// Endpoint returns service_name.service_method
|
||||||
|
Endpoint() string
|
||||||
|
}
|
@@ -64,7 +64,7 @@ func newFunction(opts ...Option) Function {
|
|||||||
// make context the last thing
|
// make context the last thing
|
||||||
fopts = append(fopts, Context(ctx))
|
fopts = append(fopts, Context(ctx))
|
||||||
|
|
||||||
service := &service{opts: NewOptions(opts...)}
|
service := &service{opts: NewOptions(fopts...)}
|
||||||
|
|
||||||
fn := &function{
|
fn := &function{
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
|
@@ -2,25 +2,26 @@
|
|||||||
|
|
||||||
package micro
|
package micro
|
||||||
|
|
||||||
|
/*
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
rmemory "github.com/unistack-org/micro-register-memory"
|
"github.com/unistack-org/micro/v3/register"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFunction(t *testing.T) {
|
func TestFunction(t *testing.T) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
r := rmemory.NewRegister()
|
r := register.NewRegister()
|
||||||
|
ctx := context.TODO()
|
||||||
// create service
|
// create service
|
||||||
fn := NewFunction(
|
fn := NewFunction(
|
||||||
Register(r),
|
Register(r),
|
||||||
Name("test.function"),
|
Name("test.function"),
|
||||||
AfterStart(func() error {
|
AfterStart(func(ctx context.Context) error {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
return nil
|
return nil
|
||||||
}),
|
}),
|
||||||
@@ -61,3 +62,5 @@ func TestFunction(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
19
go.mod
19
go.mod
@@ -1,22 +1,13 @@
|
|||||||
module github.com/unistack-org/micro/v3
|
module github.com/unistack-org/micro/v3
|
||||||
|
|
||||||
go 1.14
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/ef-ds/deque v1.0.4
|
github.com/ef-ds/deque v1.0.4
|
||||||
github.com/golang/protobuf v1.4.3
|
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
github.com/imdario/mergo v0.3.11
|
github.com/imdario/mergo v0.3.12
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/miekg/dns v1.1.38
|
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
golang.org/x/net v0.0.0-20210326220855-61e056675ecf
|
||||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c
|
|
||||||
github.com/stretchr/testify v1.7.0
|
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
|
|
||||||
google.golang.org/protobuf v1.25.0
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
|
||||||
)
|
)
|
||||||
|
121
go.sum
121
go.sum
@@ -1,126 +1,21 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI=
|
github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI=
|
||||||
github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
|
github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34 h1:vBfVmA5mZhsQa2jr1FOL9nfA37N/jnbBmi5XUfviVTI=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||||
github.com/miekg/dns v1.1.38 h1:MtIY+fmHUVVgv1AXzmKMWcwdCYxTRPG1EDjpqF4RCEw=
|
golang.org/x/net v0.0.0-20210326220855-61e056675ecf/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||||
github.com/miekg/dns v1.1.38/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
|
||||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
|
@@ -6,7 +6,7 @@ import "context"
|
|||||||
var (
|
var (
|
||||||
// DefaultLogger variable
|
// DefaultLogger variable
|
||||||
DefaultLogger Logger = NewLogger()
|
DefaultLogger Logger = NewLogger()
|
||||||
// DefaultLogger level
|
// DefaultLevel used by logger
|
||||||
DefaultLevel Level = InfoLevel
|
DefaultLevel Level = InfoLevel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -21,9 +21,9 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type defaultLogger struct {
|
type defaultLogger struct {
|
||||||
sync.RWMutex
|
|
||||||
opts Options
|
opts Options
|
||||||
enc *json.Encoder
|
enc *json.Encoder
|
||||||
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init(opts...) should only overwrite provided options
|
// Init(opts...) should only overwrite provided options
|
||||||
@@ -49,10 +49,18 @@ func (l *defaultLogger) V(level Level) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Fields(fields map[string]interface{}) Logger {
|
func (l *defaultLogger) Fields(fields map[string]interface{}) Logger {
|
||||||
l.Lock()
|
nl := &defaultLogger{opts: l.opts, enc: l.enc}
|
||||||
l.opts.Fields = copyFields(fields)
|
nl.opts.Fields = make(map[string]interface{}, len(l.opts.Fields)+len(fields))
|
||||||
l.Unlock()
|
l.RLock()
|
||||||
return l
|
for k, v := range l.opts.Fields {
|
||||||
|
nl.opts.Fields[k] = v
|
||||||
|
}
|
||||||
|
l.RUnlock()
|
||||||
|
|
||||||
|
for k, v := range fields {
|
||||||
|
nl.opts.Fields[k] = v
|
||||||
|
}
|
||||||
|
return nl
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyFields(src map[string]interface{}) map[string]interface{} {
|
func copyFields(src map[string]interface{}) map[string]interface{} {
|
||||||
@@ -153,7 +161,9 @@ func (l *defaultLogger) Log(ctx context.Context, level Level, args ...interface{
|
|||||||
}
|
}
|
||||||
|
|
||||||
fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
|
fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
|
||||||
fields["msg"] = fmt.Sprint(args...)
|
if len(args) > 0 {
|
||||||
|
fields["msg"] = fmt.Sprint(args...)
|
||||||
|
}
|
||||||
|
|
||||||
l.RLock()
|
l.RLock()
|
||||||
_ = l.enc.Encode(fields)
|
_ = l.enc.Encode(fields)
|
||||||
@@ -178,7 +188,7 @@ func (l *defaultLogger) Logf(ctx context.Context, level Level, msg string, args
|
|||||||
fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
|
fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
fields["msg"] = fmt.Sprintf(msg, args...)
|
fields["msg"] = fmt.Sprintf(msg, args...)
|
||||||
} else {
|
} else if msg != "" {
|
||||||
fields["msg"] = msg
|
fields["msg"] = msg
|
||||||
}
|
}
|
||||||
l.RLock()
|
l.RLock()
|
||||||
|
@@ -11,17 +11,18 @@ type Option func(*Options)
|
|||||||
|
|
||||||
// Options holds logger options
|
// Options holds logger options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// Out holds the output writer
|
||||||
// The logging level the logger should log at. default is `InfoLevel`
|
|
||||||
Level Level
|
|
||||||
// fields to always be logged
|
|
||||||
Fields map[string]interface{}
|
|
||||||
// It's common to set this to a file, or leave it default which is `os.Stderr`
|
|
||||||
Out io.Writer
|
Out io.Writer
|
||||||
// Caller skip frame count for file:line info
|
// Context holds exernal options
|
||||||
CallerSkipCount int
|
|
||||||
// Alternative options
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Fields holds additional metadata
|
||||||
|
Fields map[string]interface{}
|
||||||
|
// Name holds the logger name
|
||||||
|
Name string
|
||||||
|
// CallerSkipCount number of frmaes to skip
|
||||||
|
CallerSkipCount int
|
||||||
|
// The logging level the logger should log
|
||||||
|
Level Level
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions creates new options struct
|
// NewOptions creates new options struct
|
||||||
|
408
logger/wrapper/wrapper.go
Normal file
408
logger/wrapper/wrapper.go
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
// Package wrapper provides wrapper for Logger
|
||||||
|
package wrapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/client"
|
||||||
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
|
"github.com/unistack-org/micro/v3/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []client.CallOption, err error) []string {
|
||||||
|
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []client.CallOption, stream client.Stream, err error) []string {
|
||||||
|
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultClientPublishObserver = func(ctx context.Context, msg client.Message, opts []client.PublishOption, err error) []string {
|
||||||
|
labels := []string{"endpoint", msg.Topic()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, err error) []string {
|
||||||
|
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultServerSubscriberObserver = func(ctx context.Context, msg server.Message, err error) []string {
|
||||||
|
labels := []string{"endpoint", msg.Topic()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, err error) []string {
|
||||||
|
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultSkipEndpoints = []string{"Meter.Metrics"}
|
||||||
|
)
|
||||||
|
|
||||||
|
type lWrapper struct {
|
||||||
|
client.Client
|
||||||
|
serverHandler server.HandlerFunc
|
||||||
|
serverSubscriber server.SubscriberFunc
|
||||||
|
clientCallFunc client.CallFunc
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientCallObserver func(context.Context, client.Request, interface{}, []client.CallOption, error) []string
|
||||||
|
type ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, error) []string
|
||||||
|
type ClientPublishObserver func(context.Context, client.Message, []client.PublishOption, error) []string
|
||||||
|
type ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, error) []string
|
||||||
|
type ServerHandlerObserver func(context.Context, server.Request, interface{}, error) []string
|
||||||
|
type ServerSubscriberObserver func(context.Context, server.Message, error) []string
|
||||||
|
|
||||||
|
// Options struct for wrapper
|
||||||
|
type Options struct {
|
||||||
|
// Logger that used for log
|
||||||
|
Logger logger.Logger
|
||||||
|
// Level for logger
|
||||||
|
Level logger.Level
|
||||||
|
// Enabled flag
|
||||||
|
Enabled bool
|
||||||
|
// ClientCallObservers funcs
|
||||||
|
ClientCallObservers []ClientCallObserver
|
||||||
|
// ClientStreamObservers funcs
|
||||||
|
ClientStreamObservers []ClientStreamObserver
|
||||||
|
// ClientPublishObservers funcs
|
||||||
|
ClientPublishObservers []ClientPublishObserver
|
||||||
|
// ClientCallFuncObservers funcs
|
||||||
|
ClientCallFuncObservers []ClientCallFuncObserver
|
||||||
|
// ServerHandlerObservers funcs
|
||||||
|
ServerHandlerObservers []ServerHandlerObserver
|
||||||
|
// ServerSubscriberObservers funcs
|
||||||
|
ServerSubscriberObservers []ServerSubscriberObserver
|
||||||
|
// SkipEndpoints
|
||||||
|
SkipEndpoints []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option func signature
|
||||||
|
type Option func(*Options)
|
||||||
|
|
||||||
|
// NewOptions creates Options from Option slice
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{
|
||||||
|
Logger: logger.DefaultLogger,
|
||||||
|
Level: logger.TraceLevel,
|
||||||
|
ClientCallObservers: []ClientCallObserver{DefaultClientCallObserver},
|
||||||
|
ClientStreamObservers: []ClientStreamObserver{DefaultClientStreamObserver},
|
||||||
|
ClientPublishObservers: []ClientPublishObserver{DefaultClientPublishObserver},
|
||||||
|
ClientCallFuncObservers: []ClientCallFuncObserver{DefaultClientCallFuncObserver},
|
||||||
|
ServerHandlerObservers: []ServerHandlerObserver{DefaultServerHandlerObserver},
|
||||||
|
ServerSubscriberObservers: []ServerSubscriberObserver{DefaultServerSubscriberObserver},
|
||||||
|
SkipEndpoints: DefaultSkipEndpoints,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEnabled enable/diable flag
|
||||||
|
func WithEnabled(b bool) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Enabled = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLevel log level
|
||||||
|
func WithLevel(l logger.Level) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Level = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLogger logger
|
||||||
|
func WithLogger(l logger.Logger) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Logger = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientCallObservers funcs
|
||||||
|
func WithClientCallObservers(ob ...ClientCallObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ClientCallObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientStreamObservers funcs
|
||||||
|
func WithClientStreamObservers(ob ...ClientStreamObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ClientStreamObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientPublishObservers funcs
|
||||||
|
func WithClientPublishObservers(ob ...ClientPublishObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ClientPublishObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientCallFuncObservers funcs
|
||||||
|
func WithClientCallFuncObservers(ob ...ClientCallFuncObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ClientCallFuncObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithServerHandlerObservers funcs
|
||||||
|
func WithServerHandlerObservers(ob ...ServerHandlerObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ServerHandlerObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithServerSubscriberObservers funcs
|
||||||
|
func WithServerSubscriberObservers(ob ...ServerSubscriberObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ServerSubscriberObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SkipEndpoins
|
||||||
|
func SkipEndpoints(eps ...string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.SkipEndpoints = append(o.SkipEndpoints, eps...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||||
|
err := l.Client.Call(ctx, req, rsp, opts...)
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ClientCallObservers {
|
||||||
|
labels = append(labels, o(ctx, req, rsp, opts, err)...)
|
||||||
|
}
|
||||||
|
fields := make(map[string]interface{}, int(len(labels)/2))
|
||||||
|
for i := 0; i < len(labels); i += 2 {
|
||||||
|
fields[labels[i]] = labels[i+1]
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||||
|
stream, err := l.Client.Stream(ctx, req, opts...)
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return stream, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return stream, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ClientStreamObservers {
|
||||||
|
labels = append(labels, o(ctx, req, opts, stream, err)...)
|
||||||
|
}
|
||||||
|
fields := make(map[string]interface{}, int(len(labels)/2))
|
||||||
|
for i := 0; i < len(labels); i += 2 {
|
||||||
|
fields[labels[i]] = labels[i+1]
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return stream, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) Publish(ctx context.Context, msg client.Message, opts ...client.PublishOption) error {
|
||||||
|
err := l.Client.Publish(ctx, msg, opts...)
|
||||||
|
|
||||||
|
endpoint := msg.Topic()
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ClientPublishObservers {
|
||||||
|
labels = append(labels, o(ctx, msg, opts, err)...)
|
||||||
|
}
|
||||||
|
fields := make(map[string]interface{}, int(len(labels)/2))
|
||||||
|
for i := 0; i < len(labels); i += 2 {
|
||||||
|
fields[labels[i]] = labels[i+1]
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) ServerHandler(ctx context.Context, req server.Request, rsp interface{}) error {
|
||||||
|
err := l.serverHandler(ctx, req, rsp)
|
||||||
|
|
||||||
|
endpoint := req.Endpoint()
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ServerHandlerObservers {
|
||||||
|
labels = append(labels, o(ctx, req, rsp, err)...)
|
||||||
|
}
|
||||||
|
fields := make(map[string]interface{}, int(len(labels)/2))
|
||||||
|
for i := 0; i < len(labels); i += 2 {
|
||||||
|
fields[labels[i]] = labels[i+1]
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) ServerSubscriber(ctx context.Context, msg server.Message) error {
|
||||||
|
err := l.serverSubscriber(ctx, msg)
|
||||||
|
|
||||||
|
endpoint := msg.Topic()
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ServerSubscriberObservers {
|
||||||
|
labels = append(labels, o(ctx, msg, err)...)
|
||||||
|
}
|
||||||
|
fields := make(map[string]interface{}, int(len(labels)/2))
|
||||||
|
for i := 0; i < len(labels); i += 2 {
|
||||||
|
fields[labels[i]] = labels[i+1]
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientWrapper accepts an open options and returns a Client Wrapper
|
||||||
|
func NewClientWrapper(opts ...Option) client.Wrapper {
|
||||||
|
return func(c client.Client) client.Client {
|
||||||
|
options := NewOptions()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return &lWrapper{opts: options, Client: c}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientCallWrapper accepts an options and returns a Call Wrapper
|
||||||
|
func NewClientCallWrapper(opts ...Option) client.CallWrapper {
|
||||||
|
return func(h client.CallFunc) client.CallFunc {
|
||||||
|
options := NewOptions()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &lWrapper{opts: options, clientCallFunc: h}
|
||||||
|
return l.ClientCallFunc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) ClientCallFunc(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||||
|
err := l.clientCallFunc(ctx, addr, req, rsp, opts)
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ClientCallFuncObservers {
|
||||||
|
labels = append(labels, o(ctx, addr, req, rsp, opts, err)...)
|
||||||
|
}
|
||||||
|
fields := make(map[string]interface{}, int(len(labels)/2))
|
||||||
|
for i := 0; i < len(labels); i += 2 {
|
||||||
|
fields[labels[i]] = labels[i+1]
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerHandlerWrapper accepts an options and returns a Handler Wrapper
|
||||||
|
func NewServerHandlerWrapper(opts ...Option) server.HandlerWrapper {
|
||||||
|
return func(h server.HandlerFunc) server.HandlerFunc {
|
||||||
|
options := NewOptions()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &lWrapper{opts: options, serverHandler: h}
|
||||||
|
return l.ServerHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerSubscriberWrapper accepts an options and returns a Subscriber Wrapper
|
||||||
|
func NewServerSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
|
||||||
|
return func(h server.SubscriberFunc) server.SubscriberFunc {
|
||||||
|
options := NewOptions()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &lWrapper{opts: options, serverSubscriber: h}
|
||||||
|
return l.ServerSubscriber
|
||||||
|
}
|
||||||
|
}
|
@@ -16,7 +16,7 @@ func FromIncomingContext(ctx context.Context) (Metadata, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
md, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata)
|
md, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata)
|
||||||
if !ok {
|
if !ok || md.md == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return md.md, ok
|
return md.md, ok
|
||||||
@@ -29,7 +29,7 @@ func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
md, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata)
|
md, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata)
|
||||||
if !ok {
|
if !ok || md.md == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return md.md, ok
|
return md.md, ok
|
||||||
@@ -44,7 +44,7 @@ func FromContext(ctx context.Context) (Metadata, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
md, ok := ctx.Value(mdKey{}).(*rawMetadata)
|
md, ok := ctx.Value(mdKey{}).(*rawMetadata)
|
||||||
if !ok {
|
if !ok || md.md == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return md.md, ok
|
return md.md, ok
|
||||||
@@ -93,7 +93,9 @@ func NewIncomingContext(ctx context.Context, md Metadata) context.Context {
|
|||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{md})
|
ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{md})
|
||||||
ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{})
|
if v, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata); !ok || v == nil {
|
||||||
|
ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{})
|
||||||
|
}
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +105,40 @@ func NewOutgoingContext(ctx context.Context, md Metadata) context.Context {
|
|||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{md})
|
ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{md})
|
||||||
ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{})
|
if v, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata); !ok || v == nil {
|
||||||
|
ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{})
|
||||||
|
}
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AppendOutgoingContext apends new md to context
|
||||||
|
func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
||||||
|
md, ok := Pairs(kv...)
|
||||||
|
if !ok {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
omd, ok := FromOutgoingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return NewOutgoingContext(ctx, md)
|
||||||
|
}
|
||||||
|
for k, v := range md {
|
||||||
|
omd.Set(k, v)
|
||||||
|
}
|
||||||
|
return NewOutgoingContext(ctx, omd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendIncomingContext apends new md to context
|
||||||
|
func AppendIncomingContext(ctx context.Context, kv ...string) context.Context {
|
||||||
|
md, ok := Pairs(kv...)
|
||||||
|
if !ok {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
omd, ok := FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return NewIncomingContext(ctx, md)
|
||||||
|
}
|
||||||
|
for k, v := range md {
|
||||||
|
omd.Set(k, v)
|
||||||
|
}
|
||||||
|
return NewIncomingContext(ctx, omd)
|
||||||
|
}
|
||||||
|
@@ -25,13 +25,15 @@ var (
|
|||||||
defaultMetadataSize = 2
|
defaultMetadataSize = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Iterator used to iterate over metadata with order
|
||||||
type Iterator struct {
|
type Iterator struct {
|
||||||
|
md Metadata
|
||||||
|
keys []string
|
||||||
cur int
|
cur int
|
||||||
cnt int
|
cnt int
|
||||||
keys []string
|
|
||||||
md Metadata
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Next advance iterator to next element
|
||||||
func (iter *Iterator) Next(k, v *string) bool {
|
func (iter *Iterator) Next(k, v *string) bool {
|
||||||
if iter.cur+1 > iter.cnt {
|
if iter.cur+1 > iter.cnt {
|
||||||
return false
|
return false
|
||||||
@@ -43,7 +45,7 @@ func (iter *Iterator) Next(k, v *string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate returns run user func with map key, val sorted by key
|
// Iterator returns the itarator for metadata in sorted order
|
||||||
func (md Metadata) Iterator() *Iterator {
|
func (md Metadata) Iterator() *Iterator {
|
||||||
iter := &Iterator{md: md, cnt: len(md)}
|
iter := &Iterator{md: md, cnt: len(md)}
|
||||||
iter.keys = make([]string, 0, iter.cnt)
|
iter.keys = make([]string, 0, iter.cnt)
|
||||||
@@ -109,3 +111,20 @@ func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
|
|||||||
}
|
}
|
||||||
return nmd
|
return nmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pairs from which metadata created
|
||||||
|
func Pairs(kv ...string) (Metadata, bool) {
|
||||||
|
if len(kv)%2 == 1 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
md := New(len(kv) / 2)
|
||||||
|
var k string
|
||||||
|
for i, v := range kv {
|
||||||
|
if i%2 == 0 {
|
||||||
|
k = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
md.Set(k, v)
|
||||||
|
}
|
||||||
|
return md, true
|
||||||
|
}
|
||||||
|
@@ -2,10 +2,31 @@ package metadata
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestAppend(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = AppendIncomingContext(ctx, "key1", "val1", "key2", "val2")
|
||||||
|
md, ok := FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("metadata empty")
|
||||||
|
}
|
||||||
|
if _, ok := md.Get("key1"); !ok {
|
||||||
|
t.Fatal("key1 not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPairs(t *testing.T) {
|
||||||
|
md, ok := Pairs("key1", "val1", "key2", "val2")
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("odd number of kv")
|
||||||
|
}
|
||||||
|
if _, ok = md.Get("key1"); !ok {
|
||||||
|
t.Fatal("key1 not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testCtx(ctx context.Context) {
|
func testCtx(ctx context.Context) {
|
||||||
md := New(2)
|
md := New(2)
|
||||||
md.Set("Key1", "Val1_new")
|
md.Set("Key1", "Val1_new")
|
||||||
@@ -25,7 +46,9 @@ func TestPassing(t *testing.T) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("missing metadata from outgoing context")
|
t.Fatalf("missing metadata from outgoing context")
|
||||||
}
|
}
|
||||||
fmt.Printf("%#+v\n", md)
|
if v, ok := md.Get("Key1"); !ok || v != "Val1_new" {
|
||||||
|
t.Fatalf("invalid metadata value %#+v", md)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
func TestMerge(t *testing.T) {
|
||||||
|
3
meter/generate.go
Normal file
3
meter/generate.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package meter
|
||||||
|
|
||||||
|
//go:generate protoc -I./handler -I../ -I/home/vtolstov/.cache/go-path/pkg/mod/github.com/unistack-org/micro-proto@v0.0.1 --micro_out=components=micro|http|server,standalone=false,debug=true,paths=source_relative:./handler handler/handler.proto
|
34
meter/handler/handler.go
Normal file
34
meter/handler/handler.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
|
"github.com/unistack-org/micro/v3/meter"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// guard to fail early
|
||||||
|
_ MeterServer = &handler{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
meter meter.Meter
|
||||||
|
opts []meter.Option
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(meter meter.Meter, opts ...meter.Option) *handler {
|
||||||
|
return &handler{meter: meter, opts: opts}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
if err := h.meter.Write(buf, h.opts...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rsp.Data = buf.Bytes()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
28
meter/handler/handler.proto
Normal file
28
meter/handler/handler.proto
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package micro.meter.handler;
|
||||||
|
option go_package = "github.com/unistack-org/micro/v3/meter/handler;handler";
|
||||||
|
|
||||||
|
import "api/annotations.proto";
|
||||||
|
import "openapiv2/annotations.proto";
|
||||||
|
import "codec/frame.proto";
|
||||||
|
|
||||||
|
service Meter {
|
||||||
|
rpc Metrics(micro.codec.Frame) returns (micro.codec.Frame) {
|
||||||
|
option (micro.openapiv2.openapiv2_operation) = {
|
||||||
|
operation_id: "Metrics";
|
||||||
|
responses: {
|
||||||
|
key: "default";
|
||||||
|
value: {
|
||||||
|
description: "Error response";
|
||||||
|
schema: {
|
||||||
|
json_schema: {
|
||||||
|
ref: "micro.codec.Frame";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
option (micro.api.http) = { get: "/metrics"; };
|
||||||
|
};
|
||||||
|
};
|
24
meter/handler/handler_micro.pb.go
Normal file
24
meter/handler/handler_micro.pb.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Code generated by protoc-gen-micro
|
||||||
|
// source: handler.proto
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
api "github.com/unistack-org/micro/v3/api"
|
||||||
|
codec "github.com/unistack-org/micro/v3/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMeterEndpoints() []*api.Endpoint {
|
||||||
|
return []*api.Endpoint{
|
||||||
|
&api.Endpoint{
|
||||||
|
Name: "Meter.Metrics",
|
||||||
|
Path: []string{"/metrics"},
|
||||||
|
Method: []string{"GET"},
|
||||||
|
Handler: "rpc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeterServer interface {
|
||||||
|
Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error
|
||||||
|
}
|
33
meter/handler/handler_micro_http.pb.go
Normal file
33
meter/handler/handler_micro_http.pb.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Code generated by protoc-gen-micro
|
||||||
|
// source: handler.proto
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
api "github.com/unistack-org/micro/v3/api"
|
||||||
|
codec "github.com/unistack-org/micro/v3/codec"
|
||||||
|
server "github.com/unistack-org/micro/v3/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
type meterServer struct {
|
||||||
|
MeterServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *meterServer) Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error {
|
||||||
|
return h.MeterServer.Metrics(ctx, req, rsp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterMeterServer(s server.Server, sh MeterServer, opts ...server.HandlerOption) error {
|
||||||
|
type meter interface {
|
||||||
|
Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error
|
||||||
|
}
|
||||||
|
type Meter struct {
|
||||||
|
meter
|
||||||
|
}
|
||||||
|
h := &meterServer{sh}
|
||||||
|
var nopts []server.HandlerOption
|
||||||
|
for _, endpoint := range NewMeterEndpoints() {
|
||||||
|
nopts = append(nopts, api.WithEndpoint(endpoint))
|
||||||
|
}
|
||||||
|
return s.Handle(s.NewHandler(&Meter{h}, append(nopts, opts...)...))
|
||||||
|
}
|
103
meter/meter.go
103
meter/meter.go
@@ -3,6 +3,7 @@ package meter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -14,26 +15,28 @@ var (
|
|||||||
DefaultAddress = ":9090"
|
DefaultAddress = ":9090"
|
||||||
// DefaultPath the meter endpoint where the Meter data will be made available
|
// DefaultPath the meter endpoint where the Meter data will be made available
|
||||||
DefaultPath = "/metrics"
|
DefaultPath = "/metrics"
|
||||||
// timingObjectives is the default spread of stats we maintain for timings / histograms:
|
// DefaultMetricPrefix holds the string that prepends to all metrics
|
||||||
//defaultTimingObjectives = map[float64]float64{0.0: 0, 0.5: 0.05, 0.75: 0.04, 0.90: 0.03, 0.95: 0.02, 0.98: 0.001, 1: 0}
|
|
||||||
// default metric prefix
|
|
||||||
DefaultMetricPrefix = "micro_"
|
DefaultMetricPrefix = "micro_"
|
||||||
// default label prefix
|
// DefaultLabelPrefix holds the string that prepends to all labels
|
||||||
DefaultLabelPrefix = "micro_"
|
DefaultLabelPrefix = "micro_"
|
||||||
|
// DefaultSummaryQuantiles is the default spread of stats for summary
|
||||||
|
DefaultSummaryQuantiles = []float64{0.5, 0.9, 0.97, 0.99, 1}
|
||||||
|
// DefaultSummaryWindow is the default window for summary
|
||||||
|
DefaultSummaryWindow = 5 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// Meter is an interface for collecting and instrumenting metrics
|
// Meter is an interface for collecting and instrumenting metrics
|
||||||
type Meter interface {
|
type Meter interface {
|
||||||
Name() string
|
Name() string
|
||||||
Init(...Option) error
|
Init(opts ...Option) error
|
||||||
Counter(string, ...Option) Counter
|
Counter(name string, opts ...Option) Counter
|
||||||
FloatCounter(string, ...Option) FloatCounter
|
FloatCounter(name string, opts ...Option) FloatCounter
|
||||||
Gauge(string, func() float64, ...Option) Gauge
|
Gauge(name string, fn func() float64, opts ...Option) Gauge
|
||||||
Set(...Option) Meter
|
Set(opts ...Option) Meter
|
||||||
Histogram(string, ...Option) Histogram
|
Histogram(name string, opts ...Option) Histogram
|
||||||
Summary(string, ...Option) Summary
|
Summary(name string, opts ...Option) Summary
|
||||||
SummaryExt(string, time.Duration, []float64, ...Option) Summary
|
SummaryExt(name string, window time.Duration, quantiles []float64, opts ...Option) Summary
|
||||||
Write(io.Writer, bool) error
|
Write(w io.Writer, opts ...Option) error
|
||||||
Options() Options
|
Options() Options
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
@@ -74,54 +77,36 @@ type Summary interface {
|
|||||||
UpdateDuration(time.Time)
|
UpdateDuration(time.Time)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Labels struct {
|
type byKey []string
|
||||||
keys []string
|
|
||||||
vals []string
|
func (k byKey) Len() int { return len(k) / 2 }
|
||||||
|
func (k byKey) Less(i, j int) bool { return k[i*2] < k[j*2] }
|
||||||
|
func (k byKey) Swap(i, j int) {
|
||||||
|
k[i*2], k[i*2+1], k[j*2], k[j*2+1] = k[j*2], k[j*2+1], k[i*2], k[i*2+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls Labels) Len() int {
|
func Sort(slice *[]string) {
|
||||||
return len(ls.keys)
|
bk := byKey(*slice)
|
||||||
}
|
if bk.Len() <= 1 {
|
||||||
|
return
|
||||||
func (ls Labels) Swap(i, j int) {
|
|
||||||
ls.keys[i], ls.keys[j] = ls.keys[j], ls.keys[i]
|
|
||||||
ls.vals[i], ls.vals[j] = ls.vals[j], ls.vals[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ls Labels) Less(i, j int) bool {
|
|
||||||
return ls.vals[i] < ls.vals[j]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ls Labels) Sort() {
|
|
||||||
sort.Sort(ls)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ls Labels) Append(nls Labels) Labels {
|
|
||||||
for n := range nls.keys {
|
|
||||||
ls.keys = append(ls.keys, nls.keys[n])
|
|
||||||
ls.vals = append(ls.vals, nls.vals[n])
|
|
||||||
}
|
}
|
||||||
return ls
|
sort.Sort(bk)
|
||||||
}
|
v := reflect.ValueOf(slice).Elem()
|
||||||
|
cnt := 0
|
||||||
type LabelIter struct {
|
key := 0
|
||||||
labels Labels
|
val := 1
|
||||||
cnt int
|
for key < v.Len() {
|
||||||
cur int
|
if len(bk) > key+2 && bk[key] == bk[key+2] {
|
||||||
}
|
key += 2
|
||||||
|
val += 2
|
||||||
func (ls Labels) Iter() *LabelIter {
|
continue
|
||||||
ls.Sort()
|
}
|
||||||
return &LabelIter{labels: ls, cnt: len(ls.keys)}
|
v.Index(cnt).Set(v.Index(key))
|
||||||
}
|
cnt++
|
||||||
|
v.Index(cnt).Set(v.Index(val))
|
||||||
func (iter *LabelIter) Next(k, v *string) bool {
|
cnt++
|
||||||
if iter.cur+1 > iter.cnt {
|
key += 2
|
||||||
return false
|
val += 2
|
||||||
}
|
}
|
||||||
|
v.SetLen(cnt)
|
||||||
*k = iter.labels.keys[iter.cur]
|
|
||||||
*v = iter.labels.vals[iter.cur]
|
|
||||||
iter.cur++
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
@@ -2,62 +2,23 @@ package meter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNoopMeter(t *testing.T) {
|
func TestNoopMeter(t *testing.T) {
|
||||||
meter := NewMeter(Path("/noop"))
|
m := NewMeter(Path("/noop"))
|
||||||
assert.NotNil(t, meter)
|
if "/noop" != m.Options().Path {
|
||||||
assert.Equal(t, "/noop", meter.Options().Path)
|
t.Fatalf("invalid options parsing: %v", m.Options())
|
||||||
assert.Implements(t, new(Meter), meter)
|
}
|
||||||
|
|
||||||
cnt := meter.Counter("counter", Label("server", "noop"))
|
cnt := m.Counter("counter", Labels("server", "noop"))
|
||||||
cnt.Inc()
|
cnt.Inc()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLabels(t *testing.T) {
|
func TestLabelsSort(t *testing.T) {
|
||||||
var ls Labels
|
ls := []string{"server", "http", "register", "mdns", "broker", "broker1", "broker", "broker2", "server", "tcp"}
|
||||||
ls.keys = []string{"type", "server"}
|
Sort(&ls)
|
||||||
ls.vals = []string{"noop", "http"}
|
|
||||||
|
|
||||||
ls.Sort()
|
if ls[0] != "broker" || ls[1] != "broker2" {
|
||||||
|
|
||||||
if ls.keys[0] != "server" || ls.vals[0] != "http" {
|
|
||||||
t.Fatalf("sort error: %v", ls)
|
t.Fatalf("sort error: %v", ls)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLabelsAppend(t *testing.T) {
|
|
||||||
var ls Labels
|
|
||||||
ls.keys = []string{"type", "server"}
|
|
||||||
ls.vals = []string{"noop", "http"}
|
|
||||||
|
|
||||||
var nls Labels
|
|
||||||
nls.keys = []string{"register"}
|
|
||||||
nls.vals = []string{"gossip"}
|
|
||||||
ls = ls.Append(nls)
|
|
||||||
|
|
||||||
ls.Sort()
|
|
||||||
|
|
||||||
if ls.keys[0] != "register" || ls.vals[0] != "gossip" {
|
|
||||||
t.Fatalf("append error: %v", ls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIterator(t *testing.T) {
|
|
||||||
var ls Labels
|
|
||||||
ls.keys = []string{"type", "server", "register"}
|
|
||||||
ls.vals = []string{"noop", "http", "gossip"}
|
|
||||||
|
|
||||||
iter := ls.Iter()
|
|
||||||
var k, v string
|
|
||||||
cnt := 0
|
|
||||||
for iter.Next(&k, &v) {
|
|
||||||
if cnt == 1 && (k != "server" || v != "http") {
|
|
||||||
t.Fatalf("iter error: %s != %s || %s != %s", k, "server", v, "http")
|
|
||||||
}
|
|
||||||
cnt++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -7,8 +7,7 @@ import (
|
|||||||
|
|
||||||
// NoopMeter is an noop implementation of Meter
|
// NoopMeter is an noop implementation of Meter
|
||||||
type noopMeter struct {
|
type noopMeter struct {
|
||||||
opts Options
|
opts Options
|
||||||
labels Labels
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMeter returns a configured noop reporter:
|
// NewMeter returns a configured noop reporter:
|
||||||
@@ -39,27 +38,47 @@ func (r *noopMeter) Counter(name string, opts ...Option) Counter {
|
|||||||
|
|
||||||
// FloatCounter implements the Meter interface
|
// FloatCounter implements the Meter interface
|
||||||
func (r *noopMeter) FloatCounter(name string, opts ...Option) FloatCounter {
|
func (r *noopMeter) FloatCounter(name string, opts ...Option) FloatCounter {
|
||||||
return &noopFloatCounter{}
|
options := Options{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return &noopFloatCounter{labels: options.Labels}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gauge implements the Meter interface
|
// Gauge implements the Meter interface
|
||||||
func (r *noopMeter) Gauge(name string, f func() float64, opts ...Option) Gauge {
|
func (r *noopMeter) Gauge(name string, f func() float64, opts ...Option) Gauge {
|
||||||
return &noopGauge{}
|
options := Options{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return &noopGauge{labels: options.Labels}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Summary implements the Meter interface
|
// Summary implements the Meter interface
|
||||||
func (r *noopMeter) Summary(name string, opts ...Option) Summary {
|
func (r *noopMeter) Summary(name string, opts ...Option) Summary {
|
||||||
return &noopSummary{}
|
options := Options{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return &noopSummary{labels: options.Labels}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SummaryExt implements the Meter interface
|
// SummaryExt implements the Meter interface
|
||||||
func (r *noopMeter) SummaryExt(name string, window time.Duration, quantiles []float64, opts ...Option) Summary {
|
func (r *noopMeter) SummaryExt(name string, window time.Duration, quantiles []float64, opts ...Option) Summary {
|
||||||
return &noopSummary{}
|
options := Options{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return &noopSummary{labels: options.Labels}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Histogram implements the Meter interface
|
// Histogram implements the Meter interface
|
||||||
func (r *noopMeter) Histogram(name string, opts ...Option) Histogram {
|
func (r *noopMeter) Histogram(name string, opts ...Option) Histogram {
|
||||||
return &noopHistogram{}
|
options := Options{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return &noopHistogram{labels: options.Labels}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set implements the Meter interface
|
// Set implements the Meter interface
|
||||||
@@ -73,7 +92,7 @@ func (r *noopMeter) Set(opts ...Option) Meter {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopMeter) Write(w io.Writer, withProcessMetrics bool) error {
|
func (r *noopMeter) Write(w io.Writer, opts ...Option) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +107,7 @@ func (r *noopMeter) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type noopCounter struct {
|
type noopCounter struct {
|
||||||
labels Labels
|
labels []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopCounter) Add(int) {
|
func (r *noopCounter) Add(int) {
|
||||||
@@ -111,7 +130,9 @@ func (r *noopCounter) Set(uint64) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopFloatCounter struct{}
|
type noopFloatCounter struct {
|
||||||
|
labels []string
|
||||||
|
}
|
||||||
|
|
||||||
func (r *noopFloatCounter) Add(float64) {
|
func (r *noopFloatCounter) Add(float64) {
|
||||||
|
|
||||||
@@ -129,13 +150,17 @@ func (r *noopFloatCounter) Sub(float64) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopGauge struct{}
|
type noopGauge struct {
|
||||||
|
labels []string
|
||||||
|
}
|
||||||
|
|
||||||
func (r *noopGauge) Get() float64 {
|
func (r *noopGauge) Get() float64 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopSummary struct{}
|
type noopSummary struct {
|
||||||
|
labels []string
|
||||||
|
}
|
||||||
|
|
||||||
func (r *noopSummary) Update(float64) {
|
func (r *noopSummary) Update(float64) {
|
||||||
|
|
||||||
@@ -145,7 +170,9 @@ func (r *noopSummary) UpdateDuration(time.Time) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopHistogram struct{}
|
type noopHistogram struct {
|
||||||
|
labels []string
|
||||||
|
}
|
||||||
|
|
||||||
func (r *noopHistogram) Reset() {
|
func (r *noopHistogram) Reset() {
|
||||||
|
|
||||||
|
@@ -11,15 +11,26 @@ type Option func(*Options)
|
|||||||
|
|
||||||
// Options for metrics implementations:
|
// Options for metrics implementations:
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// Logger used for logging
|
||||||
|
Logger logger.Logger
|
||||||
|
// Context holds external options
|
||||||
|
Context context.Context
|
||||||
|
// Name holds the meter name
|
||||||
|
Name string
|
||||||
|
// Address holds the address that serves metrics
|
||||||
Address string
|
Address string
|
||||||
Path string
|
// Path holds the path for metrics
|
||||||
Labels Labels
|
Path string
|
||||||
//TimingObjectives map[float64]float64
|
// MetricPrefix holds the prefix for all metrics
|
||||||
Logger logger.Logger
|
|
||||||
Context context.Context
|
|
||||||
MetricPrefix string
|
MetricPrefix string
|
||||||
LabelPrefix string
|
// LabelPrefix holds the prefix for all labels
|
||||||
|
LabelPrefix string
|
||||||
|
// Labels holds the default labels
|
||||||
|
Labels []string
|
||||||
|
// WriteProcessMetrics flag to write process metrics
|
||||||
|
WriteProcessMetrics bool
|
||||||
|
// WriteFDMetrics flag to write fd metrics
|
||||||
|
WriteFDMetrics bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions prepares a set of options:
|
// NewOptions prepares a set of options:
|
||||||
@@ -61,15 +72,6 @@ func Address(value string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Labels be added to every metric
|
|
||||||
func Labels(labels []string) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Labels = labels
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TimingObjectives defines the desired spread of statistics for histogram / timing metrics:
|
// TimingObjectives defines the desired spread of statistics for histogram / timing metrics:
|
||||||
func TimingObjectives(value map[float64]float64) Option {
|
func TimingObjectives(value map[float64]float64) Option {
|
||||||
@@ -86,11 +88,9 @@ func Logger(l logger.Logger) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Label sets the label
|
func Labels(ls ...string) Option {
|
||||||
func Label(key, val string) Option {
|
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Labels.keys = append(o.Labels.keys, key)
|
o.Labels = ls
|
||||||
o.Labels.vals = append(o.Labels.vals, val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,3 +100,17 @@ func Name(n string) Option {
|
|||||||
o.Name = n
|
o.Name = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteProcessMetrics enable process metrics output for write
|
||||||
|
func WriteProcessMetrics(b bool) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.WriteProcessMetrics = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFDMetrics enable fd metrics output for write
|
||||||
|
func WriteFDMetrics(b bool) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.WriteFDMetrics = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
// +build ignore
|
|
||||||
|
|
||||||
package wrapper
|
package wrapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -12,30 +10,64 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/server"
|
"github.com/unistack-org/micro/v3/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ClientRequestDurationSeconds = "client_request_duration_seconds"
|
||||||
|
ClientRequestLatencyMicroseconds = "client_request_latency_microseconds"
|
||||||
|
ClientRequestTotal = "client_request_total"
|
||||||
|
ServerRequestDurationSeconds = "server_request_duration_seconds"
|
||||||
|
ServerRequestLatencyMicroseconds = "server_request_latency_microseconds"
|
||||||
|
ServerRequestTotal = "server_request_total"
|
||||||
|
PublishMessageDurationSeconds = "publish_message_duration_seconds"
|
||||||
|
PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds"
|
||||||
|
PublishMessageTotal = "publish_message_total"
|
||||||
|
SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds"
|
||||||
|
SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds"
|
||||||
|
SubscribeMessageTotal = "subscribe_message_total"
|
||||||
|
|
||||||
|
labelSuccess = "success"
|
||||||
|
labelFailure = "failure"
|
||||||
|
labelStatus = "status"
|
||||||
|
labelEndpoint = "endpoint"
|
||||||
|
|
||||||
|
// DefaultSkipEndpoints contains list of endpoints that not evaluted by wrapper
|
||||||
|
DefaultSkipEndpoints = []string{"Meter.Metrics"}
|
||||||
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Meter meter.Meter
|
Meter meter.Meter
|
||||||
Name string
|
lopts []meter.Option
|
||||||
Version string
|
SkipEndpoints []string
|
||||||
ID string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option func(*Options)
|
type Option func(*Options)
|
||||||
|
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{
|
||||||
|
Meter: meter.DefaultMeter,
|
||||||
|
lopts: make([]meter.Option, 0, 5),
|
||||||
|
SkipEndpoints: DefaultSkipEndpoints,
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
func ServiceName(name string) Option {
|
func ServiceName(name string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Name = name
|
o.lopts = append(o.lopts, meter.Labels("name", name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServiceVersion(version string) Option {
|
func ServiceVersion(version string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Version = version
|
o.lopts = append(o.lopts, meter.Labels("version", version))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServiceID(id string) Option {
|
func ServiceID(id string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.ID = id
|
o.lopts = append(o.lopts, meter.Labels("id", id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,187 +77,208 @@ func Meter(m meter.Meter) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SkipEndoints(eps ...string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.SkipEndpoints = append(o.SkipEndpoints, eps...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type wrapper struct {
|
type wrapper struct {
|
||||||
options Options
|
|
||||||
callFunc client.CallFunc
|
|
||||||
client.Client
|
client.Client
|
||||||
|
callFunc client.CallFunc
|
||||||
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientWrapper(opts ...Option) client.Wrapper {
|
func NewClientWrapper(opts ...Option) client.Wrapper {
|
||||||
return func(c client.Client) client.Client {
|
return func(c client.Client) client.Client {
|
||||||
handler := &wrapper{
|
handler := &wrapper{
|
||||||
labels: labels,
|
opts: NewOptions(opts...),
|
||||||
Client: c,
|
Client: c,
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCallWrapper(opts ...Option) client.CallWrapper {
|
func NewCallWrapper(opts ...Option) client.CallWrapper {
|
||||||
labels := getLabels(opts...)
|
|
||||||
|
|
||||||
return func(fn client.CallFunc) client.CallFunc {
|
return func(fn client.CallFunc) client.CallFunc {
|
||||||
handler := &wrapper{
|
handler := &wrapper{
|
||||||
labels: labels,
|
opts: NewOptions(opts...),
|
||||||
callFunc: fn,
|
callFunc: fn,
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.CallFunc
|
return handler.CallFunc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wrapper) CallFunc(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
func (w *wrapper) CallFunc(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||||
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
timeCounterSummary := metrics.GetOrCreateSummary(getName("client_request_latency_microseconds", wlabels))
|
return w.callFunc(ctx, addr, req, rsp, opts)
|
||||||
timeCounterHistogram := metrics.GetOrCreateSummary(getName("client_request_duration_seconds", wlabels))
|
}
|
||||||
|
}
|
||||||
ts := time.Now()
|
ts := time.Now()
|
||||||
err := w.callFunc(ctx, addr, req, rsp, opts)
|
err := w.callFunc(ctx, addr, req, rsp, opts)
|
||||||
te := time.Since(ts)
|
te := time.Since(ts)
|
||||||
|
|
||||||
timeCounterSummary.Update(float64(te.Seconds()))
|
lopts := w.opts.lopts
|
||||||
timeCounterHistogram.Update(te.Seconds())
|
lopts = append(lopts, meter.Labels(labelEndpoint, endpoint))
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
w.opts.Meter.Histogram(ClientRequestDurationSeconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelSuccess))
|
||||||
} else {
|
} else {
|
||||||
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelFailure))
|
||||||
}
|
}
|
||||||
|
w.opts.Meter.Counter(ClientRequestTotal, lopts...).Inc()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
func (w *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||||
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
timeCounterSummary := metrics.GetOrCreateSummary(getName("client_request_latency_microseconds", wlabels))
|
return w.Client.Call(ctx, req, rsp, opts...)
|
||||||
timeCounterHistogram := metrics.GetOrCreateSummary(getName("client_request_duration_seconds", wlabels))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ts := time.Now()
|
ts := time.Now()
|
||||||
err := w.Client.Call(ctx, req, rsp, opts...)
|
err := w.Client.Call(ctx, req, rsp, opts...)
|
||||||
te := time.Since(ts)
|
te := time.Since(ts)
|
||||||
|
|
||||||
timeCounterSummary.Update(float64(te.Seconds()))
|
lopts := w.opts.lopts
|
||||||
timeCounterHistogram.Update(te.Seconds())
|
lopts = append(lopts, meter.Labels(labelEndpoint, endpoint))
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
w.opts.Meter.Histogram(ClientRequestDurationSeconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelSuccess))
|
||||||
} else {
|
} else {
|
||||||
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelFailure))
|
||||||
}
|
}
|
||||||
|
w.opts.Meter.Counter(ClientRequestTotal, lopts...).Inc()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||||
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
timeCounterSummary := metrics.GetOrCreateSummary(getName("client_request_latency_microseconds", wlabels))
|
return w.Client.Stream(ctx, req, opts...)
|
||||||
timeCounterHistogram := metrics.GetOrCreateSummary(getName("client_request_duration_seconds", wlabels))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ts := time.Now()
|
ts := time.Now()
|
||||||
stream, err := w.Client.Stream(ctx, req, opts...)
|
stream, err := w.Client.Stream(ctx, req, opts...)
|
||||||
te := time.Since(ts)
|
te := time.Since(ts)
|
||||||
|
|
||||||
timeCounterSummary.Update(float64(te.Seconds()))
|
lopts := w.opts.lopts
|
||||||
timeCounterHistogram.Update(te.Seconds())
|
lopts = append(lopts, meter.Labels(labelEndpoint, endpoint))
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
w.opts.Meter.Histogram(ClientRequestDurationSeconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelSuccess))
|
||||||
} else {
|
} else {
|
||||||
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelFailure))
|
||||||
}
|
}
|
||||||
|
w.opts.Meter.Counter(ClientRequestTotal, lopts...).Inc()
|
||||||
|
|
||||||
return stream, err
|
return stream, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wrapper) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
func (w *wrapper) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
||||||
endpoint := p.Topic()
|
endpoint := p.Topic()
|
||||||
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
|
|
||||||
|
|
||||||
timeCounterSummary := metrics.GetOrCreateSummary(getName("publish_message_latency_microseconds", wlabels))
|
|
||||||
timeCounterHistogram := metrics.GetOrCreateSummary(getName("publish_message_duration_seconds", wlabels))
|
|
||||||
|
|
||||||
ts := time.Now()
|
ts := time.Now()
|
||||||
err := w.Client.Publish(ctx, p, opts...)
|
err := w.Client.Publish(ctx, p, opts...)
|
||||||
te := time.Since(ts)
|
te := time.Since(ts)
|
||||||
|
|
||||||
timeCounterSummary.Update(float64(te.Seconds()))
|
lopts := w.opts.lopts
|
||||||
timeCounterHistogram.Update(te.Seconds())
|
lopts = append(lopts, meter.Labels(labelEndpoint, endpoint))
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(PublishMessageLatencyMicroseconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
w.opts.Meter.Histogram(PublishMessageDurationSeconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
metrics.GetOrCreateCounter(getName("publish_message_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelSuccess))
|
||||||
} else {
|
} else {
|
||||||
metrics.GetOrCreateCounter(getName("publish_message_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelFailure))
|
||||||
}
|
}
|
||||||
|
w.opts.Meter.Counter(PublishMessageTotal, lopts...).Inc()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandlerWrapper(opts ...Option) server.HandlerWrapper {
|
func NewHandlerWrapper(opts ...Option) server.HandlerWrapper {
|
||||||
labels := getLabels(opts...)
|
|
||||||
|
|
||||||
handler := &wrapper{
|
handler := &wrapper{
|
||||||
labels: labels,
|
opts: NewOptions(opts...),
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.HandlerFunc
|
return handler.HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wrapper) HandlerFunc(fn server.HandlerFunc) server.HandlerFunc {
|
func (w *wrapper) HandlerFunc(fn server.HandlerFunc) server.HandlerFunc {
|
||||||
return func(ctx context.Context, req server.Request, rsp interface{}) error {
|
return func(ctx context.Context, req server.Request, rsp interface{}) error {
|
||||||
endpoint := req.Endpoint()
|
endpoint := req.Endpoint()
|
||||||
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
timeCounterSummary := metrics.GetOrCreateSummary(getName("server_request_latency_microseconds", wlabels))
|
return fn(ctx, req, rsp)
|
||||||
timeCounterHistogram := metrics.GetOrCreateSummary(getName("server_request_duration_seconds", wlabels))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ts := time.Now()
|
ts := time.Now()
|
||||||
err := fn(ctx, req, rsp)
|
err := fn(ctx, req, rsp)
|
||||||
te := time.Since(ts)
|
te := time.Since(ts)
|
||||||
|
|
||||||
timeCounterSummary.Update(float64(te.Seconds()))
|
lopts := w.opts.lopts
|
||||||
timeCounterHistogram.Update(te.Seconds())
|
lopts = append(lopts, meter.Labels(labelEndpoint, endpoint))
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(ServerRequestLatencyMicroseconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
w.opts.Meter.Histogram(ServerRequestDurationSeconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
metrics.GetOrCreateCounter(getName("server_request_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelSuccess))
|
||||||
} else {
|
} else {
|
||||||
metrics.GetOrCreateCounter(getName("server_request_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelFailure))
|
||||||
}
|
}
|
||||||
|
w.opts.Meter.Counter(ServerRequestTotal, lopts...).Inc()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
|
func NewSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
|
||||||
labels := getLabels(opts...)
|
|
||||||
|
|
||||||
handler := &wrapper{
|
handler := &wrapper{
|
||||||
labels: labels,
|
opts: NewOptions(opts...),
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.SubscriberFunc
|
return handler.SubscriberFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wrapper) SubscriberFunc(fn server.SubscriberFunc) server.SubscriberFunc {
|
func (w *wrapper) SubscriberFunc(fn server.SubscriberFunc) server.SubscriberFunc {
|
||||||
return func(ctx context.Context, msg server.Message) error {
|
return func(ctx context.Context, msg server.Message) error {
|
||||||
endpoint := msg.Topic()
|
endpoint := msg.Topic()
|
||||||
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
|
|
||||||
|
|
||||||
timeCounterSummary := metrics.GetOrCreateSummary(getName("subscribe_message_latency_microseconds", wlabels))
|
|
||||||
timeCounterHistogram := metrics.GetOrCreateSummary(getName("subscribe_message_duration_seconds", wlabels))
|
|
||||||
|
|
||||||
ts := time.Now()
|
ts := time.Now()
|
||||||
err := fn(ctx, msg)
|
err := fn(ctx, msg)
|
||||||
te := time.Since(ts)
|
te := time.Since(ts)
|
||||||
|
|
||||||
timeCounterSummary.Update(float64(te.Seconds()))
|
lopts := w.opts.lopts
|
||||||
timeCounterHistogram.Update(te.Seconds())
|
lopts = append(lopts, meter.Labels(labelEndpoint, endpoint))
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(SubscribeMessageLatencyMicroseconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
w.opts.Meter.Histogram(SubscribeMessageDurationSeconds, lopts...).Update(float64(te.Seconds()))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
metrics.GetOrCreateCounter(getName("subscribe_message_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelSuccess))
|
||||||
} else {
|
} else {
|
||||||
metrics.GetOrCreateCounter(getName("subscribe_message_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
|
lopts = append(lopts, meter.Labels(labelStatus, labelFailure))
|
||||||
}
|
}
|
||||||
|
w.opts.Meter.Counter(SubscribeMessageTotal, lopts...).Inc()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Package model is an interface for data modelling
|
// Package model is an interface for data modelling
|
||||||
package model
|
package model
|
||||||
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Package model is an interface for data modelling
|
// Package model is an interface for data modelling
|
||||||
package model
|
package model
|
||||||
|
|
||||||
|
@@ -15,6 +15,18 @@ type Option func(*Options)
|
|||||||
|
|
||||||
// Options configure network
|
// Options configure network
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
// Router used for routing
|
||||||
|
Router router.Router
|
||||||
|
// Proxy holds the proxy
|
||||||
|
Proxy proxy.Proxy
|
||||||
|
// Logger used for logging
|
||||||
|
Logger logger.Logger
|
||||||
|
// Meter used for metrics
|
||||||
|
Meter meter.Meter
|
||||||
|
// Tracer used for tracing
|
||||||
|
Tracer tracer.Tracer
|
||||||
|
// Tunnel used for transfer data
|
||||||
|
Tunnel tunnel.Tunnel
|
||||||
// Id of the node
|
// Id of the node
|
||||||
Id string
|
Id string
|
||||||
// Name of the network
|
// Name of the network
|
||||||
@@ -25,18 +37,6 @@ type Options struct {
|
|||||||
Advertise string
|
Advertise string
|
||||||
// Nodes is a list of nodes to connect to
|
// Nodes is a list of nodes to connect to
|
||||||
Nodes []string
|
Nodes []string
|
||||||
// Tunnel is network tunnel
|
|
||||||
Tunnel tunnel.Tunnel
|
|
||||||
// Router is network router
|
|
||||||
Router router.Router
|
|
||||||
// Proxy is network proxy
|
|
||||||
Proxy proxy.Proxy
|
|
||||||
// Logger
|
|
||||||
Logger logger.Logger
|
|
||||||
// Meter
|
|
||||||
Meter meter.Meter
|
|
||||||
// Tracer
|
|
||||||
Tracer tracer.Tracer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Id sets the id of the network node
|
// Id sets the id of the network node
|
||||||
|
258
network/transport/memory.go
Normal file
258
network/transport/memory.go
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
maddr "github.com/unistack-org/micro/v3/util/addr"
|
||||||
|
mnet "github.com/unistack-org/micro/v3/util/net"
|
||||||
|
"github.com/unistack-org/micro/v3/util/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
type memorySocket struct {
|
||||||
|
ctx context.Context
|
||||||
|
recv chan *Message
|
||||||
|
exit chan bool
|
||||||
|
lexit chan bool
|
||||||
|
send chan *Message
|
||||||
|
local string
|
||||||
|
remote string
|
||||||
|
timeout time.Duration
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type memoryClient struct {
|
||||||
|
*memorySocket
|
||||||
|
opts DialOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
type memoryListener struct {
|
||||||
|
topts Options
|
||||||
|
ctx context.Context
|
||||||
|
lopts ListenOptions
|
||||||
|
exit chan bool
|
||||||
|
conn chan *memorySocket
|
||||||
|
addr string
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type memoryTransport struct {
|
||||||
|
opts Options
|
||||||
|
listeners map[string]*memoryListener
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *memorySocket) Recv(m *Message) error {
|
||||||
|
ms.RLock()
|
||||||
|
defer ms.RUnlock()
|
||||||
|
|
||||||
|
ctx := ms.ctx
|
||||||
|
if ms.timeout > 0 {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithTimeout(ms.ctx, ms.timeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-ms.exit:
|
||||||
|
return errors.New("connection closed")
|
||||||
|
case <-ms.lexit:
|
||||||
|
return errors.New("server connection closed")
|
||||||
|
case cm := <-ms.recv:
|
||||||
|
*m = *cm
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *memorySocket) Local() string {
|
||||||
|
return ms.local
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *memorySocket) Remote() string {
|
||||||
|
return ms.remote
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *memorySocket) Send(m *Message) error {
|
||||||
|
ms.RLock()
|
||||||
|
defer ms.RUnlock()
|
||||||
|
|
||||||
|
ctx := ms.ctx
|
||||||
|
if ms.timeout > 0 {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithTimeout(ms.ctx, ms.timeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-ms.exit:
|
||||||
|
return errors.New("connection closed")
|
||||||
|
case <-ms.lexit:
|
||||||
|
return errors.New("server connection closed")
|
||||||
|
case ms.send <- m:
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *memorySocket) Close() error {
|
||||||
|
ms.Lock()
|
||||||
|
defer ms.Unlock()
|
||||||
|
select {
|
||||||
|
case <-ms.exit:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
close(ms.exit)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryListener) Addr() string {
|
||||||
|
return m.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryListener) Close() error {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
select {
|
||||||
|
case <-m.exit:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
close(m.exit)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryListener) Accept(fn func(Socket)) error {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-m.exit:
|
||||||
|
return nil
|
||||||
|
case c := <-m.conn:
|
||||||
|
go fn(&memorySocket{
|
||||||
|
lexit: c.lexit,
|
||||||
|
exit: c.exit,
|
||||||
|
send: c.recv,
|
||||||
|
recv: c.send,
|
||||||
|
local: c.Remote(),
|
||||||
|
remote: c.Local(),
|
||||||
|
timeout: m.topts.Timeout,
|
||||||
|
ctx: m.topts.Context,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryTransport) Dial(ctx context.Context, addr string, opts ...DialOption) (Client, error) {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
|
||||||
|
listener, ok := m.listeners[addr]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("could not dial " + addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
options := NewDialOptions(opts...)
|
||||||
|
|
||||||
|
client := &memoryClient{
|
||||||
|
&memorySocket{
|
||||||
|
send: make(chan *Message),
|
||||||
|
recv: make(chan *Message),
|
||||||
|
exit: make(chan bool),
|
||||||
|
lexit: listener.exit,
|
||||||
|
local: addr,
|
||||||
|
remote: addr,
|
||||||
|
timeout: m.opts.Timeout,
|
||||||
|
ctx: m.opts.Context,
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
}
|
||||||
|
|
||||||
|
// pseudo connect
|
||||||
|
select {
|
||||||
|
case <-listener.exit:
|
||||||
|
return nil, errors.New("connection error")
|
||||||
|
case listener.conn <- client.memorySocket:
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryTransport) Listen(ctx context.Context, addr string, opts ...ListenOption) (Listener, error) {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
options := NewListenOptions(opts...)
|
||||||
|
|
||||||
|
host, port, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err = maddr.Extract(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if zero port then randomly assign one
|
||||||
|
if len(port) > 0 && port == "0" {
|
||||||
|
var rng rand.Rand
|
||||||
|
i := rng.Intn(20000)
|
||||||
|
port = fmt.Sprintf("%d", 10000+i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set addr with port
|
||||||
|
addr = mnet.HostPort(addr, port)
|
||||||
|
|
||||||
|
if _, ok := m.listeners[addr]; ok {
|
||||||
|
return nil, errors.New("already listening on " + addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
listener := &memoryListener{
|
||||||
|
lopts: options,
|
||||||
|
topts: m.opts,
|
||||||
|
addr: addr,
|
||||||
|
conn: make(chan *memorySocket),
|
||||||
|
exit: make(chan bool),
|
||||||
|
ctx: m.opts.Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
m.listeners[addr] = listener
|
||||||
|
|
||||||
|
return listener, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryTransport) Init(opts ...Option) error {
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&m.opts)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryTransport) Options() Options {
|
||||||
|
return m.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryTransport) String() string {
|
||||||
|
return "memory"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryTransport) Name() string {
|
||||||
|
return m.opts.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTransport returns new memory transport with options
|
||||||
|
func NewTransport(opts ...Option) Transport {
|
||||||
|
options := NewOptions(opts...)
|
||||||
|
|
||||||
|
return &memoryTransport{
|
||||||
|
opts: options,
|
||||||
|
listeners: make(map[string]*memoryListener),
|
||||||
|
}
|
||||||
|
}
|
93
network/transport/memory_test.go
Normal file
93
network/transport/memory_test.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMemoryTransport(t *testing.T) {
|
||||||
|
tr := NewTransport()
|
||||||
|
ctx := context.Background()
|
||||||
|
// bind / listen
|
||||||
|
l, err := tr.Listen(ctx, "127.0.0.1:8080")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error listening %v", err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
// accept
|
||||||
|
go func() {
|
||||||
|
if err := l.Accept(func(sock Socket) {
|
||||||
|
for {
|
||||||
|
var m Message
|
||||||
|
if err := sock.Recv(&m); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(os.Getenv("INTEGRATION_TESTS")) == 0 {
|
||||||
|
t.Logf("Server Received %s", string(m.Body))
|
||||||
|
}
|
||||||
|
if err := sock.Send(&Message{
|
||||||
|
Body: []byte(`pong`),
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("Unexpected error accepting %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// dial
|
||||||
|
c, err := tr.Dial(ctx, "127.0.0.1:8080")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error dialing %v", err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
// send <=> receive
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
if err := c.Send(&Message{
|
||||||
|
Body: []byte(`ping`),
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var m Message
|
||||||
|
if err := c.Recv(&m); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(os.Getenv("INTEGRATION_TESTS")) == 0 {
|
||||||
|
t.Logf("Client Received %s", string(m.Body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListener(t *testing.T) {
|
||||||
|
tr := NewTransport()
|
||||||
|
ctx := context.Background()
|
||||||
|
// bind / listen on random port
|
||||||
|
l, err := tr.Listen(ctx, ":0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error listening %v", err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
// try again
|
||||||
|
l2, err := tr.Listen(ctx, ":0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error listening %v", err)
|
||||||
|
}
|
||||||
|
defer l2.Close()
|
||||||
|
|
||||||
|
// now make sure it still fails
|
||||||
|
l3, err := tr.Listen(ctx, ":8080")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error listening %v", err)
|
||||||
|
}
|
||||||
|
defer l3.Close()
|
||||||
|
|
||||||
|
if _, err := tr.Listen(ctx, ":8080"); err == nil {
|
||||||
|
t.Fatal("Expected error binding to :8080 got nil")
|
||||||
|
}
|
||||||
|
}
|
@@ -1,77 +0,0 @@
|
|||||||
package transport
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
type noopTransport struct {
|
|
||||||
opts Options
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTransport creates new noop transport
|
|
||||||
func NewTransport(opts ...Option) Transport {
|
|
||||||
return &noopTransport{opts: NewOptions(opts...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *noopTransport) Init(opts ...Option) error {
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&t.opts)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *noopTransport) Options() Options {
|
|
||||||
return t.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *noopTransport) Dial(ctx context.Context, addr string, opts ...DialOption) (Client, error) {
|
|
||||||
options := NewDialOptions(opts...)
|
|
||||||
return &noopClient{opts: options}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *noopTransport) Listen(ctx context.Context, addr string, opts ...ListenOption) (Listener, error) {
|
|
||||||
options := NewListenOptions(opts...)
|
|
||||||
return &noopListener{opts: options}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *noopTransport) String() string {
|
|
||||||
return "noop"
|
|
||||||
}
|
|
||||||
|
|
||||||
type noopClient struct {
|
|
||||||
opts DialOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *noopClient) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *noopClient) Local() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *noopClient) Remote() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *noopClient) Recv(*Message) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *noopClient) Send(*Message) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type noopListener struct {
|
|
||||||
opts ListenOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopListener) Addr() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopListener) Accept(fn func(Socket)) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopListener) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -11,31 +11,26 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/tracer"
|
"github.com/unistack-org/micro/v3/tracer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Options struct holds the transport options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// Meter used for metrics
|
||||||
// Addrs is the list of intermediary addresses to connect to
|
|
||||||
Addrs []string
|
|
||||||
// Codec is the codec interface to use where headers are not supported
|
|
||||||
// by the transport and the entire payload must be encoded
|
|
||||||
Codec codec.Codec
|
|
||||||
// Secure tells the transport to secure the connection.
|
|
||||||
// In the case TLSConfig is not specified best effort self-signed
|
|
||||||
// certs should be used
|
|
||||||
Secure bool
|
|
||||||
// TLSConfig to secure the connection. The assumption is that this
|
|
||||||
// is mTLS keypair
|
|
||||||
TLSConfig *tls.Config
|
|
||||||
// Timeout sets the timeout for Send/Recv
|
|
||||||
Timeout time.Duration
|
|
||||||
// Logger sets the logger
|
|
||||||
Logger logger.Logger
|
|
||||||
// Meter sets the meter
|
|
||||||
Meter meter.Meter
|
Meter meter.Meter
|
||||||
// Tracer
|
// Tracer used for tracing
|
||||||
Tracer tracer.Tracer
|
Tracer tracer.Tracer
|
||||||
// Other options for implementations of the interface
|
// Codec used for marshal/unmarshal messages
|
||||||
// can be stored in a context
|
Codec codec.Codec
|
||||||
|
// Logger used for logging
|
||||||
|
Logger logger.Logger
|
||||||
|
// Context holds external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// TLSConfig holds tls.TLSConfig options
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
// Name holds the transport name
|
||||||
|
Name string
|
||||||
|
// Addrs holds the transport addrs
|
||||||
|
Addrs []string
|
||||||
|
// Timeout holds the timeout
|
||||||
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions returns new options
|
// NewOptions returns new options
|
||||||
@@ -56,18 +51,12 @@ func NewOptions(opts ...Option) Options {
|
|||||||
|
|
||||||
// DialOptions struct
|
// DialOptions struct
|
||||||
type DialOptions struct {
|
type DialOptions struct {
|
||||||
// Tells the transport this is a streaming connection with
|
// Context holds the external options
|
||||||
// multiple calls to send/recv and that send may not even be called
|
|
||||||
Stream bool
|
|
||||||
// Timeout for dialing
|
|
||||||
Timeout time.Duration
|
|
||||||
|
|
||||||
// TODO: add tls options when dialling
|
|
||||||
// Currently set in global options
|
|
||||||
|
|
||||||
// Other options for implementations of the interface
|
|
||||||
// can be stored in a context
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Timeout holds the timeout
|
||||||
|
Timeout time.Duration
|
||||||
|
// Stream flag
|
||||||
|
Stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDialOptions returns new DialOptions
|
// NewDialOptions returns new DialOptions
|
||||||
@@ -88,10 +77,10 @@ func NewDialOptions(opts ...DialOption) DialOptions {
|
|||||||
type ListenOptions struct {
|
type ListenOptions struct {
|
||||||
// TODO: add tls options when listening
|
// TODO: add tls options when listening
|
||||||
// Currently set in global options
|
// Currently set in global options
|
||||||
|
// Context holds the external options
|
||||||
// Other options for implementations of the interface
|
|
||||||
// can be stored in a context
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// TLSConfig holds the *tls.Config options
|
||||||
|
TLSConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewListenOptions returns new ListenOptions
|
// NewListenOptions returns new ListenOptions
|
||||||
|
@@ -11,7 +11,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
// DefaultTransport is the global default transport
|
// DefaultTransport is the global default transport
|
||||||
DefaultTransport Transport = NewTransport()
|
DefaultTransport Transport = NewTransport()
|
||||||
// Default dial timeout
|
// DefaultDialTimeout the default dial timeout
|
||||||
DefaultDialTimeout = time.Second * 5
|
DefaultDialTimeout = time.Second * 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -17,17 +17,16 @@ type tunBroker struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type tunSubscriber struct {
|
type tunSubscriber struct {
|
||||||
topic string
|
opts broker.SubscribeOptions
|
||||||
handler broker.Handler
|
|
||||||
opts broker.SubscribeOptions
|
|
||||||
|
|
||||||
closed chan bool
|
|
||||||
listener tunnel.Listener
|
listener tunnel.Listener
|
||||||
|
handler broker.Handler
|
||||||
|
closed chan bool
|
||||||
|
topic string
|
||||||
}
|
}
|
||||||
|
|
||||||
type tunEvent struct {
|
type tunEvent struct {
|
||||||
topic string
|
|
||||||
message *broker.Message
|
message *broker.Message
|
||||||
|
topic string
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to access tunnel from options context
|
// used to access tunnel from options context
|
||||||
@@ -175,6 +174,7 @@ func (t *tunEvent) Error() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBroker returns new tunnel broker
|
||||||
func NewBroker(opts ...broker.Option) (broker.Broker, error) {
|
func NewBroker(opts ...broker.Option) (broker.Broker, error) {
|
||||||
options := broker.NewOptions(opts...)
|
options := broker.NewOptions(opts...)
|
||||||
|
|
||||||
|
@@ -13,32 +13,33 @@ import (
|
|||||||
var (
|
var (
|
||||||
// DefaultAddress is default tunnel bind address
|
// DefaultAddress is default tunnel bind address
|
||||||
DefaultAddress = ":0"
|
DefaultAddress = ":0"
|
||||||
// The shared default token
|
// DefaultToken the shared default token
|
||||||
DefaultToken = "go.micro.tunnel"
|
DefaultToken = "go.micro.tunnel"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Option func
|
// Option func signature
|
||||||
type Option func(*Options)
|
type Option func(*Options)
|
||||||
|
|
||||||
// Options provides network configuration options
|
// Options provides network configuration options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// Logger used for logging
|
||||||
// Id is tunnel id
|
|
||||||
Id string
|
|
||||||
// Address is tunnel address
|
|
||||||
Address string
|
|
||||||
// Nodes are remote nodes
|
|
||||||
Nodes []string
|
|
||||||
// The shared auth token
|
|
||||||
Token string
|
|
||||||
// Transport listens to incoming connections
|
|
||||||
Transport transport.Transport
|
|
||||||
// Logger
|
|
||||||
Logger logger.Logger
|
Logger logger.Logger
|
||||||
// Meter
|
// Meter used for metrics
|
||||||
Meter meter.Meter
|
Meter meter.Meter
|
||||||
// Tracer
|
// Tracer used for tracing
|
||||||
Tracer tracer.Tracer
|
Tracer tracer.Tracer
|
||||||
|
// Transport used for communication
|
||||||
|
Transport transport.Transport
|
||||||
|
// Token the shared auth token
|
||||||
|
Token string
|
||||||
|
// Name holds the tunnel name
|
||||||
|
Name string
|
||||||
|
// Id holds the tunnel id
|
||||||
|
Id string
|
||||||
|
// Address holds the tunnel address
|
||||||
|
Address string
|
||||||
|
// Nodes holds the tunnel nodes
|
||||||
|
Nodes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialOption func
|
// DialOption func
|
||||||
@@ -61,9 +62,9 @@ type ListenOption func(*ListenOptions)
|
|||||||
|
|
||||||
// ListenOptions provides listen options
|
// ListenOptions provides listen options
|
||||||
type ListenOptions struct {
|
type ListenOptions struct {
|
||||||
// specify mode of the session
|
// Mode specify mode of the session
|
||||||
Mode Mode
|
Mode Mode
|
||||||
// The read timeout
|
// Timeout the read timeout
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +161,7 @@ func DialWait(b bool) DialOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultOptions returns router default options
|
// NewOptions returns router default options with filled values
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Id: uuid.New().String(),
|
Id: uuid.New().String(),
|
||||||
|
@@ -88,7 +88,7 @@ func NewTransport(opts ...transport.Option) transport.Transport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// initialise
|
// initialise
|
||||||
t.Init(opts...)
|
//t.Init(opts...)
|
||||||
|
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
@@ -10,15 +10,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// DefaultTunnel contains default tunnel implementation
|
||||||
DefaultTunnel Tunnel
|
DefaultTunnel Tunnel
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// send over one link
|
// Unicast send over one link
|
||||||
Unicast Mode = iota
|
Unicast Mode = iota
|
||||||
// send to all channel listeners
|
// Multicast send to all channel listeners
|
||||||
Multicast
|
Multicast
|
||||||
// send to all links
|
// Broadcast send to all links
|
||||||
Broadcast
|
Broadcast
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ var (
|
|||||||
ErrLinkNotFound = errors.New("link not found")
|
ErrLinkNotFound = errors.New("link not found")
|
||||||
// ErrLinkDisconnected is returned when a link we attempt to send to is disconnected
|
// ErrLinkDisconnected is returned when a link we attempt to send to is disconnected
|
||||||
ErrLinkDisconnected = errors.New("link not connected")
|
ErrLinkDisconnected = errors.New("link not connected")
|
||||||
// ErrLinkLoppback is returned when attempting to send an outbound message over loopback link
|
// ErrLinkLoopback is returned when attempting to send an outbound message over loopback link
|
||||||
ErrLinkLoopback = errors.New("link is loopback")
|
ErrLinkLoopback = errors.New("link is loopback")
|
||||||
// ErrLinkRemote is returned when attempting to send a loopback message over remote link
|
// ErrLinkRemote is returned when attempting to send a loopback message over remote link
|
||||||
ErrLinkRemote = errors.New("link is remote")
|
ErrLinkRemote = errors.New("link is remote")
|
||||||
@@ -87,7 +88,7 @@ type Link interface {
|
|||||||
transport.Socket
|
transport.Socket
|
||||||
}
|
}
|
||||||
|
|
||||||
// The listener provides similar constructs to the transport.Listener
|
// Listener provides similar constructs to the transport.Listener
|
||||||
type Listener interface {
|
type Listener interface {
|
||||||
Accept() (Session, error)
|
Accept() (Session, error)
|
||||||
Channel() string
|
Channel() string
|
||||||
|
86
options.go
86
options.go
@@ -17,39 +17,50 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/server"
|
"github.com/unistack-org/micro/v3/server"
|
||||||
"github.com/unistack-org/micro/v3/store"
|
"github.com/unistack-org/micro/v3/store"
|
||||||
"github.com/unistack-org/micro/v3/tracer"
|
"github.com/unistack-org/micro/v3/tracer"
|
||||||
// "github.com/unistack-org/micro/v3/debug/profile"
|
// "github.com/unistack-org/micro/v3/profiler"
|
||||||
// "github.com/unistack-org/micro/v3/runtime"
|
// "github.com/unistack-org/micro/v3/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options for micro service
|
// Options for micro service
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// Context holds external options or cancel stuff
|
||||||
Version string
|
|
||||||
Metadata metadata.Metadata
|
|
||||||
|
|
||||||
Auths []auth.Auth
|
|
||||||
Brokers []broker.Broker
|
|
||||||
Loggers []logger.Logger
|
|
||||||
Meters []meter.Meter
|
|
||||||
Configs []config.Config
|
|
||||||
Clients []client.Client
|
|
||||||
Servers []server.Server
|
|
||||||
Stores []store.Store
|
|
||||||
Registers []register.Register
|
|
||||||
Tracers []tracer.Tracer
|
|
||||||
Routers []router.Router
|
|
||||||
// Runtime runtime.Runtime
|
|
||||||
// Profile profile.Profile
|
|
||||||
|
|
||||||
// Before and After funcs
|
|
||||||
BeforeStart []func(context.Context) error
|
|
||||||
BeforeStop []func(context.Context) error
|
|
||||||
AfterStart []func(context.Context) error
|
|
||||||
AfterStop []func(context.Context) error
|
|
||||||
|
|
||||||
// Other options for implementations of the interface
|
|
||||||
// can be stored in a context
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Metadata holds service metadata
|
||||||
|
Metadata metadata.Metadata
|
||||||
|
// Version holds service version
|
||||||
|
Version string
|
||||||
|
// Name holds service name
|
||||||
|
Name string
|
||||||
|
// Brokers holds brokers
|
||||||
|
Brokers []broker.Broker
|
||||||
|
// Loggers holds loggers
|
||||||
|
Loggers []logger.Logger
|
||||||
|
// Meters holds meter
|
||||||
|
Meters []meter.Meter
|
||||||
|
// Configs holds config
|
||||||
|
Configs []config.Config
|
||||||
|
// Clients holds clients
|
||||||
|
Clients []client.Client
|
||||||
|
// Auths holds auths
|
||||||
|
Auths []auth.Auth
|
||||||
|
// Stores holds stores
|
||||||
|
Stores []store.Store
|
||||||
|
// Registers holds registers
|
||||||
|
Registers []register.Register
|
||||||
|
// Tracers holds tracers
|
||||||
|
Tracers []tracer.Tracer
|
||||||
|
// Routers holds routers
|
||||||
|
Routers []router.Router
|
||||||
|
// BeforeStart holds funcs that runs before service starts
|
||||||
|
BeforeStart []func(context.Context) error
|
||||||
|
// BeforeStop holds funcs that runs before service stops
|
||||||
|
BeforeStop []func(context.Context) error
|
||||||
|
// AfterStart holds funcs that runs after service starts
|
||||||
|
AfterStart []func(context.Context) error
|
||||||
|
// AfterStop holds funcs that runs after service stops
|
||||||
|
AfterStop []func(context.Context) error
|
||||||
|
// Servers holds servers
|
||||||
|
Servers []server.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions returns new Options filled with defaults and overrided by provided opts
|
// NewOptions returns new Options filled with defaults and overrided by provided opts
|
||||||
@@ -72,6 +83,7 @@ func NewOptions(opts ...Option) Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
|
//nolint:errcheck
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,14 +132,17 @@ type brokerOptions struct {
|
|||||||
clients []string
|
clients []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BrokerOption func signature
|
||||||
type BrokerOption func(*brokerOptions)
|
type BrokerOption func(*brokerOptions)
|
||||||
|
|
||||||
|
// BrokerClient specifies clients for broker
|
||||||
func BrokerClient(n string) BrokerOption {
|
func BrokerClient(n string) BrokerOption {
|
||||||
return func(o *brokerOptions) {
|
return func(o *brokerOptions) {
|
||||||
o.clients = append(o.clients, n)
|
o.clients = append(o.clients, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BrokerServer specifies servers for broker
|
||||||
func BrokerServer(n string) BrokerOption {
|
func BrokerServer(n string) BrokerOption {
|
||||||
return func(o *brokerOptions) {
|
return func(o *brokerOptions) {
|
||||||
o.servers = append(o.servers, n)
|
o.servers = append(o.servers, n)
|
||||||
@@ -202,6 +217,7 @@ func Stores(s ...store.Store) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Logger set the logger to use
|
// Logger set the logger to use
|
||||||
|
//nolint:gocyclo
|
||||||
func Logger(l logger.Logger, opts ...LoggerOption) Option {
|
func Logger(l logger.Logger, opts ...LoggerOption) Option {
|
||||||
return func(o *Options) error {
|
return func(o *Options) error {
|
||||||
var err error
|
var err error
|
||||||
@@ -281,8 +297,10 @@ func Logger(l logger.Logger, opts ...LoggerOption) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoggerOption func signature
|
||||||
type LoggerOption func(*loggerOptions)
|
type LoggerOption func(*loggerOptions)
|
||||||
|
|
||||||
|
// loggerOptions
|
||||||
type loggerOptions struct {
|
type loggerOptions struct {
|
||||||
servers []string
|
servers []string
|
||||||
clients []string
|
clients []string
|
||||||
@@ -317,6 +335,7 @@ func Meters(m ...meter.Meter) Option {
|
|||||||
|
|
||||||
// Register sets the register for the service
|
// Register sets the register for the service
|
||||||
// and the underlying components
|
// and the underlying components
|
||||||
|
//nolint:gocyclo
|
||||||
func Register(r register.Register, opts ...RegisterOption) Option {
|
func Register(r register.Register, opts ...RegisterOption) Option {
|
||||||
return func(o *Options) error {
|
return func(o *Options) error {
|
||||||
var err error
|
var err error
|
||||||
@@ -365,26 +384,32 @@ type registerOptions struct {
|
|||||||
brokers []string
|
brokers []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterOption func signature
|
||||||
type RegisterOption func(*registerOptions)
|
type RegisterOption func(*registerOptions)
|
||||||
|
|
||||||
|
// RegisterRouter speciefies routers for register
|
||||||
func RegisterRouter(n string) RegisterOption {
|
func RegisterRouter(n string) RegisterOption {
|
||||||
return func(o *registerOptions) {
|
return func(o *registerOptions) {
|
||||||
o.routers = append(o.routers, n)
|
o.routers = append(o.routers, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterServer specifies servers for register
|
||||||
func RegisterServer(n string) RegisterOption {
|
func RegisterServer(n string) RegisterOption {
|
||||||
return func(o *registerOptions) {
|
return func(o *registerOptions) {
|
||||||
o.servers = append(o.servers, n)
|
o.servers = append(o.servers, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterBroker specifies broker for register
|
||||||
func RegisterBroker(n string) RegisterOption {
|
func RegisterBroker(n string) RegisterOption {
|
||||||
return func(o *registerOptions) {
|
return func(o *registerOptions) {
|
||||||
o.brokers = append(o.brokers, n)
|
o.brokers = append(o.brokers, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tracer sets the tracer
|
||||||
|
//nolint:gocyclo
|
||||||
func Tracer(t tracer.Tracer, opts ...TracerOption) Option {
|
func Tracer(t tracer.Tracer, opts ...TracerOption) Option {
|
||||||
return func(o *Options) error {
|
return func(o *Options) error {
|
||||||
var err error
|
var err error
|
||||||
@@ -443,26 +468,31 @@ type tracerOptions struct {
|
|||||||
stores []string
|
stores []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TracerOption func signature
|
||||||
type TracerOption func(*tracerOptions)
|
type TracerOption func(*tracerOptions)
|
||||||
|
|
||||||
|
// TracerClient sets the clients for tracer
|
||||||
func TracerClient(n string) TracerOption {
|
func TracerClient(n string) TracerOption {
|
||||||
return func(o *tracerOptions) {
|
return func(o *tracerOptions) {
|
||||||
o.clients = append(o.clients, n)
|
o.clients = append(o.clients, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TracerServer sets the servers for tracer
|
||||||
func TracerServer(n string) TracerOption {
|
func TracerServer(n string) TracerOption {
|
||||||
return func(o *tracerOptions) {
|
return func(o *tracerOptions) {
|
||||||
o.servers = append(o.servers, n)
|
o.servers = append(o.servers, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TracerBroker sets the broker for tracer
|
||||||
func TracerBroker(n string) TracerOption {
|
func TracerBroker(n string) TracerOption {
|
||||||
return func(o *tracerOptions) {
|
return func(o *tracerOptions) {
|
||||||
o.brokers = append(o.brokers, n)
|
o.brokers = append(o.brokers, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TracerStore sets the store for tracer
|
||||||
func TracerStore(n string) TracerOption {
|
func TracerStore(n string) TracerOption {
|
||||||
return func(o *tracerOptions) {
|
return func(o *tracerOptions) {
|
||||||
o.stores = append(o.stores, n)
|
o.stores = append(o.stores, n)
|
||||||
@@ -547,8 +577,10 @@ type routerOptions struct {
|
|||||||
clients []string
|
clients []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RouterOption func signature
|
||||||
type RouterOption func(*routerOptions)
|
type RouterOption func(*routerOptions)
|
||||||
|
|
||||||
|
// RouterClient sets the clients for router
|
||||||
func RouterClient(n string) RouterOption {
|
func RouterClient(n string) RouterOption {
|
||||||
return func(o *routerOptions) {
|
return func(o *routerOptions) {
|
||||||
o.clients = append(o.clients, n)
|
o.clients = append(o.clients, n)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user