Compare commits
131 Commits
Author | SHA1 | Date | |
---|---|---|---|
fa8fb3aed7 | |||
cfd2d53a79 | |||
d306f77ffc | |||
e5b0a7e20d | |||
9a5b158b4d | |||
af8d81f3c6 | |||
5c9b3dae33 | |||
9f3957d101 | |||
8fd8bdcb39 | |||
80e3d239ab | |||
419cd486cf | |||
e64269b2a8 | |||
d18429e024 | |||
675e121049 | |||
d357fb1e0d | |||
e4673bcc50 | |||
a839f75a2f | |||
a7e6d61b95 | |||
650d167313 | |||
c6ba2a91e6 | |||
7ece08896f | |||
|
57f6f23294 | ||
09e6fa2fed | |||
10a09a5c6f | |||
b4e5d9462a | |||
96aa0b6906 | |||
f54658830d | |||
1e43122660 | |||
42800fa247 | |||
5b9c810653 | |||
c3def24bf4 | |||
0d1ef31764 | |||
d49afa230f | |||
e545eb4e13 | |||
f28b107372 | |||
c592fabe2a | |||
eb107020c7 | |||
bd4d4c363e | |||
2a548634fd | |||
598dddc476 | |||
887b48f1e7 | |||
6e55d07636 | |||
919520219c | |||
60a5e737f8 | |||
34f0b209cc | |||
ba8e1889fe | |||
dae5c57a60 | |||
ea590d57df | |||
|
9aa6969836 | ||
|
c00c705c24 | ||
|
0239f795d8 | ||
|
e69b43881d | ||
3a48a613fe | |||
86626c5922 | |||
ee11f39a2f | |||
3bdfdd8fd2 | |||
6dfdff7fd8 | |||
00a4785df3 | |||
|
bae3b0ef94 | ||
|
89b0565062 | ||
1f8b0aeb61 | |||
|
5b6f849e0a | ||
|
3b416fffde | ||
3a60103aed | |||
41837a67f8 | |||
852f19598d | |||
6537b35773 | |||
b733f1316f | |||
|
840af5574c | ||
|
56e5b7001c | ||
|
11dc6fd752 | ||
a2695d8699 | |||
618421de05 | |||
|
30baaabd9f | ||
df5bce1191 | |||
|
089d0fe4df | ||
a06f535303 | |||
|
eba586a329 | ||
|
d74a8645e8 | ||
|
5a00786192 | ||
|
b3e9941634 | ||
|
a5a5904302 | ||
|
a59832e57e | ||
0e42033e7f | |||
52d8255974 | |||
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 |
19
.github/dependabot.yml
vendored
Normal file
19
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
|
||||||
|
# Maintain dependencies for GitHub Actions
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
|
||||||
|
# Maintain dependencies for Golang
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
15
.github/generate.sh
vendored
15
.github/generate.sh
vendored
@@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
find . -type f -name '*.pb.*.go' -o -name '*.pb.go' -a ! -name 'message.pb.go' -delete
|
|
||||||
PROTOS=$(find . -type f -name '*.proto' | grep -v proto/google/api)
|
|
||||||
|
|
||||||
mkdir -p proto/google/api
|
|
||||||
curl -s -o proto/google/api/annotations.proto -L https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto
|
|
||||||
curl -s -o proto/google/api/http.proto -L https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto
|
|
||||||
|
|
||||||
for PROTO in $PROTOS; do
|
|
||||||
echo $PROTO
|
|
||||||
protoc -I./proto -I. -I$(dirname $PROTO) --go-grpc_out=paths=source_relative:. --go_out=paths=source_relative:. --micro_out=paths=source_relative:. $PROTO
|
|
||||||
done
|
|
||||||
|
|
||||||
rm -r proto
|
|
19
.github/renovate.json
vendored
19
.github/renovate.json
vendored
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"config:base"
|
|
||||||
],
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
|
|
||||||
"automerge": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"groupName": "all deps",
|
|
||||||
"separateMajorMinor": true,
|
|
||||||
"groupSlug": "all",
|
|
||||||
"packagePatterns": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
13
.github/stale.sh
vendored
13
.github/stale.sh
vendored
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash -ex
|
|
||||||
|
|
||||||
export PATH=$PATH:$(pwd)/bin
|
|
||||||
export GO111MODULE=on
|
|
||||||
export GOBIN=$(pwd)/bin
|
|
||||||
|
|
||||||
#go get github.com/rvflash/goup@v0.4.1
|
|
||||||
|
|
||||||
#goup -v ./...
|
|
||||||
#go get github.com/psampaz/go-mod-outdated@v0.6.0
|
|
||||||
go list -u -m -mod=mod -json all | go-mod-outdated -update -direct -ci || true
|
|
||||||
|
|
||||||
#go list -u -m -json all | go-mod-outdated -update
|
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||||
version: v1.30
|
version: v1.39
|
||||||
# Optional: working directory, useful for monorepos
|
# Optional: working directory, useful for monorepos
|
||||||
# working-directory: somedir
|
# working-directory: somedir
|
||||||
# Optional: golangci-lint command line arguments.
|
# Optional: golangci-lint command line arguments.
|
||||||
|
2
.github/workflows/pr.yml
vendored
2
.github/workflows/pr.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||||
version: v1.30
|
version: v1.39
|
||||||
# Optional: working directory, useful for monorepos
|
# Optional: working directory, useful for monorepos
|
||||||
# working-directory: somedir
|
# working-directory: somedir
|
||||||
# Optional: golangci-lint command line arguments.
|
# Optional: golangci-lint command line arguments.
|
||||||
|
@@ -1,30 +1,44 @@
|
|||||||
run:
|
run:
|
||||||
|
concurrency: 4
|
||||||
deadline: 5m
|
deadline: 5m
|
||||||
modules-download-mode: readonly
|
issues-exit-code: 1
|
||||||
skip-files:
|
tests: true
|
||||||
- ".*\\.pb\\.go$"
|
|
||||||
- ".*\\.pb\\.micro\\.go$"
|
linters-settings:
|
||||||
|
govet:
|
||||||
|
check-shadowing: true
|
||||||
|
enable:
|
||||||
|
- fieldalignment
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
disable-all: false
|
|
||||||
enable-all: false
|
|
||||||
enable:
|
enable:
|
||||||
- megacheck
|
|
||||||
- staticcheck
|
|
||||||
- deadcode
|
|
||||||
- varcheck
|
|
||||||
- gosimple
|
|
||||||
- unused
|
|
||||||
- prealloc
|
|
||||||
- scopelint
|
|
||||||
- gocritic
|
|
||||||
- goimports
|
|
||||||
- unconvert
|
|
||||||
- govet
|
- govet
|
||||||
- nakedret
|
- deadcode
|
||||||
|
- errcheck
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- staticcheck
|
||||||
- structcheck
|
- structcheck
|
||||||
- gosec
|
|
||||||
disable:
|
|
||||||
- maligned
|
|
||||||
- interfacer
|
|
||||||
- typecheck
|
- typecheck
|
||||||
- dupl
|
- unused
|
||||||
|
- varcheck
|
||||||
|
- bodyclose
|
||||||
|
- gci
|
||||||
|
- goconst
|
||||||
|
- gocritic
|
||||||
|
- gosimple
|
||||||
|
- gofmt
|
||||||
|
- gofumpt
|
||||||
|
- goimports
|
||||||
|
- golint
|
||||||
|
- gosec
|
||||||
|
- makezero
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- nestif
|
||||||
|
- nilerr
|
||||||
|
- noctx
|
||||||
|
- prealloc
|
||||||
|
- unconvert
|
||||||
|
- unparam
|
||||||
|
disable-all: false
|
||||||
|
10
api/api.go
10
api/api.go
@@ -34,20 +34,20 @@ type Option func(*Options) error
|
|||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
// Name Greeter.Hello
|
// Name Greeter.Hello
|
||||||
Name string
|
Name string
|
||||||
// Description e.g what's this endpoint for
|
// Desciption for endpoint
|
||||||
Description string
|
Description string
|
||||||
// Handler e.g rpc, proxy
|
// Handler e.g rpc, proxy
|
||||||
Handler string
|
Handler string
|
||||||
|
// Body destination
|
||||||
|
// "*" or "" - top level message value
|
||||||
|
// "string" - inner message value
|
||||||
|
Body string
|
||||||
// Host e.g example.com
|
// Host e.g example.com
|
||||||
Host []string
|
Host []string
|
||||||
// Method e.g GET, POST
|
// Method e.g GET, POST
|
||||||
Method []string
|
Method []string
|
||||||
// Path e.g /greeter. Expect POSIX regex
|
// Path e.g /greeter. Expect POSIX regex
|
||||||
Path []string
|
Path []string
|
||||||
// Body destination
|
|
||||||
// "*" or "" - top level message value
|
|
||||||
// "string" - inner message value
|
|
||||||
Body string
|
|
||||||
// Stream flag
|
// Stream flag
|
||||||
Stream bool
|
Stream bool
|
||||||
}
|
}
|
||||||
|
@@ -149,5 +149,4 @@ func TestValidate(t *testing.T) {
|
|||||||
if err := Validate(epPcreInvalid); err == nil {
|
if err := Validate(epPcreInvalid); err == nil {
|
||||||
t.Fatalf("invalid pcre %v", epPcreInvalid.Path[0])
|
t.Fatalf("invalid pcre %v", epPcreInvalid.Path[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -6,18 +6,16 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/logger"
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultMaxRecvSize specifies max recv size for handler
|
||||||
// DefaultMaxRecvSize specifies max recv size for handler
|
var DefaultMaxRecvSize int64 = 1024 * 1024 * 100 // 10Mb
|
||||||
DefaultMaxRecvSize int64 = 1024 * 1024 * 100 // 10Mb
|
|
||||||
)
|
|
||||||
|
|
||||||
// Options struct holds handler options
|
// 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
|
Logger logger.Logger
|
||||||
|
Namespace string
|
||||||
|
MaxRecvSize int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func signature
|
// Option func signature
|
||||||
|
@@ -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
|
||||||
|
@@ -15,12 +15,12 @@ import (
|
|||||||
// NewResolver creates new subdomain api resolver
|
// 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 &subdomainResolver{options, parent}
|
return &subdomainResolver{opts: options, Resolver: parent}
|
||||||
}
|
}
|
||||||
|
|
||||||
type subdomainResolver struct {
|
type subdomainResolver struct {
|
||||||
opts resolver.Options
|
|
||||||
resolver.Resolver
|
resolver.Resolver
|
||||||
|
opts resolver.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve resolve endpoint based on subdomain
|
// Resolve resolve endpoint based on subdomain
|
||||||
|
@@ -19,9 +19,7 @@ type vpathResolver struct {
|
|||||||
opts resolver.Options
|
opts resolver.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var re = regexp.MustCompile("^v[0-9]+$")
|
||||||
re = regexp.MustCompile("^v[0-9]+$")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Resolve endpoint
|
// Resolve endpoint
|
||||||
func (r *vpathResolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
func (r *vpathResolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
|
||||||
|
@@ -11,11 +11,16 @@ import (
|
|||||||
|
|
||||||
// Options holds the options for api router
|
// 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
|
// Option func signature
|
||||||
|
@@ -7,10 +7,8 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/api"
|
"github.com/unistack-org/micro/v3/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultRouter contains default router implementation
|
||||||
// DefaultRouter contains default router implementation
|
var DefaultRouter Router
|
||||||
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 {
|
||||||
|
36
auth/auth.go
36
auth/auth.go
@@ -30,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
|
||||||
@@ -53,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
|
||||||
@@ -106,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
|
||||||
|
@@ -26,33 +26,34 @@ func NewOptions(opts ...Option) Options {
|
|||||||
|
|
||||||
// Options struct holds auth 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
|
||||||
@@ -124,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
|
||||||
@@ -194,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
|
||||||
|
@@ -24,11 +24,11 @@ func TestVerify(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt := []struct {
|
tt := []struct {
|
||||||
Name string
|
Error error
|
||||||
Rules []*Rule
|
|
||||||
Account *Account
|
Account *Account
|
||||||
Resource *Resource
|
Resource *Resource
|
||||||
Error error
|
Name string
|
||||||
|
Rules []*Rule
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Name: "NoRules",
|
Name: "NoRules",
|
||||||
|
100
broker/broker.go
100
broker/broker.go
@@ -3,48 +3,128 @@ package broker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/metadata"
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultBroker default memory broker
|
||||||
|
var DefaultBroker Broker = NewBroker()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultBroker default broker
|
// ErrNotConnected returns when broker used but not connected yet
|
||||||
DefaultBroker Broker = NewBroker()
|
ErrNotConnected = errors.New("broker not connected")
|
||||||
|
// ErrDisconnected returns when broker disconnected
|
||||||
|
ErrDisconnected = errors.New("broker disconnected")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Broker is an interface used for asynchronous messaging.
|
// Broker is an interface used for asynchronous messaging.
|
||||||
type Broker interface {
|
type Broker interface {
|
||||||
|
// Name returns broker instance name
|
||||||
Name() string
|
Name() string
|
||||||
Init(...Option) error
|
// Init initilize broker
|
||||||
|
Init(opts ...Option) error
|
||||||
|
// Options returns broker options
|
||||||
Options() Options
|
Options() Options
|
||||||
|
// Address return configured address
|
||||||
Address() string
|
Address() string
|
||||||
Connect(context.Context) error
|
// Connect connects to broker
|
||||||
Disconnect(context.Context) error
|
Connect(ctx context.Context) error
|
||||||
Publish(context.Context, string, *Message, ...PublishOption) error
|
// Disconnect disconnect from broker
|
||||||
Subscribe(context.Context, string, Handler, ...SubscribeOption) (Subscriber, error)
|
Disconnect(ctx context.Context) error
|
||||||
|
// Publish message to broker topic
|
||||||
|
Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error
|
||||||
|
// Subscribe subscribes to topic message via handler
|
||||||
|
Subscribe(ctx context.Context, topic string, h Handler, opts ...SubscribeOption) (Subscriber, error)
|
||||||
|
// BatchPublish messages to broker with multiple topics
|
||||||
|
BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error
|
||||||
|
// BatchSubscribe subscribes to topic messages via handler
|
||||||
|
BatchSubscribe(ctx context.Context, topic string, h BatchHandler, opts ...SubscribeOption) (Subscriber, error)
|
||||||
|
// String type of broker
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler is used to process messages via a subscription of a topic.
|
// Handler is used to process messages via a subscription of a topic.
|
||||||
type Handler func(Event) error
|
type Handler func(Event) error
|
||||||
|
|
||||||
|
// Events contains multiple events
|
||||||
|
type Events []Event
|
||||||
|
|
||||||
|
func (evs Events) Ack() error {
|
||||||
|
var err error
|
||||||
|
for _, ev := range evs {
|
||||||
|
if err = ev.Ack(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (evs Events) SetError(err error) {
|
||||||
|
for _, ev := range evs {
|
||||||
|
ev.SetError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchHandler is used to process messages in batches via a subscription of a topic.
|
||||||
|
type BatchHandler func(Events) error
|
||||||
|
|
||||||
// Event is given to a subscription handler for processing
|
// Event is given to a subscription handler for processing
|
||||||
type Event interface {
|
type Event interface {
|
||||||
|
// Topic returns event topic
|
||||||
Topic() string
|
Topic() string
|
||||||
|
// Message returns broker message
|
||||||
Message() *Message
|
Message() *Message
|
||||||
|
// Ack acknowledge message
|
||||||
Ack() error
|
Ack() error
|
||||||
|
// Error returns message error (like decoding errors or some other)
|
||||||
Error() error
|
Error() error
|
||||||
|
// SetError set event processing error
|
||||||
|
SetError(err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawMessage is a raw encoded JSON value.
|
||||||
|
// It implements Marshaler and Unmarshaler and can be used to delay decoding or precompute a encoding.
|
||||||
|
type RawMessage []byte
|
||||||
|
|
||||||
|
// MarshalJSON returns m as the JSON encoding of m.
|
||||||
|
func (m *RawMessage) MarshalJSON() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
return *m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON sets *m to a copy of data.
|
||||||
|
func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
||||||
|
if m == nil {
|
||||||
|
return errors.New("RawMessage UnmarshalJSON on nil pointer")
|
||||||
|
}
|
||||||
|
*m = append((*m)[0:0], data...)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message is used to transfer data
|
// Message is used to transfer data
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Header metadata.Metadata // contains message metadata
|
// Header contains message metadata
|
||||||
Body []byte // contains message body
|
Header metadata.Metadata
|
||||||
|
// Body contains message body
|
||||||
|
Body RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMessage create broker message with topic filled
|
||||||
|
func NewMessage(topic string) *Message {
|
||||||
|
m := &Message{Header: metadata.New(2)}
|
||||||
|
m.Header.Set(metadata.HeaderTopic, topic)
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscriber is a convenience return type for the Subscribe method
|
// Subscriber is a convenience return type for the Subscribe method
|
||||||
type Subscriber interface {
|
type Subscriber interface {
|
||||||
|
// Options returns subscriber options
|
||||||
Options() SubscribeOptions
|
Options() SubscribeOptions
|
||||||
|
// Topic returns topic for subscription
|
||||||
Topic() string
|
Topic() string
|
||||||
Unsubscribe(context.Context) error
|
// Unsubscribe from topic
|
||||||
|
Unsubscribe(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
224
broker/memory.go
224
broker/memory.go
@@ -2,39 +2,39 @@ package broker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/unistack-org/micro/v3/logger"
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
maddr "github.com/unistack-org/micro/v3/util/addr"
|
maddr "github.com/unistack-org/micro/v3/util/addr"
|
||||||
mnet "github.com/unistack-org/micro/v3/util/net"
|
mnet "github.com/unistack-org/micro/v3/util/net"
|
||||||
"github.com/unistack-org/micro/v3/util/rand"
|
"github.com/unistack-org/micro/v3/util/rand"
|
||||||
)
|
)
|
||||||
|
|
||||||
type memoryBroker struct {
|
type memoryBroker struct {
|
||||||
opts Options
|
subscribers map[string][]*memorySubscriber
|
||||||
|
addr string
|
||||||
addr string
|
opts Options
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
connected bool
|
connected bool
|
||||||
Subscribers map[string][]*memorySubscriber
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type memoryEvent struct {
|
type memoryEvent struct {
|
||||||
opts Options
|
|
||||||
topic string
|
|
||||||
err error
|
err error
|
||||||
message interface{}
|
message interface{}
|
||||||
|
topic string
|
||||||
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
type memorySubscriber struct {
|
type memorySubscriber struct {
|
||||||
id string
|
ctx context.Context
|
||||||
topic string
|
exit chan bool
|
||||||
exit chan bool
|
handler Handler
|
||||||
handler Handler
|
batchhandler BatchHandler
|
||||||
opts SubscribeOptions
|
id string
|
||||||
ctx context.Context
|
topic string
|
||||||
|
opts SubscribeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) Options() Options {
|
func (m *memoryBroker) Options() Options {
|
||||||
@@ -78,7 +78,6 @@ func (m *memoryBroker) Disconnect(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.connected = false
|
m.connected = false
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,67 +92,190 @@ func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *Message,
|
|||||||
m.RLock()
|
m.RLock()
|
||||||
if !m.connected {
|
if !m.connected {
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
return errors.New("not connected")
|
return ErrNotConnected
|
||||||
}
|
}
|
||||||
|
|
||||||
subs, ok := m.Subscribers[topic]
|
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var v interface{}
|
options := NewPublishOptions(opts...)
|
||||||
if m.opts.Codec != nil {
|
vs := make([]msgWrapper, 0, 1)
|
||||||
|
if m.opts.Codec == nil || options.BodyOnly {
|
||||||
|
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||||
|
vs = append(vs, msgWrapper{topic: topic, body: msg})
|
||||||
|
} else {
|
||||||
|
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||||
buf, err := m.opts.Codec.Marshal(msg)
|
buf, err := m.opts.Codec.Marshal(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v = buf
|
vs = append(vs, msgWrapper{topic: topic, body: buf})
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.publish(ctx, vs, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type msgWrapper struct {
|
||||||
|
topic string
|
||||||
|
body interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memoryBroker) BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
|
||||||
|
m.RLock()
|
||||||
|
if !m.connected {
|
||||||
|
m.RUnlock()
|
||||||
|
return ErrNotConnected
|
||||||
|
}
|
||||||
|
m.RUnlock()
|
||||||
|
|
||||||
|
options := NewPublishOptions(opts...)
|
||||||
|
vs := make([]msgWrapper, 0, len(msgs))
|
||||||
|
if m.opts.Codec == nil || options.BodyOnly {
|
||||||
|
for _, msg := range msgs {
|
||||||
|
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||||
|
vs = append(vs, msgWrapper{topic: topic, body: msg})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
v = msg
|
for _, msg := range msgs {
|
||||||
|
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||||
|
buf, err := m.opts.Codec.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
vs = append(vs, msgWrapper{topic: topic, body: buf})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p := &memoryEvent{
|
return m.publish(ctx, vs, opts...)
|
||||||
topic: topic,
|
}
|
||||||
message: v,
|
|
||||||
opts: m.opts,
|
func (m *memoryBroker) publish(ctx context.Context, vs []msgWrapper, opts ...PublishOption) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
msgTopicMap := make(map[string]Events)
|
||||||
|
for _, v := range vs {
|
||||||
|
p := &memoryEvent{
|
||||||
|
topic: v.topic,
|
||||||
|
message: v.body,
|
||||||
|
opts: m.opts,
|
||||||
|
}
|
||||||
|
msgTopicMap[p.topic] = append(msgTopicMap[p.topic], p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beh := m.opts.BatchErrorHandler
|
||||||
eh := m.opts.ErrorHandler
|
eh := m.opts.ErrorHandler
|
||||||
|
|
||||||
for _, sub := range subs {
|
for t, ms := range msgTopicMap {
|
||||||
if err := sub.handler(p); err != nil {
|
m.RLock()
|
||||||
p.err = err
|
subs, ok := m.subscribers[t]
|
||||||
if sub.opts.ErrorHandler != nil {
|
m.RUnlock()
|
||||||
eh = sub.opts.ErrorHandler
|
if !ok {
|
||||||
}
|
|
||||||
if eh != nil {
|
|
||||||
eh(p)
|
|
||||||
} else if m.opts.Logger.V(logger.ErrorLevel) {
|
|
||||||
m.opts.Logger.Error(m.opts.Context, err.Error())
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, sub := range subs {
|
||||||
|
// batch processing
|
||||||
|
if sub.batchhandler != nil {
|
||||||
|
if err = sub.batchhandler(ms); err != nil {
|
||||||
|
ms.SetError(err)
|
||||||
|
if sub.opts.BatchErrorHandler != nil {
|
||||||
|
beh = sub.opts.BatchErrorHandler
|
||||||
|
}
|
||||||
|
if beh != nil {
|
||||||
|
beh(ms)
|
||||||
|
} else if m.opts.Logger.V(logger.ErrorLevel) {
|
||||||
|
m.opts.Logger.Error(m.opts.Context, err.Error())
|
||||||
|
}
|
||||||
|
} else if sub.opts.AutoAck {
|
||||||
|
if err = ms.Ack(); err != nil {
|
||||||
|
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// single processing
|
||||||
|
if sub.handler != nil {
|
||||||
|
for _, p := range ms {
|
||||||
|
if err = sub.handler(p); err != nil {
|
||||||
|
p.SetError(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())
|
||||||
|
}
|
||||||
|
} else if sub.opts.AutoAck {
|
||||||
|
if err = p.Ack(); err != nil {
|
||||||
|
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
|
func (m *memoryBroker) BatchSubscribe(ctx context.Context, topic string, handler BatchHandler, opts ...SubscribeOption) (Subscriber, error) {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
if !m.connected {
|
if !m.connected {
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
return nil, errors.New("not connected")
|
return nil, ErrNotConnected
|
||||||
}
|
}
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
options := NewSubscribeOptions(opts...)
|
|
||||||
|
|
||||||
id, err := uuid.NewRandom()
|
id, err := uuid.NewRandom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options := NewSubscribeOptions(opts...)
|
||||||
|
|
||||||
|
sub := &memorySubscriber{
|
||||||
|
exit: make(chan bool, 1),
|
||||||
|
id: id.String(),
|
||||||
|
topic: topic,
|
||||||
|
batchhandler: handler,
|
||||||
|
opts: options,
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Lock()
|
||||||
|
m.subscribers[topic] = append(m.subscribers[topic], sub)
|
||||||
|
m.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sub.exit
|
||||||
|
m.Lock()
|
||||||
|
newSubscribers := make([]*memorySubscriber, 0, len(m.subscribers)-1)
|
||||||
|
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) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
|
||||||
|
m.RLock()
|
||||||
|
if !m.connected {
|
||||||
|
m.RUnlock()
|
||||||
|
return nil, ErrNotConnected
|
||||||
|
}
|
||||||
|
m.RUnlock()
|
||||||
|
|
||||||
|
id, err := uuid.NewRandom()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
options := NewSubscribeOptions(opts...)
|
||||||
|
|
||||||
sub := &memorySubscriber{
|
sub := &memorySubscriber{
|
||||||
exit: make(chan bool, 1),
|
exit: make(chan bool, 1),
|
||||||
id: id.String(),
|
id: id.String(),
|
||||||
@@ -164,20 +286,20 @@ func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler Hand
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.Lock()
|
m.Lock()
|
||||||
m.Subscribers[topic] = append(m.Subscribers[topic], sub)
|
m.subscribers[topic] = append(m.subscribers[topic], sub)
|
||||||
m.Unlock()
|
m.Unlock()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-sub.exit
|
<-sub.exit
|
||||||
m.Lock()
|
m.Lock()
|
||||||
var newSubscribers []*memorySubscriber
|
newSubscribers := make([]*memorySubscriber, 0, len(m.subscribers)-1)
|
||||||
for _, sb := range m.Subscribers[topic] {
|
for _, sb := range m.subscribers[topic] {
|
||||||
if sb.id == sub.id {
|
if sb.id == sub.id {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newSubscribers = append(newSubscribers, sb)
|
newSubscribers = append(newSubscribers, sb)
|
||||||
}
|
}
|
||||||
m.Subscribers[topic] = newSubscribers
|
m.subscribers[topic] = newSubscribers
|
||||||
m.Unlock()
|
m.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -222,6 +344,10 @@ func (m *memoryEvent) Error() error {
|
|||||||
return m.err
|
return m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *memoryEvent) SetError(err error) {
|
||||||
|
m.err = err
|
||||||
|
}
|
||||||
|
|
||||||
func (m *memorySubscriber) Options() SubscribeOptions {
|
func (m *memorySubscriber) Options() SubscribeOptions {
|
||||||
return m.opts
|
return m.opts
|
||||||
}
|
}
|
||||||
@@ -239,6 +365,6 @@ func (m *memorySubscriber) Unsubscribe(ctx context.Context) error {
|
|||||||
func NewBroker(opts ...Option) Broker {
|
func NewBroker(opts ...Option) Broker {
|
||||||
return &memoryBroker{
|
return &memoryBroker{
|
||||||
opts: NewOptions(opts...),
|
opts: NewOptions(opts...),
|
||||||
Subscribers: make(map[string][]*memorySubscriber),
|
subscribers: make(map[string][]*memorySubscriber),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,8 +4,55 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestMemoryBatchBroker(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(evts Events) error {
|
||||||
|
return evts.Ack()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub, err := b.BatchSubscribe(ctx, topic, fn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error subscribing %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs := make([]*Message, 0, 0)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
message := &Message{
|
||||||
|
Header: map[string]string{
|
||||||
|
metadata.HeaderTopic: topic,
|
||||||
|
"foo": "bar",
|
||||||
|
"id": fmt.Sprintf("%d", i),
|
||||||
|
},
|
||||||
|
Body: []byte(`"hello world"`),
|
||||||
|
}
|
||||||
|
msgs = append(msgs, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.BatchPublish(ctx, msgs); err != nil {
|
||||||
|
t.Fatalf("Unexpected error publishing %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestMemoryBroker(t *testing.T) {
|
func TestMemoryBroker(t *testing.T) {
|
||||||
b := NewBroker()
|
b := NewBroker()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -26,20 +73,27 @@ func TestMemoryBroker(t *testing.T) {
|
|||||||
t.Fatalf("Unexpected error subscribing %v", err)
|
t.Fatalf("Unexpected error subscribing %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msgs := make([]*Message, 0, 0)
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
message := &Message{
|
message := &Message{
|
||||||
Header: map[string]string{
|
Header: map[string]string{
|
||||||
"foo": "bar",
|
metadata.HeaderTopic: topic,
|
||||||
"id": fmt.Sprintf("%d", i),
|
"foo": "bar",
|
||||||
|
"id": fmt.Sprintf("%d", i),
|
||||||
},
|
},
|
||||||
Body: []byte(`hello world`),
|
Body: []byte(`"hello world"`),
|
||||||
}
|
}
|
||||||
|
msgs = append(msgs, message)
|
||||||
|
|
||||||
if err := b.Publish(ctx, topic, message); err != nil {
|
if err := b.Publish(ctx, topic, message); err != nil {
|
||||||
t.Fatalf("Unexpected error publishing %d", i)
|
t.Fatalf("Unexpected error publishing %d err: %v", i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := b.BatchPublish(ctx, msgs); err != nil {
|
||||||
|
t.Fatalf("Unexpected error publishing %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := sub.Unsubscribe(ctx); err != nil {
|
if err := sub.Unsubscribe(ctx); err != nil {
|
||||||
t.Fatalf("Unexpected error unsubscribing from %s: %v", topic, err)
|
t.Fatalf("Unexpected error unsubscribing from %s: %v", topic, err)
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package broker
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/codec"
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
"github.com/unistack-org/micro/v3/logger"
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
@@ -13,25 +14,28 @@ 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
|
||||||
|
// BatchErrorHandler used when broker can't unmashal incoming messages
|
||||||
|
BatchErrorHandler BatchHandler
|
||||||
|
// Name holds the broker name
|
||||||
|
Name string
|
||||||
|
// Addrs holds the broker address
|
||||||
|
Addrs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions create new Options
|
// NewOptions create new Options
|
||||||
@@ -59,10 +63,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
|
||||||
@@ -70,32 +74,30 @@ func NewPublishOptions(opts ...PublishOption) PublishOptions {
|
|||||||
options := PublishOptions{
|
options := PublishOptions{
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
// BatchErrorHandler used when broker can't unmashal incoming messages
|
||||||
|
BatchErrorHandler BatchHandler
|
||||||
|
// 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
|
||||||
|
// BatchSize flag specifies max batch size
|
||||||
|
BatchSize int
|
||||||
|
// BatchWait flag specifies max wait time for batch filling
|
||||||
|
BatchWait time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func
|
// Option func
|
||||||
@@ -118,23 +120,6 @@ func PublishContext(ctx context.Context) PublishOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribeOption func
|
|
||||||
type SubscribeOption func(*SubscribeOptions)
|
|
||||||
|
|
||||||
// NewSubscribeOptions creates new SubscribeOptions
|
|
||||||
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
|
|
||||||
options := SubscribeOptions{
|
|
||||||
AutoAck: true,
|
|
||||||
Context: context.Background(),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
// Addrs sets the host addresses to be used by the broker
|
// Addrs sets the host addresses to be used by the broker
|
||||||
func Addrs(addrs ...string) Option {
|
func Addrs(addrs ...string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@@ -150,28 +135,6 @@ func Codec(c codec.Codec) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableAutoAck disables auto ack
|
|
||||||
func DisableAutoAck() SubscribeOption {
|
|
||||||
return func(o *SubscribeOptions) {
|
|
||||||
o.AutoAck = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeAutoAck will disable auto acking of messages
|
|
||||||
// after they have been handled.
|
|
||||||
func SubscribeAutoAck(b bool) SubscribeOption {
|
|
||||||
return func(o *SubscribeOptions) {
|
|
||||||
o.AutoAck = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeBodyOnly consumes only body of the message
|
|
||||||
func SubscribeBodyOnly(b bool) SubscribeOption {
|
|
||||||
return func(o *SubscribeOptions) {
|
|
||||||
o.BodyOnly = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorHandler will catch all broker errors that cant be handled
|
// ErrorHandler will catch all broker errors that cant be handled
|
||||||
// in normal way, for example Codec errors
|
// in normal way, for example Codec errors
|
||||||
func ErrorHandler(h Handler) Option {
|
func ErrorHandler(h Handler) Option {
|
||||||
@@ -180,6 +143,14 @@ func ErrorHandler(h Handler) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchErrorHandler will catch all broker errors that cant be handled
|
||||||
|
// in normal way, for example Codec errors
|
||||||
|
func BatchErrorHandler(h BatchHandler) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.BatchErrorHandler = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SubscribeErrorHandler will catch all broker errors that cant be handled
|
// SubscribeErrorHandler will catch all broker errors that cant be handled
|
||||||
// in normal way, for example Codec errors
|
// in normal way, for example Codec errors
|
||||||
func SubscribeErrorHandler(h Handler) SubscribeOption {
|
func SubscribeErrorHandler(h Handler) SubscribeOption {
|
||||||
@@ -188,6 +159,14 @@ func SubscribeErrorHandler(h Handler) SubscribeOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubscribeBatchErrorHandler will catch all broker errors that cant be handled
|
||||||
|
// in normal way, for example Codec errors
|
||||||
|
func SubscribeBatchErrorHandler(h BatchHandler) SubscribeOption {
|
||||||
|
return func(o *SubscribeOptions) {
|
||||||
|
o.BatchErrorHandler = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Queue sets the subscribers queue
|
// Queue sets the subscribers queue
|
||||||
// Deprecated
|
// Deprecated
|
||||||
func Queue(name string) SubscribeOption {
|
func Queue(name string) SubscribeOption {
|
||||||
@@ -251,3 +230,55 @@ func SubscribeContext(ctx context.Context) SubscribeOption {
|
|||||||
o.Context = ctx
|
o.Context = ctx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DisableAutoAck disables auto ack
|
||||||
|
// Deprecated
|
||||||
|
func DisableAutoAck() SubscribeOption {
|
||||||
|
return func(o *SubscribeOptions) {
|
||||||
|
o.AutoAck = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeAutoAck contol auto acking of messages
|
||||||
|
// after they have been handled.
|
||||||
|
func SubscribeAutoAck(b bool) SubscribeOption {
|
||||||
|
return func(o *SubscribeOptions) {
|
||||||
|
o.AutoAck = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeBodyOnly consumes only body of the message
|
||||||
|
func SubscribeBodyOnly(b bool) SubscribeOption {
|
||||||
|
return func(o *SubscribeOptions) {
|
||||||
|
o.BodyOnly = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeBatchSize specifies max batch size
|
||||||
|
func SubscribeBatchSize(n int) SubscribeOption {
|
||||||
|
return func(o *SubscribeOptions) {
|
||||||
|
o.BatchSize = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeBatchWait specifies max batch wait time
|
||||||
|
func SubscribeBatchWait(td time.Duration) SubscribeOption {
|
||||||
|
return func(o *SubscribeOptions) {
|
||||||
|
o.BatchWait = td
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeOption func
|
||||||
|
type SubscribeOption func(*SubscribeOptions)
|
||||||
|
|
||||||
|
// NewSubscribeOptions creates new SubscribeOptions
|
||||||
|
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
|
||||||
|
options := SubscribeOptions{
|
||||||
|
AutoAck: true,
|
||||||
|
Context: context.Background(),
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
@@ -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
|
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,7 @@ type Client interface {
|
|||||||
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
|
||||||
|
BatchPublish(ctx context.Context, msg []Message, opts ...PublishOption) error
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
101
client/noop.go
101
client/noop.go
@@ -9,16 +9,10 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/metadata"
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultCodecs will be used to encode/decode data
|
||||||
// DefaultCodecs will be used to encode/decode data
|
var DefaultCodecs = map[string]codec.Codec{
|
||||||
DefaultCodecs = map[string]codec.Codec{
|
"application/octet-stream": codec.NewCodec(),
|
||||||
//"application/json": cjson.NewCodec,
|
}
|
||||||
//"application/json-rpc": cjsonrpc.NewCodec,
|
|
||||||
//"application/protobuf": cproto.NewCodec,
|
|
||||||
//"application/proto-rpc": cprotorpc.NewCodec,
|
|
||||||
"application/octet-stream": codec.NewCodec(),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type noopClient struct {
|
type noopClient struct {
|
||||||
opts Options
|
opts Options
|
||||||
@@ -31,12 +25,12 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,11 +169,11 @@ 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 {
|
||||||
options := NewMessageOptions(opts...)
|
options := NewMessageOptions(append([]MessageOption{MessageContentType(n.opts.ContentType)}, opts...)...)
|
||||||
return &noopMessage{topic: topic, payload: msg, opts: options}
|
return &noopMessage{topic: topic, payload: msg, opts: options}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,45 +181,60 @@ func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption
|
|||||||
return &noopStream{}, nil
|
return &noopStream{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error {
|
func (n *noopClient) BatchPublish(ctx context.Context, ps []Message, opts ...PublishOption) error {
|
||||||
var body []byte
|
return n.publish(ctx, ps, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error {
|
||||||
|
return n.publish(ctx, []Message{p}, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopClient) publish(ctx context.Context, ps []Message, opts ...PublishOption) error {
|
||||||
options := NewPublishOptions(opts...)
|
options := NewPublishOptions(opts...)
|
||||||
|
|
||||||
md, ok := metadata.FromOutgoingContext(ctx)
|
msgs := make([]*broker.Message, 0, len(ps))
|
||||||
if !ok {
|
|
||||||
md = metadata.New(0)
|
|
||||||
}
|
|
||||||
md["Content-Type"] = p.ContentType()
|
|
||||||
md["Micro-Topic"] = p.Topic()
|
|
||||||
|
|
||||||
// passed in raw data
|
for _, p := range ps {
|
||||||
if d, ok := p.Payload().(*codec.Frame); ok {
|
md, ok := metadata.FromOutgoingContext(ctx)
|
||||||
body = d.Data
|
if !ok {
|
||||||
} else {
|
md = metadata.New(0)
|
||||||
// use codec for payload
|
}
|
||||||
cf, err := n.newCodec(p.ContentType())
|
md[metadata.HeaderContentType] = p.ContentType()
|
||||||
if err != nil {
|
|
||||||
return errors.InternalServerError("go.micro.client", err.Error())
|
topic := p.Topic()
|
||||||
|
|
||||||
|
// get the exchange
|
||||||
|
if len(options.Exchange) > 0 {
|
||||||
|
topic = options.Exchange
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the body
|
md[metadata.HeaderTopic] = topic
|
||||||
b, err := cf.Marshal(p.Payload())
|
|
||||||
if err != nil {
|
var body []byte
|
||||||
return errors.InternalServerError("go.micro.client", err.Error())
|
|
||||||
|
// passed in raw data
|
||||||
|
if d, ok := p.Payload().(*codec.Frame); ok {
|
||||||
|
body = d.Data
|
||||||
|
} else {
|
||||||
|
// use codec for payload
|
||||||
|
cf, err := n.newCodec(p.ContentType())
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("go.micro.client", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the body
|
||||||
|
b, err := cf.Marshal(p.Payload())
|
||||||
|
if err != nil {
|
||||||
|
return errors.InternalServerError("go.micro.client", err.Error())
|
||||||
|
}
|
||||||
|
body = b
|
||||||
}
|
}
|
||||||
body = b
|
|
||||||
|
msgs = append(msgs, &broker.Message{Header: md, Body: body})
|
||||||
}
|
}
|
||||||
|
|
||||||
topic := p.Topic()
|
return n.opts.Broker.BatchPublish(ctx, msgs,
|
||||||
|
broker.PublishContext(options.Context),
|
||||||
// get the exchange
|
broker.PublishBodyOnly(options.BodyOnly),
|
||||||
if len(options.Exchange) > 0 {
|
)
|
||||||
topic = options.Exchange
|
|
||||||
}
|
|
||||||
|
|
||||||
return n.opts.Broker.Publish(ctx, topic, &broker.Message{
|
|
||||||
Header: md,
|
|
||||||
Body: body,
|
|
||||||
}, broker.PublishContext(options.Context))
|
|
||||||
}
|
}
|
||||||
|
@@ -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
|
// Selector used to select needed address
|
||||||
// Used to select codec
|
Selector selector.Selector
|
||||||
ContentType string
|
// Logger used to log messages
|
||||||
// Proxy address to send requests via
|
Logger logger.Logger
|
||||||
Proxy string
|
// Tracer used for tracing
|
||||||
|
Tracer tracer.Tracer
|
||||||
// Plugged interfaces
|
// Broker used to publish messages
|
||||||
Broker broker.Broker
|
Broker broker.Broker
|
||||||
Codecs map[string]codec.Codec
|
// Meter used for metrics
|
||||||
Router router.Router
|
Meter meter.Meter
|
||||||
Selector selector.Selector
|
// Router used to get route
|
||||||
|
Router router.Router
|
||||||
|
// Transport used for transfer messages
|
||||||
Transport transport.Transport
|
Transport transport.Transport
|
||||||
Logger logger.Logger
|
// Context is used for external options
|
||||||
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
|
|
||||||
|
|
||||||
// Context is used for non default options
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Lookup func used to get destination addr
|
||||||
|
Lookup LookupFunc
|
||||||
|
// Codecs map
|
||||||
|
Codecs map[string]codec.Codec
|
||||||
|
// TLSConfig specifies tls.Config for secure connection
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
// 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
|
||||||
|
// CallOptions contains default CallOptions
|
||||||
|
CallOptions CallOptions
|
||||||
|
// PoolSize connection pool size
|
||||||
|
PoolSize int
|
||||||
|
// PoolTTL connection pool ttl
|
||||||
|
PoolTTL time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCallOptions creates new call options struct
|
// NewCallOptions creates new call options struct
|
||||||
@@ -60,34 +68,36 @@ 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
|
|
||||||
// DialTimeout is the transport Dial Timeout
|
|
||||||
DialTimeout time.Duration
|
|
||||||
// Retries is the number of Call attempts
|
|
||||||
Retries int
|
|
||||||
// Retry func to be used for retries
|
|
||||||
Retry RetryFunc
|
|
||||||
// RequestTimeout specifies request 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
|
|
||||||
// StreamTimeout timeout for the stream
|
|
||||||
StreamTimeout time.Duration
|
|
||||||
// AuthToken specifies the auth token as the authorization header
|
|
||||||
AuthToken bool
|
|
||||||
// Network to lookup the route within
|
|
||||||
Network string
|
|
||||||
// CallWrappers is for low level call func
|
|
||||||
CallWrappers []CallWrapper
|
|
||||||
// Context is used 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
|
||||||
|
// Content-Type
|
||||||
|
ContentType 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 string
|
||||||
|
AuthToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context pass context to client
|
// Context pass context to client
|
||||||
@@ -108,10 +118,12 @@ 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
|
// BodyOnly will publish only message body
|
||||||
Exchange string
|
BodyOnly bool
|
||||||
// Context holds additional options
|
// Context used for external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Exchange topic exchange name
|
||||||
|
Exchange string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessageOptions creates message options struct
|
// NewMessageOptions creates message options struct
|
||||||
@@ -125,6 +137,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,12 +152,12 @@ func NewRequestOptions(opts ...RequestOption) RequestOptions {
|
|||||||
|
|
||||||
// RequestOptions holds client request options
|
// RequestOptions holds client request options
|
||||||
type RequestOptions struct {
|
type RequestOptions struct {
|
||||||
// ContentType specify content-type of request
|
// Context used for external options
|
||||||
ContentType string
|
|
||||||
// Stream says that request is the streaming
|
|
||||||
Stream bool
|
|
||||||
// Context can hold other options
|
|
||||||
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
|
||||||
@@ -154,20 +167,23 @@ func NewOptions(opts ...Option) Options {
|
|||||||
ContentType: DefaultContentType,
|
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 {
|
||||||
@@ -305,6 +321,22 @@ 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.
|
||||||
func Retries(i int) Option {
|
func Retries(i int) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@@ -341,12 +373,35 @@ func DialTimeout(d time.Duration) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithExchange sets the exchange to route a message through
|
// WithExchange sets the exchange to route a message through
|
||||||
|
// Deprecated
|
||||||
func WithExchange(e string) PublishOption {
|
func WithExchange(e string) PublishOption {
|
||||||
return func(o *PublishOptions) {
|
return func(o *PublishOptions) {
|
||||||
o.Exchange = e
|
o.Exchange = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublishExchange sets the exchange to route a message through
|
||||||
|
func PublishExchange(e string) PublishOption {
|
||||||
|
return func(o *PublishOptions) {
|
||||||
|
o.Exchange = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithBodyOnly publish only message body
|
||||||
|
// DERECATED
|
||||||
|
func WithBodyOnly(b bool) PublishOption {
|
||||||
|
return func(o *PublishOptions) {
|
||||||
|
o.BodyOnly = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublishBodyOnly publish only message body
|
||||||
|
func PublishBodyOnly(b bool) PublishOption {
|
||||||
|
return func(o *PublishOptions) {
|
||||||
|
o.BodyOnly = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PublishContext sets the context in publish options
|
// PublishContext sets the context in publish options
|
||||||
func PublishContext(ctx context.Context) PublishOption {
|
func PublishContext(ctx context.Context) PublishOption {
|
||||||
return func(o *PublishOptions) {
|
return func(o *PublishOptions) {
|
||||||
@@ -354,6 +409,13 @@ func PublishContext(ctx context.Context) PublishOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithContentType specifies call content type
|
||||||
|
func WithContentType(ct string) CallOption {
|
||||||
|
return func(o *CallOptions) {
|
||||||
|
o.ContentType = ct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithAddress sets the remote addresses to use rather than using service discovery
|
// WithAddress sets the remote addresses to use rather than using service discovery
|
||||||
func WithAddress(a ...string) CallOption {
|
func WithAddress(a ...string) CallOption {
|
||||||
return func(o *CallOptions) {
|
return func(o *CallOptions) {
|
||||||
@@ -417,9 +479,9 @@ func WithDialTimeout(d time.Duration) CallOption {
|
|||||||
|
|
||||||
// WithAuthToken is a CallOption which overrides the
|
// WithAuthToken is a CallOption which overrides the
|
||||||
// authorization header with the services own auth token
|
// authorization header with the services own auth token
|
||||||
func WithAuthToken() CallOption {
|
func WithAuthToken(t string) CallOption {
|
||||||
return func(o *CallOptions) {
|
return func(o *CallOptions) {
|
||||||
o.AuthToken = true
|
o.AuthToken = t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,22 +514,30 @@ func WithSelectOptions(sops ...selector.SelectOption) CallOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithMessageContentType sets the message content type
|
// WithMessageContentType sets the message content type
|
||||||
|
// Deprecated
|
||||||
func WithMessageContentType(ct string) MessageOption {
|
func WithMessageContentType(ct string) MessageOption {
|
||||||
return func(o *MessageOptions) {
|
return func(o *MessageOptions) {
|
||||||
o.ContentType = ct
|
o.ContentType = ct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithContentType specifies request content type
|
// MessageContentType sets the message content type
|
||||||
func WithContentType(ct string) RequestOption {
|
func MessageContentType(ct string) MessageOption {
|
||||||
return func(o *RequestOptions) {
|
return func(o *MessageOptions) {
|
||||||
o.ContentType = ct
|
o.ContentType = ct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StreamingRequest specifies that request is streaming
|
// StreamingRequest specifies that request is streaming
|
||||||
func StreamingRequest() RequestOption {
|
func StreamingRequest(b bool) RequestOption {
|
||||||
return func(o *RequestOptions) {
|
return func(o *RequestOptions) {
|
||||||
o.Stream = true
|
o.Stream = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestContentType specifies request content type
|
||||||
|
func RequestContentType(ct string) RequestOption {
|
||||||
|
return func(o *RequestOptions) {
|
||||||
|
o.ContentType = ct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,12 +5,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type testRequest struct {
|
type testRequest struct {
|
||||||
|
codec codec.Codec
|
||||||
|
body interface{}
|
||||||
service string
|
service string
|
||||||
method string
|
method string
|
||||||
endpoint string
|
endpoint string
|
||||||
contentType string
|
contentType string
|
||||||
codec codec.Codec
|
|
||||||
body interface{}
|
|
||||||
opts RequestOptions
|
opts RequestOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,6 +28,8 @@ var (
|
|||||||
DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb
|
DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb
|
||||||
// DefaultCodec is the global default codec
|
// DefaultCodec is the global default codec
|
||||||
DefaultCodec Codec = NewCodec()
|
DefaultCodec Codec = NewCodec()
|
||||||
|
// DefaultTagName specifies struct tag name to control codec Marshal/Unmarshal
|
||||||
|
DefaultTagName = "codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageType specifies message type for codec
|
// MessageType specifies message type for codec
|
||||||
@@ -51,16 +53,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;
|
||||||
|
}
|
@@ -5,13 +5,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
||||||
|
@@ -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
|
||||||
|
@@ -4,12 +4,14 @@ package config
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultConfig default config
|
||||||
// DefaultConfig default config
|
var DefaultConfig Config = NewConfig()
|
||||||
DefaultConfig Config = NewConfig()
|
|
||||||
)
|
// DefaultWatcherInterval default interval for poll changes
|
||||||
|
var DefaultWatcherInterval = 5 * time.Second
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrCodecMissing is returned when codec needed and not specified
|
// ErrCodecMissing is returned when codec needed and not specified
|
||||||
@@ -22,34 +24,38 @@ var (
|
|||||||
|
|
||||||
// Config is an interface abstraction for dynamic configuration
|
// Config is an interface abstraction for dynamic configuration
|
||||||
type Config interface {
|
type Config interface {
|
||||||
|
// Name returns name of config
|
||||||
Name() string
|
Name() string
|
||||||
// Init the config
|
// Init the config
|
||||||
Init(opts ...Option) error
|
Init(opts ...Option) error
|
||||||
// Options in the config
|
// Options in the config
|
||||||
Options() Options
|
Options() Options
|
||||||
// Load config from sources
|
// Load config from sources
|
||||||
Load(context.Context) error
|
Load(context.Context, ...LoadOption) error
|
||||||
// Save config to sources
|
// Save config to sources
|
||||||
Save(context.Context) error
|
Save(context.Context, ...SaveOption) error
|
||||||
// Watch a value for changes
|
// Watch a config for changes
|
||||||
// Watch(interface{}) (Watcher, error)
|
Watch(context.Context, ...WatchOption) (Watcher, error)
|
||||||
|
// String returns config type name
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watcher is the config watcher
|
// Watcher is the config watcher
|
||||||
//type Watcher interface {
|
type Watcher interface {
|
||||||
// Next() (, error)
|
// Next blocks until update happens or error returned
|
||||||
// Stop() error
|
Next() (map[string]interface{}, error)
|
||||||
//}
|
// Stop stops watcher
|
||||||
|
Stop() error
|
||||||
|
}
|
||||||
|
|
||||||
// Load loads config from config sources
|
// Load loads config from config sources
|
||||||
func Load(ctx context.Context, cs ...Config) error {
|
func Load(ctx context.Context, cs []Config, opts ...LoadOption) error {
|
||||||
var err error
|
var err error
|
||||||
for _, c := range cs {
|
for _, c := range cs {
|
||||||
if err = c.Init(); err != nil {
|
if err = c.Init(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = c.Load(ctx); err != nil {
|
if err = c.Load(ctx, opts...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
rutil "github.com/unistack-org/micro/v3/util/reflect"
|
rutil "github.com/unistack-org/micro/v3/util/reflect"
|
||||||
@@ -25,18 +26,31 @@ func (c *defaultConfig) Init(opts ...Option) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultConfig) Load(ctx context.Context) error {
|
func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
|
||||||
for _, fn := range c.opts.BeforeLoad {
|
for _, fn := range c.opts.BeforeLoad {
|
||||||
if err := fn(ctx, c); err != nil && !c.opts.AllowFail {
|
if err := fn(ctx, c); err != nil && !c.opts.AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
src, err := rutil.Zero(c.opts.Struct)
|
options := NewLoadOptions(opts...)
|
||||||
|
mopts := []func(*mergo.Config){mergo.WithTypeCheck}
|
||||||
|
if options.Override {
|
||||||
|
mopts = append(mopts, mergo.WithOverride)
|
||||||
|
}
|
||||||
|
if options.Append {
|
||||||
|
mopts = append(mopts, mergo.WithAppendSlice)
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := c.opts.Struct
|
||||||
|
if options.Struct != nil {
|
||||||
|
dst = options.Struct
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := rutil.Zero(dst)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
valueOf := reflect.ValueOf(src)
|
if err = fillValues(reflect.ValueOf(src), c.opts.StructTag); err == nil {
|
||||||
if err = c.fillValues(ctx, valueOf); err == nil {
|
err = mergo.Merge(dst, src, mopts...)
|
||||||
err = mergo.Merge(c.opts.Struct, src, mergo.WithOverride, mergo.WithTypeCheck, mergo.WithAppendSlice)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +68,7 @@ func (c *defaultConfig) Load(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val string) error {
|
func fillValue(value reflect.Value, val string) error {
|
||||||
if !rutil.IsEmpty(value) {
|
if !rutil.IsEmpty(value) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -71,10 +85,10 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
|
|||||||
kv := strings.FieldsFunc(nval, func(c rune) bool { return c == '=' })
|
kv := strings.FieldsFunc(nval, func(c rune) bool { return c == '=' })
|
||||||
mkey := reflect.Indirect(reflect.New(kt))
|
mkey := reflect.Indirect(reflect.New(kt))
|
||||||
mval := reflect.Indirect(reflect.New(et))
|
mval := reflect.Indirect(reflect.New(et))
|
||||||
if err := c.fillValue(ctx, mkey, kv[0]); err != nil {
|
if err := fillValue(mkey, kv[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := c.fillValue(ctx, mval, kv[1]); err != nil {
|
if err := fillValue(mval, kv[1]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
value.SetMapIndex(mkey, mval)
|
value.SetMapIndex(mkey, mval)
|
||||||
@@ -84,7 +98,7 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
|
|||||||
value.Set(reflect.MakeSlice(reflect.SliceOf(value.Type().Elem()), len(nvals), len(nvals)))
|
value.Set(reflect.MakeSlice(reflect.SliceOf(value.Type().Elem()), len(nvals), len(nvals)))
|
||||||
for idx, nval := range nvals {
|
for idx, nval := range nvals {
|
||||||
nvalue := reflect.Indirect(reflect.New(value.Type().Elem()))
|
nvalue := reflect.Indirect(reflect.New(value.Type().Elem()))
|
||||||
if err := c.fillValue(ctx, nvalue, nval); err != nil {
|
if err := fillValue(nvalue, nval); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
value.Index(idx).Set(nvalue)
|
value.Index(idx).Set(nvalue)
|
||||||
@@ -173,7 +187,7 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultConfig) fillValues(ctx context.Context, valueOf reflect.Value) error {
|
func fillValues(valueOf reflect.Value, tname string) error {
|
||||||
var values reflect.Value
|
var values reflect.Value
|
||||||
|
|
||||||
if valueOf.Kind() == reflect.Ptr {
|
if valueOf.Kind() == reflect.Ptr {
|
||||||
@@ -200,7 +214,7 @@ func (c *defaultConfig) fillValues(ctx context.Context, valueOf reflect.Value) e
|
|||||||
switch value.Kind() {
|
switch value.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
value.Set(reflect.Indirect(reflect.New(value.Type())))
|
value.Set(reflect.Indirect(reflect.New(value.Type())))
|
||||||
if err := c.fillValues(ctx, value); err != nil {
|
if err := fillValues(value, tname); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -214,17 +228,17 @@ func (c *defaultConfig) fillValues(ctx context.Context, valueOf reflect.Value) e
|
|||||||
value.Set(reflect.New(value.Type().Elem()))
|
value.Set(reflect.New(value.Type().Elem()))
|
||||||
}
|
}
|
||||||
value = value.Elem()
|
value = value.Elem()
|
||||||
if err := c.fillValues(ctx, value); err != nil {
|
if err := fillValues(value, tname); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tag, ok := field.Tag.Lookup(c.opts.StructTag)
|
tag, ok := field.Tag.Lookup(tname)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.fillValue(ctx, value, tag); err != nil {
|
if err := fillValue(value, tag); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +246,7 @@ func (c *defaultConfig) fillValues(ctx context.Context, valueOf reflect.Value) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultConfig) Save(ctx context.Context) error {
|
func (c *defaultConfig) Save(ctx context.Context, opts ...SaveOption) error {
|
||||||
for _, fn := range c.opts.BeforeSave {
|
for _, fn := range c.opts.BeforeSave {
|
||||||
if err := fn(ctx, c); err != nil && !c.opts.AllowFail {
|
if err := fn(ctx, c); err != nil && !c.opts.AllowFail {
|
||||||
return err
|
return err
|
||||||
@@ -256,6 +270,20 @@ func (c *defaultConfig) Name() string {
|
|||||||
return c.opts.Name
|
return c.opts.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *defaultConfig) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) {
|
||||||
|
w := &defaultWatcher{
|
||||||
|
opts: c.opts,
|
||||||
|
wopts: NewWatchOptions(opts...),
|
||||||
|
done: make(chan bool),
|
||||||
|
vchan: make(chan map[string]interface{}),
|
||||||
|
echan: make(chan error),
|
||||||
|
}
|
||||||
|
|
||||||
|
go w.run()
|
||||||
|
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewConfig returns new default config source
|
// NewConfig returns new default config source
|
||||||
func NewConfig(opts ...Option) Config {
|
func NewConfig(opts ...Option) Config {
|
||||||
options := NewOptions(opts...)
|
options := NewOptions(opts...)
|
||||||
@@ -264,3 +292,73 @@ func NewConfig(opts ...Option) Config {
|
|||||||
}
|
}
|
||||||
return &defaultConfig{opts: options}
|
return &defaultConfig{opts: options}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type defaultWatcher struct {
|
||||||
|
opts Options
|
||||||
|
wopts WatchOptions
|
||||||
|
done chan bool
|
||||||
|
ticker *time.Ticker
|
||||||
|
vchan chan map[string]interface{}
|
||||||
|
echan chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *defaultWatcher) run() {
|
||||||
|
ticker := time.NewTicker(w.wopts.Interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
src := w.opts.Struct
|
||||||
|
if w.wopts.Struct != nil {
|
||||||
|
src = w.wopts.Struct
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-w.done:
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
dst, err := rutil.Zero(src)
|
||||||
|
if err == nil {
|
||||||
|
err = fillValues(reflect.ValueOf(dst), w.opts.StructTag)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
w.echan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srcmp, err := rutil.StructFieldsMap(src)
|
||||||
|
if err != nil {
|
||||||
|
w.echan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dstmp, err := rutil.StructFieldsMap(dst)
|
||||||
|
if err != nil {
|
||||||
|
w.echan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for sk, sv := range srcmp {
|
||||||
|
if reflect.DeepEqual(dstmp[sk], sv) {
|
||||||
|
delete(dstmp, sk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.vchan <- dstmp
|
||||||
|
src = dst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *defaultWatcher) Next() (map[string]interface{}, error) {
|
||||||
|
select {
|
||||||
|
case <-w.done:
|
||||||
|
break
|
||||||
|
case v, ok := <-w.vchan:
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, ErrWatcherStopped
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *defaultWatcher) Stop() error {
|
||||||
|
close(w.done)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -4,36 +4,88 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/config"
|
"github.com/unistack-org/micro/v3/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Cfg struct {
|
type Cfg struct {
|
||||||
StringValue string `default:"string_value"`
|
StringValue string `default:"string_value"`
|
||||||
IntValue int `default:"99"`
|
|
||||||
IgnoreValue string `json:"-"`
|
IgnoreValue string `json:"-"`
|
||||||
StructValue struct {
|
StructValue struct {
|
||||||
StringValue string `default:"string_value"`
|
StringValue string `default:"string_value"`
|
||||||
}
|
}
|
||||||
|
IntValue int `default:"99"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWatch(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
conf := &Cfg{IntValue: 10}
|
||||||
|
|
||||||
|
cfg := config.NewConfig(config.Struct(conf))
|
||||||
|
if err := cfg.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := cfg.Load(ctx); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := cfg.Watch(ctx, config.WatchInterval(500*time.Millisecond))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = w.Stop()
|
||||||
|
}()
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
mp, err := w.Next()
|
||||||
|
if err != nil && err != config.ErrWatcherStopped {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err == config.ErrWatcherStopped {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(mp) != 1 {
|
||||||
|
t.Fatal(fmt.Errorf("default watcher err: %v", mp))
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := mp["IntValue"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatal(fmt.Errorf("default watcher err: %v", v))
|
||||||
|
}
|
||||||
|
if nv, ok := v.(int); !ok || nv != 99 {
|
||||||
|
t.Fatal(fmt.Errorf("default watcher err: %v", v))
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-done
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefault(t *testing.T) {
|
func TestDefault(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
conf := &Cfg{IntValue: 10}
|
conf := &Cfg{IntValue: 10}
|
||||||
blfn := func(ctx context.Context, cfg config.Config) error {
|
blfn := func(ctx context.Context, cfg config.Config) error {
|
||||||
conf, ok := cfg.Options().Struct.(*Cfg)
|
nconf, ok := cfg.Options().Struct.(*Cfg)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("failed to get Struct from options: %v", cfg.Options())
|
return fmt.Errorf("failed to get Struct from options: %v", cfg.Options())
|
||||||
}
|
}
|
||||||
conf.StringValue = "before_load"
|
nconf.StringValue = "before_load"
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
alfn := func(ctx context.Context, cfg config.Config) error {
|
alfn := func(ctx context.Context, cfg config.Config) error {
|
||||||
conf, ok := cfg.Options().Struct.(*Cfg)
|
nconf, ok := cfg.Options().Struct.(*Cfg)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("failed to get Struct from options: %v", cfg.Options())
|
return fmt.Errorf("failed to get Struct from options: %v", cfg.Options())
|
||||||
}
|
}
|
||||||
conf.StringValue = "after_load"
|
nconf.StringValue = "after_load"
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +99,6 @@ func TestDefault(t *testing.T) {
|
|||||||
if conf.StringValue != "after_load" {
|
if conf.StringValue != "after_load" {
|
||||||
t.Fatal("AfterLoad option not working")
|
t.Fatal("AfterLoad option not working")
|
||||||
}
|
}
|
||||||
|
_ = conf
|
||||||
t.Logf("%#+v\n", conf)
|
//t.Logf("%#+v\n", conf)
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/codec"
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
"github.com/unistack-org/micro/v3/logger"
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
@@ -11,26 +12,32 @@ import (
|
|||||||
|
|
||||||
// Options hold the config options
|
// 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
|
// Option function signature
|
||||||
@@ -51,6 +58,69 @@ func NewOptions(opts ...Option) Options {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadOption function signature
|
||||||
|
type LoadOption func(o *LoadOptions)
|
||||||
|
|
||||||
|
// LoadOptions struct
|
||||||
|
type LoadOptions struct {
|
||||||
|
Struct interface{}
|
||||||
|
Override bool
|
||||||
|
Append bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoadOptions(opts ...LoadOption) LoadOptions {
|
||||||
|
options := LoadOptions{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadOverride override values when load
|
||||||
|
func LoadOverride(b bool) LoadOption {
|
||||||
|
return func(o *LoadOptions) {
|
||||||
|
o.Override = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadAppend override values when load
|
||||||
|
func LoadAppend(b bool) LoadOption {
|
||||||
|
return func(o *LoadOptions) {
|
||||||
|
o.Append = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadStruct override struct for loading
|
||||||
|
func LoadStruct(src interface{}) LoadOption {
|
||||||
|
return func(o *LoadOptions) {
|
||||||
|
o.Struct = src
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveOption function signature
|
||||||
|
type SaveOption func(o *SaveOptions)
|
||||||
|
|
||||||
|
// SaveOptions struct
|
||||||
|
type SaveOptions struct {
|
||||||
|
Struct interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveStruct override struct for save to config
|
||||||
|
func SaveStruct(src interface{}) SaveOption {
|
||||||
|
return func(o *SaveOptions) {
|
||||||
|
o.Struct = src
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSaveOptions fill SaveOptions struct
|
||||||
|
func NewSaveOptions(opts ...SaveOption) SaveOptions {
|
||||||
|
options := SaveOptions{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
// AllowFail allows config source to fail
|
// AllowFail allows config source to fail
|
||||||
func AllowFail(b bool) Option {
|
func AllowFail(b bool) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@@ -134,3 +204,56 @@ func Name(n string) Option {
|
|||||||
o.Name = n
|
o.Name = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WatchOptions struuct
|
||||||
|
type WatchOptions struct {
|
||||||
|
// Context used by non default options
|
||||||
|
Context context.Context
|
||||||
|
// Coalesce multiple events to one
|
||||||
|
Coalesce bool
|
||||||
|
// Interval to periodically pull changes if config source not supports async notify
|
||||||
|
Interval time.Duration
|
||||||
|
// Struct for filling
|
||||||
|
Struct interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WatchOption func(*WatchOptions)
|
||||||
|
|
||||||
|
func NewWatchOptions(opts ...WatchOption) WatchOptions {
|
||||||
|
options := WatchOptions{
|
||||||
|
Context: context.Background(),
|
||||||
|
Interval: DefaultWatcherInterval,
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchContext pass context
|
||||||
|
func WatchContext(ctx context.Context) WatchOption {
|
||||||
|
return func(o *WatchOptions) {
|
||||||
|
o.Context = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchCoalesce controls watch event combining
|
||||||
|
func WatchCoalesce(b bool) WatchOption {
|
||||||
|
return func(o *WatchOptions) {
|
||||||
|
o.Coalesce = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchInterval specifies time.Duration for pulling changes
|
||||||
|
func WatchInterval(td time.Duration) WatchOption {
|
||||||
|
return func(o *WatchOptions) {
|
||||||
|
o.Interval = td
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchStruct overrides struct for fill
|
||||||
|
func WatchStruct(src interface{}) WatchOption {
|
||||||
|
return func(o *WatchOptions) {
|
||||||
|
o.Struct = src
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,26 +0,0 @@
|
|||||||
package config_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
rutil "github.com/unistack-org/micro/v3/util/reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Value string
|
|
||||||
SubConfig *SubConfig
|
|
||||||
Config *Config
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubConfig struct {
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReflect(t *testing.T) {
|
|
||||||
cfg1 := &Config{Value: "cfg1", Config: &Config{Value: "cfg1_1"}, SubConfig: &SubConfig{Value: "cfg1"}}
|
|
||||||
cfg2, err := rutil.Zero(cfg1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Logf("dst: %#+v\n", cfg2)
|
|
||||||
}
|
|
@@ -37,10 +37,14 @@ var (
|
|||||||
|
|
||||||
// Error type
|
// 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
|
// Error satisfies error interface
|
||||||
@@ -49,7 +53,7 @@ func (e *Error) Error() string {
|
|||||||
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,
|
||||||
|
@@ -17,7 +17,6 @@ func TestFromError(t *testing.T) {
|
|||||||
if merr.Id != "go.micro.test" || merr.Code != 404 {
|
if merr.Id != "go.micro.test" || merr.Code != 404 {
|
||||||
t.Fatalf("invalid conversation %v != %v", err, merr)
|
t.Fatalf("invalid conversation %v != %v", err, merr)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEqual(t *testing.T) {
|
func TestEqual(t *testing.T) {
|
||||||
@@ -32,7 +31,6 @@ func TestEqual(t *testing.T) {
|
|||||||
if Equal(err1, err3) {
|
if Equal(err1, err3) {
|
||||||
t.Fatal("errors must be not equal")
|
t.Fatal("errors must be not equal")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestErrors(t *testing.T) {
|
func TestErrors(t *testing.T) {
|
||||||
|
34
flow/context.go
Normal file
34
flow/context.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package flow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type flowKey struct{}
|
||||||
|
|
||||||
|
// FromContext returns Flow from context
|
||||||
|
func FromContext(ctx context.Context) (Flow, bool) {
|
||||||
|
if ctx == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
c, ok := ctx.Value(flowKey{}).(Flow)
|
||||||
|
return c, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext stores Flow to context
|
||||||
|
func NewContext(ctx context.Context, f Flow) context.Context {
|
||||||
|
if ctx == nil {
|
||||||
|
ctx = context.Background()
|
||||||
|
}
|
||||||
|
return context.WithValue(ctx, flowKey{}, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOption returns a function to setup a context with given value
|
||||||
|
func SetOption(k, v interface{}) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
if o.Context == nil {
|
||||||
|
o.Context = context.Background()
|
||||||
|
}
|
||||||
|
o.Context = context.WithValue(o.Context, k, v)
|
||||||
|
}
|
||||||
|
}
|
@@ -44,10 +44,10 @@ func TestDag(t *testing.T) {
|
|||||||
var steps [][]string
|
var steps [][]string
|
||||||
fn := func(n dag.Vertex, idx int) error {
|
fn := func(n dag.Vertex, idx int) error {
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
steps = make([][]string, 1, 1)
|
steps = make([][]string, 1)
|
||||||
steps[0] = make([]string, 0, 1)
|
steps[0] = make([]string, 0, 1)
|
||||||
} else if idx >= len(steps) {
|
} else if idx >= len(steps) {
|
||||||
tsteps := make([][]string, idx+1, idx+1)
|
tsteps := make([][]string, idx+1)
|
||||||
copy(tsteps, steps)
|
copy(tsteps, steps)
|
||||||
steps = tsteps
|
steps = tsteps
|
||||||
steps[idx] = make([]string, 0, 1)
|
steps[idx] = make([]string, 0, 1)
|
||||||
|
588
flow/default.go
Normal file
588
flow/default.go
Normal file
@@ -0,0 +1,588 @@
|
|||||||
|
package flow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/silas/dag"
|
||||||
|
"github.com/unistack-org/micro/v3/client"
|
||||||
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
|
"github.com/unistack-org/micro/v3/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
type microFlow struct {
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
type microWorkflow struct {
|
||||||
|
id string
|
||||||
|
g *dag.AcyclicGraph
|
||||||
|
init bool
|
||||||
|
sync.RWMutex
|
||||||
|
opts Options
|
||||||
|
steps map[string]Step
|
||||||
|
status Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) ID() string {
|
||||||
|
return w.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) Steps() ([][]Step, error) {
|
||||||
|
return w.getSteps("", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) Status() Status {
|
||||||
|
return w.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) AppendSteps(steps ...Step) error {
|
||||||
|
w.Lock()
|
||||||
|
|
||||||
|
for _, s := range steps {
|
||||||
|
w.steps[s.String()] = s
|
||||||
|
w.g.Add(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dst := range steps {
|
||||||
|
for _, req := range dst.Requires() {
|
||||||
|
src, ok := w.steps[req]
|
||||||
|
if !ok {
|
||||||
|
return ErrStepNotExists
|
||||||
|
}
|
||||||
|
w.g.Connect(dag.BasicEdge(src, dst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.g.Validate(); err != nil {
|
||||||
|
w.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.g.TransitiveReduction()
|
||||||
|
|
||||||
|
w.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) RemoveSteps(steps ...Step) error {
|
||||||
|
// TODO: handle case when some step requires or required by removed step
|
||||||
|
|
||||||
|
w.Lock()
|
||||||
|
|
||||||
|
for _, s := range steps {
|
||||||
|
delete(w.steps, s.String())
|
||||||
|
w.g.Remove(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dst := range steps {
|
||||||
|
for _, req := range dst.Requires() {
|
||||||
|
src, ok := w.steps[req]
|
||||||
|
if !ok {
|
||||||
|
return ErrStepNotExists
|
||||||
|
}
|
||||||
|
w.g.Connect(dag.BasicEdge(src, dst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.g.Validate(); err != nil {
|
||||||
|
w.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.g.TransitiveReduction()
|
||||||
|
|
||||||
|
w.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) getSteps(start string, reverse bool) ([][]Step, error) {
|
||||||
|
var steps [][]Step
|
||||||
|
var root dag.Vertex
|
||||||
|
var err error
|
||||||
|
|
||||||
|
fn := func(n dag.Vertex, idx int) error {
|
||||||
|
if idx == 0 {
|
||||||
|
steps = make([][]Step, 1)
|
||||||
|
steps[0] = make([]Step, 0, 1)
|
||||||
|
} else if idx >= len(steps) {
|
||||||
|
tsteps := make([][]Step, idx+1)
|
||||||
|
copy(tsteps, steps)
|
||||||
|
steps = tsteps
|
||||||
|
steps[idx] = make([]Step, 0, 1)
|
||||||
|
}
|
||||||
|
steps[idx] = append(steps[idx], n.(Step))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if start != "" {
|
||||||
|
var ok bool
|
||||||
|
w.RLock()
|
||||||
|
root, ok = w.steps[start]
|
||||||
|
w.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrStepNotExists
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root, err = w.g.Root()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reverse {
|
||||||
|
err = w.g.SortedReverseDepthFirstWalk([]dag.Vertex{root}, fn)
|
||||||
|
} else {
|
||||||
|
err = w.g.SortedDepthFirstWalk([]dag.Vertex{root}, fn)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return steps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) Abort(ctx context.Context, eid string) error {
|
||||||
|
workflowStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("workflows", eid))
|
||||||
|
return workflowStore.Write(ctx, "status", &codec.Frame{Data: []byte(StatusAborted.String())})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) Suspend(ctx context.Context, eid string) error {
|
||||||
|
workflowStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("workflows", eid))
|
||||||
|
return workflowStore.Write(ctx, "status", &codec.Frame{Data: []byte(StatusSuspend.String())})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) Resume(ctx context.Context, eid string) error {
|
||||||
|
workflowStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("workflows", eid))
|
||||||
|
return workflowStore.Write(ctx, "status", &codec.Frame{Data: []byte(StatusRunning.String())})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (string, error) {
|
||||||
|
w.Lock()
|
||||||
|
if !w.init {
|
||||||
|
if err := w.g.Validate(); err != nil {
|
||||||
|
w.Unlock()
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
w.g.TransitiveReduction()
|
||||||
|
w.init = true
|
||||||
|
}
|
||||||
|
w.Unlock()
|
||||||
|
|
||||||
|
uid, err := uuid.NewRandom()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
eid := uid.String()
|
||||||
|
|
||||||
|
stepStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("steps", eid))
|
||||||
|
workflowStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("workflows", eid))
|
||||||
|
|
||||||
|
options := NewExecuteOptions(opts...)
|
||||||
|
|
||||||
|
steps, err := w.getSteps(options.Start, options.Reverse)
|
||||||
|
if err != nil {
|
||||||
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusPending.String())}); werr != nil {
|
||||||
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
cherr := make(chan error, 1)
|
||||||
|
chstatus := make(chan Status, 1)
|
||||||
|
|
||||||
|
nctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
nopts := make([]ExecuteOption, 0, len(opts)+5)
|
||||||
|
|
||||||
|
nopts = append(nopts,
|
||||||
|
ExecuteClient(w.opts.Client),
|
||||||
|
ExecuteTracer(w.opts.Tracer),
|
||||||
|
ExecuteLogger(w.opts.Logger),
|
||||||
|
ExecuteMeter(w.opts.Meter),
|
||||||
|
)
|
||||||
|
nopts = append(nopts, opts...)
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
|
||||||
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
|
return eid, werr
|
||||||
|
}
|
||||||
|
for idx := range steps {
|
||||||
|
for nidx := range steps[idx] {
|
||||||
|
cstep := steps[idx][nidx]
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusPending.String())}); werr != nil {
|
||||||
|
return eid, werr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for idx := range steps {
|
||||||
|
for nidx := range steps[idx] {
|
||||||
|
wStatus := &codec.Frame{}
|
||||||
|
if werr := workflowStore.Read(w.opts.Context, "status", wStatus); werr != nil {
|
||||||
|
cherr <- werr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if status := StringStatus[string(wStatus.Data)]; status != StatusRunning {
|
||||||
|
chstatus <- status
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if w.opts.Logger.V(logger.TraceLevel) {
|
||||||
|
w.opts.Logger.Tracef(nctx, "will be executed %v", steps[idx][nidx])
|
||||||
|
}
|
||||||
|
cstep := steps[idx][nidx]
|
||||||
|
if len(cstep.Requires()) == 0 {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(step Step) {
|
||||||
|
defer wg.Done()
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "req"), req); werr != nil {
|
||||||
|
cherr <- werr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "status"), &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
|
||||||
|
cherr <- werr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rsp, serr := step.Execute(nctx, req, nopts...)
|
||||||
|
if serr != nil {
|
||||||
|
step.SetStatus(StatusFailure)
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "rsp"), serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
|
}
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "status"), &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
|
}
|
||||||
|
cherr <- serr
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "rsp"), rsp); werr != nil {
|
||||||
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
|
cherr <- werr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "status"), &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||||
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
|
cherr <- werr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(cstep)
|
||||||
|
wg.Wait()
|
||||||
|
} else {
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "req"), req); werr != nil {
|
||||||
|
cherr <- werr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
|
||||||
|
cherr <- werr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rsp, serr := cstep.Execute(nctx, req, nopts...)
|
||||||
|
if serr != nil {
|
||||||
|
cstep.SetStatus(StatusFailure)
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "rsp"), serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
|
}
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
|
}
|
||||||
|
cherr <- serr
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "rsp"), rsp); werr != nil {
|
||||||
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
|
cherr <- werr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||||
|
cherr <- werr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if options.Async {
|
||||||
|
return eid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Tracef(ctx, "wait for finish or error")
|
||||||
|
select {
|
||||||
|
case <-nctx.Done():
|
||||||
|
err = nctx.Err()
|
||||||
|
case cerr := <-cherr:
|
||||||
|
err = cerr
|
||||||
|
case <-done:
|
||||||
|
close(cherr)
|
||||||
|
case <-chstatus:
|
||||||
|
close(chstatus)
|
||||||
|
return uid.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case nctx.Err() != nil:
|
||||||
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusAborted.String())}); werr != nil {
|
||||||
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case err == nil:
|
||||||
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||||
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case err != nil:
|
||||||
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil {
|
||||||
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return uid.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFlow(opts ...Option) Flow {
|
||||||
|
options := NewOptions(opts...)
|
||||||
|
return µFlow{opts: options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *microFlow) Options() Options {
|
||||||
|
return f.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *microFlow) Init(opts ...Option) error {
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&f.opts)
|
||||||
|
}
|
||||||
|
if err := f.opts.Client.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f.opts.Tracer.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f.opts.Logger.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f.opts.Meter.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f.opts.Store.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *microFlow) WorkflowList(ctx context.Context) ([]Workflow, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *microFlow) WorkflowCreate(ctx context.Context, id string, steps ...Step) (Workflow, error) {
|
||||||
|
w := µWorkflow{opts: f.opts, id: id, g: &dag.AcyclicGraph{}, steps: make(map[string]Step, len(steps))}
|
||||||
|
|
||||||
|
for _, s := range steps {
|
||||||
|
w.steps[s.String()] = s
|
||||||
|
w.g.Add(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dst := range steps {
|
||||||
|
for _, req := range dst.Requires() {
|
||||||
|
src, ok := w.steps[req]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrStepNotExists
|
||||||
|
}
|
||||||
|
w.g.Connect(dag.BasicEdge(src, dst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.g.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
w.g.TransitiveReduction()
|
||||||
|
|
||||||
|
w.init = true
|
||||||
|
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *microFlow) WorkflowRemove(ctx context.Context, id string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *microFlow) WorkflowSave(ctx context.Context, w Workflow) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *microFlow) WorkflowLoad(ctx context.Context, id string) (Workflow, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type microCallStep struct {
|
||||||
|
opts StepOptions
|
||||||
|
service string
|
||||||
|
method string
|
||||||
|
rsp *Message
|
||||||
|
req *Message
|
||||||
|
status Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) Request() *Message {
|
||||||
|
return s.req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) Response() *Message {
|
||||||
|
return s.rsp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) ID() string {
|
||||||
|
return s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) Options() StepOptions {
|
||||||
|
return s.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) Endpoint() string {
|
||||||
|
return s.method
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) Requires() []string {
|
||||||
|
return s.opts.Requires
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) Require(steps ...Step) error {
|
||||||
|
for _, step := range steps {
|
||||||
|
s.opts.Requires = append(s.opts.Requires, step.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) String() string {
|
||||||
|
if s.opts.ID != "" {
|
||||||
|
return s.opts.ID
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s.%s", s.service, s.method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) Name() string {
|
||||||
|
return s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) Hashcode() interface{} {
|
||||||
|
return s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) GetStatus() Status {
|
||||||
|
return s.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) SetStatus(status Status) {
|
||||||
|
s.status = status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microCallStep) Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (*Message, error) {
|
||||||
|
options := NewExecuteOptions(opts...)
|
||||||
|
if options.Client == nil {
|
||||||
|
return nil, ErrMissingClient
|
||||||
|
}
|
||||||
|
rsp := &codec.Frame{}
|
||||||
|
copts := []client.CallOption{client.WithRetries(0)}
|
||||||
|
if options.Timeout > 0 {
|
||||||
|
copts = append(copts, client.WithRequestTimeout(options.Timeout), client.WithDialTimeout(options.Timeout))
|
||||||
|
}
|
||||||
|
nctx := metadata.NewOutgoingContext(ctx, req.Header)
|
||||||
|
err := options.Client.Call(nctx, options.Client.NewRequest(s.service, s.method, &codec.Frame{Data: req.Body}), rsp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
md, _ := metadata.FromOutgoingContext(nctx)
|
||||||
|
return &Message{Header: md, Body: rsp.Data}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type microPublishStep struct {
|
||||||
|
opts StepOptions
|
||||||
|
topic string
|
||||||
|
req *Message
|
||||||
|
rsp *Message
|
||||||
|
status Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) Request() *Message {
|
||||||
|
return s.req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) Response() *Message {
|
||||||
|
return s.rsp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) ID() string {
|
||||||
|
return s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) Options() StepOptions {
|
||||||
|
return s.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) Endpoint() string {
|
||||||
|
return s.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) Requires() []string {
|
||||||
|
return s.opts.Requires
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) Require(steps ...Step) error {
|
||||||
|
for _, step := range steps {
|
||||||
|
s.opts.Requires = append(s.opts.Requires, step.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) String() string {
|
||||||
|
if s.opts.ID != "" {
|
||||||
|
return s.opts.ID
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s", s.topic)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) Name() string {
|
||||||
|
return s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) Hashcode() interface{} {
|
||||||
|
return s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) GetStatus() Status {
|
||||||
|
return s.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) SetStatus(status Status) {
|
||||||
|
s.status = status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *microPublishStep) Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (*Message, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCallStep(service string, name string, method string, opts ...StepOption) Step {
|
||||||
|
options := NewStepOptions(opts...)
|
||||||
|
return µCallStep{service: service, method: name + "." + method, opts: options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPublishStep(topic string, opts ...StepOption) Step {
|
||||||
|
options := NewStepOptions(opts...)
|
||||||
|
return µPublishStep{topic: topic, opts: options}
|
||||||
|
}
|
153
flow/flow.go
153
flow/flow.go
@@ -1,7 +1,152 @@
|
|||||||
// Package flow is an interface used for saga pattern messaging
|
// Package flow is an interface used for saga pattern microservice workflow
|
||||||
package flow
|
package flow
|
||||||
|
|
||||||
type Step interface {
|
import (
|
||||||
// Endpoint returns service_name.service_method
|
"context"
|
||||||
Endpoint() string
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrStepNotExists = errors.New("step not exists")
|
||||||
|
ErrMissingClient = errors.New("client not set")
|
||||||
|
)
|
||||||
|
|
||||||
|
// RawMessage is a raw encoded JSON value.
|
||||||
|
// It implements Marshaler and Unmarshaler and can be used to delay decoding or precompute a encoding.
|
||||||
|
type RawMessage []byte
|
||||||
|
|
||||||
|
// MarshalJSON returns m as the JSON encoding of m.
|
||||||
|
func (m *RawMessage) MarshalJSON() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
return *m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON sets *m to a copy of data.
|
||||||
|
func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
||||||
|
if m == nil {
|
||||||
|
return errors.New("RawMessage UnmarshalJSON on nil pointer")
|
||||||
|
}
|
||||||
|
*m = append((*m)[0:0], data...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Header metadata.Metadata
|
||||||
|
Body RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step represents dedicated workflow step
|
||||||
|
type Step interface {
|
||||||
|
// ID returns step id
|
||||||
|
ID() string
|
||||||
|
// Endpoint returns rpc endpoint service_name.service_method or broker topic
|
||||||
|
Endpoint() string
|
||||||
|
// Execute step run
|
||||||
|
Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (*Message, error)
|
||||||
|
// Requires returns dependent steps
|
||||||
|
Requires() []string
|
||||||
|
// Options returns step options
|
||||||
|
Options() StepOptions
|
||||||
|
// Require add required steps
|
||||||
|
Require(steps ...Step) error
|
||||||
|
// String
|
||||||
|
String() string
|
||||||
|
// GetStatus returns step status
|
||||||
|
GetStatus() Status
|
||||||
|
// SetStatus sets the step status
|
||||||
|
SetStatus(Status)
|
||||||
|
// Request returns step request message
|
||||||
|
Request() *Message
|
||||||
|
// Response returns step response message
|
||||||
|
Response() *Message
|
||||||
|
}
|
||||||
|
|
||||||
|
type Status int
|
||||||
|
|
||||||
|
func (status Status) String() string {
|
||||||
|
return StatusString[status]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatusPending Status = iota
|
||||||
|
StatusRunning
|
||||||
|
StatusFailure
|
||||||
|
StatusSuccess
|
||||||
|
StatusAborted
|
||||||
|
StatusSuspend
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
StatusString = map[Status]string{
|
||||||
|
StatusPending: "StatusPending",
|
||||||
|
StatusRunning: "StatusRunning",
|
||||||
|
StatusFailure: "StatusFailure",
|
||||||
|
StatusSuccess: "StatusSuccess",
|
||||||
|
StatusAborted: "StatusAborted",
|
||||||
|
StatusSuspend: "StatusSuspend",
|
||||||
|
}
|
||||||
|
StringStatus = map[string]Status{
|
||||||
|
"StatusPending": StatusPending,
|
||||||
|
"StatusRunning": StatusRunning,
|
||||||
|
"StatusFailure": StatusFailure,
|
||||||
|
"StatusSuccess": StatusSuccess,
|
||||||
|
"StatusAborted": StatusAborted,
|
||||||
|
"StatusSuspend": StatusSuspend,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Workflow contains all steps to execute
|
||||||
|
type Workflow interface {
|
||||||
|
// ID returns id of the workflow
|
||||||
|
ID() string
|
||||||
|
// Execute workflow with args, return execution id and error
|
||||||
|
Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (string, error)
|
||||||
|
// RemoveSteps remove steps from workflow
|
||||||
|
RemoveSteps(steps ...Step) error
|
||||||
|
// AppendSteps append steps to workflow
|
||||||
|
AppendSteps(steps ...Step) error
|
||||||
|
// Status returns workflow status
|
||||||
|
Status() Status
|
||||||
|
// Steps returns steps slice where parallel steps returned on the same level
|
||||||
|
Steps() ([][]Step, error)
|
||||||
|
// Suspend suspends execution
|
||||||
|
Suspend(ctx context.Context, eid string) error
|
||||||
|
// Resume resumes execution
|
||||||
|
Resume(ctx context.Context, eid string) error
|
||||||
|
// Abort abort execution
|
||||||
|
Abort(ctx context.Context, eid string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flow the base interface to interact with workflows
|
||||||
|
type Flow interface {
|
||||||
|
// Options returns options
|
||||||
|
Options() Options
|
||||||
|
// Init initialize
|
||||||
|
Init(...Option) error
|
||||||
|
// WorkflowCreate creates new workflow with specific id and steps
|
||||||
|
WorkflowCreate(ctx context.Context, id string, steps ...Step) (Workflow, error)
|
||||||
|
// WorkflowSave saves workflow
|
||||||
|
WorkflowSave(ctx context.Context, w Workflow) error
|
||||||
|
// WorkflowLoad loads workflow with specific id
|
||||||
|
WorkflowLoad(ctx context.Context, id string) (Workflow, error)
|
||||||
|
// WorkflowList lists all workflows
|
||||||
|
WorkflowList(ctx context.Context) ([]Workflow, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
flowMu sync.Mutex
|
||||||
|
atomicSteps atomic.Value
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterStep(step Step) {
|
||||||
|
flowMu.Lock()
|
||||||
|
steps, _ := atomicSteps.Load().([]Step)
|
||||||
|
atomicSteps.Store(append(steps, step))
|
||||||
|
flowMu.Unlock()
|
||||||
}
|
}
|
||||||
|
230
flow/options.go
Normal file
230
flow/options.go
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
package flow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/client"
|
||||||
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
|
"github.com/unistack-org/micro/v3/meter"
|
||||||
|
"github.com/unistack-org/micro/v3/store"
|
||||||
|
"github.com/unistack-org/micro/v3/tracer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Option func
|
||||||
|
type Option func(*Options)
|
||||||
|
|
||||||
|
// Options server struct
|
||||||
|
type Options struct {
|
||||||
|
// Context holds the external options and can be used for flow shutdown
|
||||||
|
Context context.Context
|
||||||
|
// Client holds the client.Client
|
||||||
|
Client client.Client
|
||||||
|
// Tracer holds the tracer
|
||||||
|
Tracer tracer.Tracer
|
||||||
|
// Logger holds the logger
|
||||||
|
Logger logger.Logger
|
||||||
|
// Meter holds the meter
|
||||||
|
Meter meter.Meter
|
||||||
|
// Store used for intermediate results
|
||||||
|
Store store.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOptions returns new options struct with default or passed values
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{
|
||||||
|
Context: context.Background(),
|
||||||
|
Logger: logger.DefaultLogger,
|
||||||
|
Meter: meter.DefaultMeter,
|
||||||
|
Tracer: tracer.DefaultTracer,
|
||||||
|
Client: client.DefaultClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger sets the logger option
|
||||||
|
func Logger(l logger.Logger) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Logger = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meter sets the meter option
|
||||||
|
func Meter(m meter.Meter) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Meter = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client to use for sync/async communication
|
||||||
|
func Client(c client.Client) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Client = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context specifies a context for the service.
|
||||||
|
// Can be used to signal shutdown of the flow
|
||||||
|
// Can be used for extra option values.
|
||||||
|
func Context(ctx context.Context) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Context = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tracer mechanism for distributed tracking
|
||||||
|
func Tracer(t tracer.Tracer) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Tracer = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store used for intermediate results
|
||||||
|
func Store(s store.Store) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Store = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorflowOption signature
|
||||||
|
type WorkflowOption func(*WorkflowOptions)
|
||||||
|
|
||||||
|
// WorkflowOptions holds workflow options
|
||||||
|
type WorkflowOptions struct {
|
||||||
|
ID string
|
||||||
|
Context context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkflowID set workflow id
|
||||||
|
func WorkflowID(id string) WorkflowOption {
|
||||||
|
return func(o *WorkflowOptions) {
|
||||||
|
o.ID = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecuteOptions struct {
|
||||||
|
// Client holds the client.Client
|
||||||
|
Client client.Client
|
||||||
|
// Tracer holds the tracer
|
||||||
|
Tracer tracer.Tracer
|
||||||
|
// Logger holds the logger
|
||||||
|
Logger logger.Logger
|
||||||
|
// Meter holds the meter
|
||||||
|
Meter meter.Meter
|
||||||
|
// Context can be used to abort execution or pass additional opts
|
||||||
|
Context context.Context
|
||||||
|
// Start step
|
||||||
|
Start string
|
||||||
|
// Reverse execution
|
||||||
|
Reverse bool
|
||||||
|
// Timeout for execution
|
||||||
|
Timeout time.Duration
|
||||||
|
// Async enables async execution
|
||||||
|
Async bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecuteOption func(*ExecuteOptions)
|
||||||
|
|
||||||
|
func ExecuteClient(c client.Client) ExecuteOption {
|
||||||
|
return func(o *ExecuteOptions) {
|
||||||
|
o.Client = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteTracer(t tracer.Tracer) ExecuteOption {
|
||||||
|
return func(o *ExecuteOptions) {
|
||||||
|
o.Tracer = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteLogger(l logger.Logger) ExecuteOption {
|
||||||
|
return func(o *ExecuteOptions) {
|
||||||
|
o.Logger = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteMeter(m meter.Meter) ExecuteOption {
|
||||||
|
return func(o *ExecuteOptions) {
|
||||||
|
o.Meter = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteContext(ctx context.Context) ExecuteOption {
|
||||||
|
return func(o *ExecuteOptions) {
|
||||||
|
o.Context = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteReverse(b bool) ExecuteOption {
|
||||||
|
return func(o *ExecuteOptions) {
|
||||||
|
o.Reverse = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteTimeout(td time.Duration) ExecuteOption {
|
||||||
|
return func(o *ExecuteOptions) {
|
||||||
|
o.Timeout = td
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteAsync(b bool) ExecuteOption {
|
||||||
|
return func(o *ExecuteOptions) {
|
||||||
|
o.Async = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExecuteOptions(opts ...ExecuteOption) ExecuteOptions {
|
||||||
|
options := ExecuteOptions{
|
||||||
|
Client: client.DefaultClient,
|
||||||
|
Logger: logger.DefaultLogger,
|
||||||
|
Tracer: tracer.DefaultTracer,
|
||||||
|
Meter: meter.DefaultMeter,
|
||||||
|
Context: context.Background(),
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepOptions struct {
|
||||||
|
ID string
|
||||||
|
Context context.Context
|
||||||
|
Requires []string
|
||||||
|
Fallback string
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepOption func(*StepOptions)
|
||||||
|
|
||||||
|
func NewStepOptions(opts ...StepOption) StepOptions {
|
||||||
|
options := StepOptions{
|
||||||
|
Context: context.Background(),
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
func StepID(id string) StepOption {
|
||||||
|
return func(o *StepOptions) {
|
||||||
|
o.ID = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func StepRequires(steps ...string) StepOption {
|
||||||
|
return func(o *StepOptions) {
|
||||||
|
o.Requires = steps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func StepFallback(step string) StepOption {
|
||||||
|
return func(o *StepOptions) {
|
||||||
|
o.Fallback = step
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +0,0 @@
|
|||||||
package micro
|
|
||||||
|
|
||||||
//go:generate ./.github/generate.sh
|
|
14
go.mod
14
go.mod
@@ -3,15 +3,11 @@ module github.com/unistack-org/micro/v3
|
|||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
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/google/uuid v1.2.0
|
github.com/golang-jwt/jwt v3.2.1+incompatible
|
||||||
github.com/heimdalr/dag v1.0.1 // indirect
|
github.com/google/uuid v1.3.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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34 // indirect
|
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
|
||||||
)
|
)
|
||||||
|
33
go.sum
33
go.sum
@@ -1,35 +1,22 @@
|
|||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
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/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/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
|
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
|
||||||
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
github.com/google/uuid v1.3.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.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||||
github.com/heimdalr/dag v1.0.1 h1:iR2K3DSUFDYx0GeV7iXBnZkedWS1xePSGrylQ197uxg=
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/heimdalr/dag v1.0.1/go.mod h1:t+ZkR+sjKL4xhlE1B9rwpvwfo+x+2R0363efS+Oghns=
|
|
||||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
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/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34 h1:vBfVmA5mZhsQa2jr1FOL9nfA37N/jnbBmi5XUfviVTI=
|
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34 h1:vBfVmA5mZhsQa2jr1FOL9nfA37N/jnbBmi5XUfviVTI=
|
||||||
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
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/sys v0.0.0-20210423082822-04245dca01da/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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/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=
|
||||||
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=
|
||||||
|
@@ -21,9 +21,11 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type defaultLogger struct {
|
type defaultLogger struct {
|
||||||
sync.RWMutex
|
|
||||||
opts Options
|
|
||||||
enc *json.Encoder
|
enc *json.Encoder
|
||||||
|
opts Options
|
||||||
|
sync.RWMutex
|
||||||
|
logFunc LogFunc
|
||||||
|
logfFunc LogfFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init(opts...) should only overwrite provided options
|
// Init(opts...) should only overwrite provided options
|
||||||
@@ -33,6 +35,12 @@ func (l *defaultLogger) Init(opts ...Option) error {
|
|||||||
o(&l.opts)
|
o(&l.opts)
|
||||||
}
|
}
|
||||||
l.enc = json.NewEncoder(l.opts.Out)
|
l.enc = json.NewEncoder(l.opts.Out)
|
||||||
|
// wrap the Log func
|
||||||
|
for i := len(l.opts.Wrappers); i > 0; i-- {
|
||||||
|
l.logFunc = l.opts.Wrappers[i-1].Log(l.logFunc)
|
||||||
|
l.logfFunc = l.opts.Wrappers[i-1].Logf(l.logfFunc)
|
||||||
|
}
|
||||||
|
|
||||||
l.Unlock()
|
l.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -49,10 +57,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{} {
|
||||||
@@ -113,27 +129,27 @@ func (l *defaultLogger) Fatal(ctx context.Context, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.Logf(ctx, InfoLevel, msg, args...)
|
l.logfFunc(ctx, InfoLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.Logf(ctx, ErrorLevel, msg, args...)
|
l.logfFunc(ctx, ErrorLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.Logf(ctx, DebugLevel, msg, args...)
|
l.logfFunc(ctx, DebugLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.Logf(ctx, WarnLevel, msg, args...)
|
l.logfFunc(ctx, WarnLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.Logf(ctx, TraceLevel, msg, args...)
|
l.logfFunc(ctx, TraceLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.Logf(ctx, FatalLevel, msg, args...)
|
l.logfFunc(ctx, FatalLevel, msg, args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +169,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 +196,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()
|
||||||
@@ -197,7 +215,11 @@ func (l *defaultLogger) Options() Options {
|
|||||||
|
|
||||||
// NewLogger builds a new logger based on options
|
// NewLogger builds a new logger based on options
|
||||||
func NewLogger(opts ...Option) Logger {
|
func NewLogger(opts ...Option) Logger {
|
||||||
l := &defaultLogger{opts: NewOptions(opts...)}
|
l := &defaultLogger{
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
}
|
||||||
|
l.logFunc = l.Log
|
||||||
|
l.logfFunc = l.Logf
|
||||||
l.enc = json.NewEncoder(l.opts.Out)
|
l.enc = json.NewEncoder(l.opts.Out)
|
||||||
return l
|
return l
|
||||||
}
|
}
|
@@ -8,6 +8,8 @@ var (
|
|||||||
DefaultLogger Logger = NewLogger()
|
DefaultLogger Logger = NewLogger()
|
||||||
// DefaultLevel used by logger
|
// DefaultLevel used by logger
|
||||||
DefaultLevel Level = InfoLevel
|
DefaultLevel Level = InfoLevel
|
||||||
|
// DefaultCallerSkipCount used by logger
|
||||||
|
DefaultCallerSkipCount = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger is a generic logging interface
|
// Logger is a generic logging interface
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLogger(t *testing.T) {
|
func TestLogger(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
l := NewLogger(WithLevel(TraceLevel))
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
if err := l.Init(); err != nil {
|
if err := l.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -15,4 +17,52 @@ func TestLogger(t *testing.T) {
|
|||||||
l.Warn(ctx, "warn_msg1")
|
l.Warn(ctx, "warn_msg1")
|
||||||
l.Fields(map[string]interface{}{"error": "test"}).Info(ctx, "error message")
|
l.Fields(map[string]interface{}{"error": "test"}).Info(ctx, "error message")
|
||||||
l.Warn(ctx, "first", " ", "second")
|
l.Warn(ctx, "first", " ", "second")
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"level":"trace","msg":"trace_msg1"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"warn","msg":"warn_msg1"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"error":"test","level":"info","msg":"error message"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"level":"warn","msg":"first second"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoggerWrapper(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
|
if err := l.Init(WrapLogger(NewOmitWrapper())); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
type secret struct {
|
||||||
|
Name string
|
||||||
|
Passw string `logger:"omit"`
|
||||||
|
}
|
||||||
|
s := &secret{Name: "name", Passw: "secret"}
|
||||||
|
l.Errorf(ctx, "test %#+v", s)
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`logger.secret{Name:\"name\", Passw:\"\"}"`)) {
|
||||||
|
t.Fatalf("omit not works, struct: %v, output: %s", s, buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOmitLoggerWrapper(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewOmitLogger(NewLogger(WithLevel(TraceLevel), WithOutput(buf)))
|
||||||
|
if err := l.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
type secret struct {
|
||||||
|
Name string
|
||||||
|
Passw string `logger:"omit"`
|
||||||
|
}
|
||||||
|
s := &secret{Name: "name", Passw: "secret"}
|
||||||
|
l.Errorf(ctx, "test %#+v", s)
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`logger.secret{Name:\"name\", Passw:\"\"}"`)) {
|
||||||
|
t.Fatalf("omit not works, struct: %v, output: %s", s, buf.Bytes())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,17 +11,20 @@ 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
|
||||||
|
// Wrappers logger wrapper that called before actual Log/Logf function
|
||||||
|
Wrappers []Wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions creates new options struct
|
// NewOptions creates new options struct
|
||||||
@@ -30,7 +33,7 @@ func NewOptions(opts ...Option) Options {
|
|||||||
Level: DefaultLevel,
|
Level: DefaultLevel,
|
||||||
Fields: make(map[string]interface{}),
|
Fields: make(map[string]interface{}),
|
||||||
Out: os.Stderr,
|
Out: os.Stderr,
|
||||||
CallerSkipCount: 0,
|
CallerSkipCount: DefaultCallerSkipCount,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
@@ -75,8 +78,15 @@ func WithContext(ctx context.Context) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithName sets the name
|
// WithName sets the name
|
||||||
func withName(n string) Option {
|
func WithName(n string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Name = n
|
o.Name = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WrapLogger adds a logger Wrapper to a list of options passed into the logger
|
||||||
|
func WrapLogger(w Wrapper) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Wrappers = append(o.Wrappers, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
154
logger/wrapper.go
Normal file
154
logger/wrapper.go
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
rutil "github.com/unistack-org/micro/v3/util/reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogFunc function used for Log method
|
||||||
|
type LogFunc func(ctx context.Context, level Level, args ...interface{})
|
||||||
|
|
||||||
|
// LogfFunc function used for Logf method
|
||||||
|
type LogfFunc func(ctx context.Context, level Level, msg string, args ...interface{})
|
||||||
|
|
||||||
|
type Wrapper interface {
|
||||||
|
// Log logs message with needed level
|
||||||
|
Log(LogFunc) LogFunc
|
||||||
|
// Logf logs message with needed level
|
||||||
|
Logf(LogfFunc) LogfFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ Logger = &OmitLogger{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type OmitLogger struct {
|
||||||
|
l Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOmitLogger(l Logger) Logger {
|
||||||
|
return &OmitLogger{l: l}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Init(opts ...Option) error {
|
||||||
|
return w.l.Init(append(opts, WrapLogger(NewOmitWrapper()))...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) V(level Level) bool {
|
||||||
|
return w.l.V(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Options() Options {
|
||||||
|
return w.l.Options()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Fields(fields map[string]interface{}) Logger {
|
||||||
|
return w.l.Fields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Info(ctx context.Context, args ...interface{}) {
|
||||||
|
w.l.Info(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Trace(ctx context.Context, args ...interface{}) {
|
||||||
|
w.l.Trace(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Debug(ctx context.Context, args ...interface{}) {
|
||||||
|
w.l.Debug(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Warn(ctx context.Context, args ...interface{}) {
|
||||||
|
w.l.Warn(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Error(ctx context.Context, args ...interface{}) {
|
||||||
|
w.l.Error(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Fatal(ctx context.Context, args ...interface{}) {
|
||||||
|
w.l.Fatal(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
w.l.Infof(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
w.l.Tracef(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
w.l.Debugf(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
w.l.Warnf(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
w.l.Errorf(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
w.l.Fatalf(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Log(ctx context.Context, level Level, args ...interface{}) {
|
||||||
|
w.l.Log(ctx, level, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) Logf(ctx context.Context, level Level, msg string, args ...interface{}) {
|
||||||
|
w.l.Logf(ctx, level, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitLogger) String() string {
|
||||||
|
return w.l.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type OmitWrapper struct{}
|
||||||
|
|
||||||
|
func NewOmitWrapper() Wrapper {
|
||||||
|
return &OmitWrapper{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArgs(args []interface{}) []interface{} {
|
||||||
|
nargs := make([]interface{}, 0, len(args))
|
||||||
|
var err error
|
||||||
|
for _, arg := range args {
|
||||||
|
val := reflect.ValueOf(arg)
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
narg := arg
|
||||||
|
if val.Kind() == reflect.Struct {
|
||||||
|
if narg, err = rutil.Zero(arg); err == nil {
|
||||||
|
rutil.CopyDefaults(narg, arg)
|
||||||
|
if flds, ferr := rutil.StructFields(narg); ferr == nil {
|
||||||
|
for _, fld := range flds {
|
||||||
|
if tv, ok := fld.Field.Tag.Lookup("logger"); ok && tv == "omit" {
|
||||||
|
fld.Value.Set(reflect.Zero(fld.Value.Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nargs = append(nargs, narg)
|
||||||
|
}
|
||||||
|
return nargs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitWrapper) Log(fn LogFunc) LogFunc {
|
||||||
|
return func(ctx context.Context, level Level, args ...interface{}) {
|
||||||
|
fn(ctx, level, getArgs(args)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *OmitWrapper) Logf(fn LogfFunc) LogfFunc {
|
||||||
|
return func(ctx context.Context, level Level, msg string, args ...interface{}) {
|
||||||
|
fn(ctx, level, msg, getArgs(args)...)
|
||||||
|
}
|
||||||
|
}
|
410
logger/wrapper/wrapper.go
Normal file
410
logger/wrapper/wrapper.go
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
// 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
|
||||||
|
ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, error) []string
|
||||||
|
ClientPublishObserver func(context.Context, client.Message, []client.PublishOption, error) []string
|
||||||
|
ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, error) []string
|
||||||
|
ServerHandlerObserver func(context.Context, server.Request, interface{}, error) []string
|
||||||
|
ServerSubscriberObserver func(context.Context, server.Message, error) []string
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options struct for wrapper
|
||||||
|
type Options struct {
|
||||||
|
// Logger that used for log
|
||||||
|
Logger logger.Logger
|
||||||
|
// ServerHandlerObservers funcs
|
||||||
|
ServerHandlerObservers []ServerHandlerObserver
|
||||||
|
// ServerSubscriberObservers funcs
|
||||||
|
ServerSubscriberObservers []ServerSubscriberObserver
|
||||||
|
// ClientCallObservers funcs
|
||||||
|
ClientCallObservers []ClientCallObserver
|
||||||
|
// ClientStreamObservers funcs
|
||||||
|
ClientStreamObservers []ClientStreamObserver
|
||||||
|
// ClientPublishObservers funcs
|
||||||
|
ClientPublishObservers []ClientPublishObserver
|
||||||
|
// ClientCallFuncObservers funcs
|
||||||
|
ClientCallFuncObservers []ClientCallFuncObserver
|
||||||
|
// SkipEndpoints
|
||||||
|
SkipEndpoints []string
|
||||||
|
// Level for logger
|
||||||
|
Level logger.Level
|
||||||
|
// Enabled flag
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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{}, 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{}, 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{}, 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{}, 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{}, 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{}, 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
|
||||||
|
}
|
||||||
|
}
|
@@ -5,9 +5,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mdIncomingKey struct{}
|
type (
|
||||||
type mdOutgoingKey struct{}
|
mdIncomingKey struct{}
|
||||||
type mdKey struct{}
|
mdOutgoingKey struct{}
|
||||||
|
mdKey struct{}
|
||||||
|
)
|
||||||
|
|
||||||
// FromIncomingContext returns metadata from incoming ctx
|
// FromIncomingContext returns metadata from incoming ctx
|
||||||
// returned metadata shoud not be modified or race condition happens
|
// returned metadata shoud not be modified or race condition happens
|
||||||
@@ -93,7 +95,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 +107,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)
|
||||||
|
}
|
||||||
|
@@ -7,8 +7,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// HeaderPrefix for all headers passed
|
// HeaderTopic is the header name that contains topic name
|
||||||
HeaderPrefix = "Micro-"
|
HeaderTopic = "Micro-Topic"
|
||||||
|
// HeaderContentType specifies content type of message
|
||||||
|
HeaderContentType = "Content-Type"
|
||||||
|
// HeaderEndpoint specifies endpoint in service
|
||||||
|
HeaderEndpoint = "Micro-Endpoint"
|
||||||
|
// HeaderService specifies service
|
||||||
|
HeaderService = "Micro-Service"
|
||||||
|
// HeaderTimeout specifies timeout of operation
|
||||||
|
HeaderTimeout = "Micro-Timeout"
|
||||||
|
// HeaderAuthorization specifies Authorization header
|
||||||
|
HeaderAuthorization = "Authorization"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Metadata is our way of representing request headers internally.
|
// Metadata is our way of representing request headers internally.
|
||||||
@@ -20,17 +30,15 @@ type rawMetadata struct {
|
|||||||
md Metadata
|
md Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// defaultMetadataSize used when need to init new Metadata
|
||||||
// defaultMetadataSize used when need to init new Metadata
|
var defaultMetadataSize = 2
|
||||||
defaultMetadataSize = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// Iterator used to iterate over metadata with order
|
// 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
|
// Next advance iterator to next element
|
||||||
@@ -111,3 +119,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
|
||||||
|
}
|
||||||
|
@@ -5,6 +5,28 @@ import (
|
|||||||
"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")
|
||||||
@@ -54,7 +76,7 @@ func TestIterator(t *testing.T) {
|
|||||||
var k, v string
|
var k, v string
|
||||||
|
|
||||||
for iter.Next(&k, &v) {
|
for iter.Next(&k, &v) {
|
||||||
//fmt.Printf("k: %s, v: %s\n", k, v)
|
// fmt.Printf("k: %s, v: %s\n", k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +102,6 @@ func TestMedataCanonicalKey(t *testing.T) {
|
|||||||
} else if v != "12345" {
|
} else if v != "12345" {
|
||||||
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMetadataSet(t *testing.T) {
|
func TestMetadataSet(t *testing.T) {
|
||||||
@@ -108,7 +129,6 @@ func TestMetadataDelete(t *testing.T) {
|
|||||||
if ok {
|
if ok {
|
||||||
t.Fatal("key Baz not deleted")
|
t.Fatal("key Baz not deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNilContext(t *testing.T) {
|
func TestNilContext(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
|
67
meter/handler/handler.go
Normal file
67
meter/handler/handler.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
|
"github.com/unistack-org/micro/v3/errors"
|
||||||
|
"github.com/unistack-org/micro/v3/meter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// guard to fail early
|
||||||
|
var _ MeterServer = &Handler{}
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*Options)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
Meter meter.Meter
|
||||||
|
Name string
|
||||||
|
MeterOptions []meter.Option
|
||||||
|
}
|
||||||
|
|
||||||
|
func Meter(m meter.Meter) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Meter = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Name(name string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MeterOptions(opts ...meter.Option) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.MeterOptions = append(o.MeterOptions, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{Meter: meter.DefaultMeter}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(opts ...Option) *Handler {
|
||||||
|
options := NewOptions(opts...)
|
||||||
|
return &Handler{opts: options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
if err := h.opts.Meter.Write(buf, h.opts.MeterOptions...); err != nil {
|
||||||
|
return errors.InternalServerError(h.opts.Name, "%v", 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...)...))
|
||||||
|
}
|
119
meter/meter.go
119
meter/meter.go
@@ -4,6 +4,8 @@ package meter
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,13 +30,13 @@ var (
|
|||||||
type Meter interface {
|
type Meter interface {
|
||||||
Name() string
|
Name() string
|
||||||
Init(opts ...Option) error
|
Init(opts ...Option) error
|
||||||
Counter(name string, opts ...Option) Counter
|
Counter(name string, labels ...string) Counter
|
||||||
FloatCounter(name string, opts ...Option) FloatCounter
|
FloatCounter(name string, labels ...string) FloatCounter
|
||||||
Gauge(name string, fn func() float64, opts ...Option) Gauge
|
Gauge(name string, fn func() float64, labels ...string) Gauge
|
||||||
Set(opts ...Option) Meter
|
Set(opts ...Option) Meter
|
||||||
Histogram(name string, opts ...Option) Histogram
|
Histogram(name string, labels ...string) Histogram
|
||||||
Summary(name string, opts ...Option) Summary
|
Summary(name string, labels ...string) Summary
|
||||||
SummaryExt(name string, window time.Duration, quantiles []float64, opts ...Option) Summary
|
SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) Summary
|
||||||
Write(w io.Writer, opts ...Option) error
|
Write(w io.Writer, opts ...Option) error
|
||||||
Options() Options
|
Options() Options
|
||||||
String() string
|
String() string
|
||||||
@@ -76,65 +78,62 @@ type Summary interface {
|
|||||||
UpdateDuration(time.Time)
|
UpdateDuration(time.Time)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Labels holds the metrics labels with k, v
|
// sort labels alphabeticaly by label name
|
||||||
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[j*2] = k[j*2], k[i*2]
|
||||||
|
k[i*2+1], k[j*2+1] = k[j*2+1], k[i*2+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
type labels Labels
|
// BuildLables used to sort labels and delete duplicates.
|
||||||
|
// Last value wins in case of duplicate label keys.
|
||||||
func (ls labels) sort() {
|
func BuildLabels(labels ...string) []string {
|
||||||
sort.Sort(ls)
|
if len(labels)%2 == 1 {
|
||||||
}
|
labels = labels[:len(labels)-1]
|
||||||
|
|
||||||
func (ls labels) Len() int {
|
|
||||||
return len(ls.keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append adds labels to label set
|
|
||||||
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(byKey(labels))
|
||||||
|
return labels
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls Labels) Len() int {
|
// BuildName used to combine metric with labels.
|
||||||
return len(ls.keys)
|
// If labels count is odd, drop last element
|
||||||
}
|
func BuildName(name string, labels ...string) string {
|
||||||
|
if len(labels)%2 == 1 {
|
||||||
// LabelIter holds the
|
labels = labels[:len(labels)-1]
|
||||||
type LabelIter struct {
|
|
||||||
labels Labels
|
|
||||||
cnt int
|
|
||||||
cur int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iter returns labels iterator
|
|
||||||
func (ls Labels) Iter() *LabelIter {
|
|
||||||
labels(ls).sort()
|
|
||||||
return &LabelIter{labels: ls, cnt: len(ls.keys)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next advance itarator to new pos
|
|
||||||
func (iter *LabelIter) Next(k, v *string) bool {
|
|
||||||
if iter.cur+1 > iter.cnt {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*k = iter.labels.keys[iter.cur]
|
if len(labels) > 2 {
|
||||||
*v = iter.labels.vals[iter.cur]
|
sort.Sort(byKey(labels))
|
||||||
iter.cur++
|
|
||||||
return true
|
idx := 0
|
||||||
|
for {
|
||||||
|
if labels[idx] == labels[idx+2] {
|
||||||
|
copy(labels[idx:], labels[idx+2:])
|
||||||
|
labels = labels[:len(labels)-2]
|
||||||
|
} else {
|
||||||
|
idx += 2
|
||||||
|
}
|
||||||
|
if idx+2 >= len(labels) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
_, _ = b.WriteString(name)
|
||||||
|
_, _ = b.WriteRune('{')
|
||||||
|
for idx := 0; idx < len(labels); idx += 2 {
|
||||||
|
if idx > 0 {
|
||||||
|
_, _ = b.WriteRune(',')
|
||||||
|
}
|
||||||
|
_, _ = b.WriteString(labels[idx])
|
||||||
|
_, _ = b.WriteString(`=`)
|
||||||
|
_, _ = b.WriteString(strconv.Quote(labels[idx+1]))
|
||||||
|
}
|
||||||
|
_, _ = b.WriteRune('}')
|
||||||
|
|
||||||
|
return b.String()
|
||||||
}
|
}
|
||||||
|
@@ -5,44 +5,66 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNoopMeter(t *testing.T) {
|
func TestNoopMeter(t *testing.T) {
|
||||||
meter := NewMeter(Path("/noop"))
|
m := NewMeter(Path("/noop"))
|
||||||
if "/noop" != meter.Options().Path {
|
if "/noop" != m.Options().Path {
|
||||||
t.Fatalf("invalid options parsing: %v", meter.Options())
|
t.Fatalf("invalid options parsing: %v", m.Options())
|
||||||
}
|
}
|
||||||
|
|
||||||
cnt := meter.Counter("counter", Label("server", "noop"))
|
cnt := m.Counter("counter", "server", "noop")
|
||||||
cnt.Inc()
|
cnt.Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLabelsAppend(t *testing.T) {
|
func testEq(a, b []string) bool {
|
||||||
var ls Labels
|
if len(a) != len(b) {
|
||||||
ls.keys = []string{"type", "server"}
|
return false
|
||||||
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] != "type" || ls.vals[0] != "noop" {
|
|
||||||
t.Fatalf("append error: %v", ls)
|
|
||||||
}
|
}
|
||||||
}
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
func TestIterator(t *testing.T) {
|
return false
|
||||||
var ls Labels
|
}
|
||||||
ls.keys = []string{"type", "server", "register"}
|
}
|
||||||
ls.vals = []string{"noop", "http", "gossip"}
|
return true
|
||||||
|
}
|
||||||
iter := ls.Iter()
|
|
||||||
var k, v string
|
func TestBuildLabels(t *testing.T) {
|
||||||
cnt := 0
|
type testData struct {
|
||||||
for iter.Next(&k, &v) {
|
src []string
|
||||||
if cnt == 1 && (k != "server" || v != "http") {
|
dst []string
|
||||||
t.Fatalf("iter error: %s != %s || %s != %s", k, "server", v, "http")
|
}
|
||||||
|
|
||||||
|
data := []testData{
|
||||||
|
testData{
|
||||||
|
src: []string{"zerolabel", "value3", "firstlabel", "value2"},
|
||||||
|
dst: []string{"firstlabel", "value2", "zerolabel", "value3"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range data {
|
||||||
|
if !testEq(d.dst, BuildLabels(d.src...)) {
|
||||||
|
t.Fatalf("slices not properly sorted: %v %v", d.dst, d.src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildName(t *testing.T) {
|
||||||
|
data := map[string][]string{
|
||||||
|
`my_metric{firstlabel="value2",zerolabel="value3"}`: []string{
|
||||||
|
"my_metric",
|
||||||
|
"zerolabel", "value3", "firstlabel", "value2",
|
||||||
|
},
|
||||||
|
`my_metric{broker="broker2",register="mdns",server="tcp"}`: []string{
|
||||||
|
"my_metric",
|
||||||
|
"broker", "broker1", "broker", "broker2", "server", "http", "server", "tcp", "register", "mdns",
|
||||||
|
},
|
||||||
|
`my_metric{aaa="aaa"}`: []string{
|
||||||
|
"my_metric",
|
||||||
|
"aaa", "aaa",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for e, d := range data {
|
||||||
|
if x := BuildName(d[0], d[1:]...); x != e {
|
||||||
|
t.Fatalf("expect: %s, result: %s", e, x)
|
||||||
}
|
}
|
||||||
cnt++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,57 +28,33 @@ func (r *noopMeter) Init(opts ...Option) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Counter implements the Meter interface
|
// Counter implements the Meter interface
|
||||||
func (r *noopMeter) Counter(name string, opts ...Option) Counter {
|
func (r *noopMeter) Counter(name string, labels ...string) Counter {
|
||||||
options := Options{}
|
return &noopCounter{labels: labels}
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
return &noopCounter{labels: options.Labels}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, labels ...string) FloatCounter {
|
||||||
options := Options{}
|
return &noopFloatCounter{labels: labels}
|
||||||
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, labels ...string) Gauge {
|
||||||
options := Options{}
|
return &noopGauge{labels: labels}
|
||||||
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, labels ...string) Summary {
|
||||||
options := Options{}
|
return &noopSummary{labels: labels}
|
||||||
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, labels ...string) Summary {
|
||||||
options := Options{}
|
return &noopSummary{labels: labels}
|
||||||
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, labels ...string) Histogram {
|
||||||
options := Options{}
|
return &noopHistogram{labels: labels}
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
return &noopHistogram{labels: options.Labels}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set implements the Meter interface
|
// Set implements the Meter interface
|
||||||
@@ -107,15 +83,13 @@ 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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopCounter) Dec() {
|
func (r *noopCounter) Dec() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopCounter) Get() uint64 {
|
func (r *noopCounter) Get() uint64 {
|
||||||
@@ -123,19 +97,16 @@ func (r *noopCounter) Get() uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopCounter) Inc() {
|
func (r *noopCounter) Inc() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopCounter) Set(uint64) {
|
func (r *noopCounter) Set(uint64) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopFloatCounter struct {
|
type noopFloatCounter struct {
|
||||||
labels Labels
|
labels []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopFloatCounter) Add(float64) {
|
func (r *noopFloatCounter) Add(float64) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopFloatCounter) Get() float64 {
|
func (r *noopFloatCounter) Get() float64 {
|
||||||
@@ -143,15 +114,13 @@ func (r *noopFloatCounter) Get() float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopFloatCounter) Set(float64) {
|
func (r *noopFloatCounter) Set(float64) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopFloatCounter) Sub(float64) {
|
func (r *noopFloatCounter) Sub(float64) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopGauge struct {
|
type noopGauge struct {
|
||||||
labels Labels
|
labels []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopGauge) Get() float64 {
|
func (r *noopGauge) Get() float64 {
|
||||||
@@ -159,31 +128,26 @@ func (r *noopGauge) Get() float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type noopSummary struct {
|
type noopSummary struct {
|
||||||
labels Labels
|
labels []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopSummary) Update(float64) {
|
func (r *noopSummary) Update(float64) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopSummary) UpdateDuration(time.Time) {
|
func (r *noopSummary) UpdateDuration(time.Time) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopHistogram struct {
|
type noopHistogram struct {
|
||||||
labels Labels
|
labels []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopHistogram) Reset() {
|
func (r *noopHistogram) Reset() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopHistogram) Update(float64) {
|
func (r *noopHistogram) Update(float64) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *noopHistogram) UpdateDuration(time.Time) {
|
func (r *noopHistogram) UpdateDuration(time.Time) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//func (r *noopHistogram) VisitNonZeroBuckets(f func(vmrange string, count uint64)) {}
|
// func (r *noopHistogram) VisitNonZeroBuckets(f func(vmrange string, count uint64)) {}
|
||||||
|
@@ -11,17 +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
|
MetricPrefix string
|
||||||
Context context.Context
|
// LabelPrefix holds the prefix for all labels
|
||||||
MetricPrefix string
|
LabelPrefix string
|
||||||
LabelPrefix string
|
// Labels holds the default labels
|
||||||
|
Labels []string
|
||||||
|
// WriteProcessMetrics flag to write process metrics
|
||||||
WriteProcessMetrics bool
|
WriteProcessMetrics bool
|
||||||
WriteFDMetrics bool
|
// WriteFDMetrics flag to write fd metrics
|
||||||
|
WriteFDMetrics bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions prepares a set of options:
|
// NewOptions prepares a set of options:
|
||||||
@@ -79,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 = append(o.Labels, ls...)
|
||||||
o.Labels.vals = append(o.Labels.vals, val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,36 +11,49 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ClientRequestDurationSeconds = "client_request_duration_seconds"
|
ClientRequestDurationSeconds = "client_request_duration_seconds"
|
||||||
ClientRequestLatencyMicroseconds = "client_request_latency_microseconds"
|
ClientRequestLatencyMicroseconds = "client_request_latency_microseconds"
|
||||||
ClientRequestTotal = "client_request_total"
|
ClientRequestTotal = "client_request_total"
|
||||||
ServerRequestDurationSeconds = "server_request_duration_seconds"
|
ClientRequestInflight = "client_request_inflight"
|
||||||
ServerRequestLatencyMicroseconds = "server_request_latency_microseconds"
|
|
||||||
ServerRequestTotal = "server_request_total"
|
ServerRequestDurationSeconds = "server_request_duration_seconds"
|
||||||
PublishMessageDurationSeconds = "publish_message_duration_seconds"
|
ServerRequestLatencyMicroseconds = "server_request_latency_microseconds"
|
||||||
PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds"
|
ServerRequestTotal = "server_request_total"
|
||||||
PublishMessageTotal = "publish_message_total"
|
ServerRequestInflight = "server_request_inflight"
|
||||||
|
|
||||||
|
PublishMessageDurationSeconds = "publish_message_duration_seconds"
|
||||||
|
PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds"
|
||||||
|
PublishMessageTotal = "publish_message_total"
|
||||||
|
PublishMessageInflight = "publish_message_inflight"
|
||||||
|
|
||||||
SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds"
|
SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds"
|
||||||
SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds"
|
SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds"
|
||||||
SubscribeMessageTotal = "subscribe_message_total"
|
SubscribeMessageTotal = "subscribe_message_total"
|
||||||
|
SubscribeMessageInflight = "subscribe_message_inflight"
|
||||||
|
|
||||||
labelSuccess = "success"
|
labelSuccess = "success"
|
||||||
labelFailure = "failure"
|
labelFailure = "failure"
|
||||||
labelStatus = "status"
|
labelStatus = "status"
|
||||||
labelEndpoint = "endpoint"
|
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 {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{}
|
options := Options{
|
||||||
|
Meter: meter.DefaultMeter,
|
||||||
|
lopts: make([]meter.Option, 0, 5),
|
||||||
|
SkipEndpoints: DefaultSkipEndpoints,
|
||||||
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
@@ -49,19 +62,19 @@ func NewOptions(opts ...Option) 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,10 +84,16 @@ 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 {
|
||||||
opts 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 {
|
||||||
@@ -99,69 +118,90 @@ func NewCallWrapper(opts ...Option) client.CallWrapper {
|
|||||||
|
|
||||||
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())
|
||||||
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return w.callFunc(ctx, addr, req, rsp, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Inc()
|
||||||
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)
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Dec()
|
||||||
|
|
||||||
lopts := make([]meter.Option, 0, 2)
|
w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
lopts = append(lopts, meter.Label(labelEndpoint, endpoint))
|
w.opts.Meter.Histogram(ClientRequestDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
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 {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelSuccess))
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
} else {
|
} else {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelFailure))
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
}
|
}
|
||||||
w.opts.Meter.Counter(ClientRequestTotal, lopts...).Inc()
|
w.opts.Meter.Counter(ClientRequestTotal, labels...).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())
|
||||||
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return w.Client.Call(ctx, req, rsp, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Inc()
|
||||||
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)
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Dec()
|
||||||
|
|
||||||
lopts := make([]meter.Option, 0, 2)
|
w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
lopts = append(lopts, meter.Label(labelEndpoint, endpoint))
|
w.opts.Meter.Histogram(ClientRequestDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
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 {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelSuccess))
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
} else {
|
} else {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelFailure))
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
}
|
}
|
||||||
w.opts.Meter.Counter(ClientRequestTotal, lopts...).Inc()
|
w.opts.Meter.Counter(ClientRequestTotal, labels...).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())
|
||||||
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return w.Client.Stream(ctx, req, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Inc()
|
||||||
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)
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Dec()
|
||||||
|
|
||||||
lopts := make([]meter.Option, 0, 2)
|
w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
lopts = append(lopts, meter.Label(labelEndpoint, endpoint))
|
w.opts.Meter.Histogram(ClientRequestDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
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 {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelSuccess))
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
} else {
|
} else {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelFailure))
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
}
|
}
|
||||||
w.opts.Meter.Counter(ClientRequestTotal, lopts...).Inc()
|
w.opts.Meter.Counter(ClientRequestTotal, labels...).Inc()
|
||||||
|
|
||||||
return stream, err
|
return stream, err
|
||||||
}
|
}
|
||||||
@@ -169,22 +209,24 @@ func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client
|
|||||||
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()
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(PublishMessageInflight, labels...).Inc()
|
||||||
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)
|
||||||
|
w.opts.Meter.Counter(PublishMessageInflight, labels...).Dec()
|
||||||
|
|
||||||
lopts := make([]meter.Option, 0, 2)
|
w.opts.Meter.Summary(PublishMessageLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
lopts = append(lopts, meter.Label(labelEndpoint, endpoint))
|
w.opts.Meter.Histogram(PublishMessageDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
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 {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelSuccess))
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
} else {
|
} else {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelFailure))
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
}
|
}
|
||||||
w.opts.Meter.Counter(PublishMessageTotal, lopts...).Inc()
|
w.opts.Meter.Counter(PublishMessageTotal, labels...).Inc()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -199,23 +241,30 @@ func NewHandlerWrapper(opts ...Option) server.HandlerWrapper {
|
|||||||
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()
|
||||||
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return fn(ctx, req, rsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(ServerRequestInflight, labels...).Inc()
|
||||||
ts := time.Now()
|
ts := time.Now()
|
||||||
err := fn(ctx, req, rsp)
|
err := fn(ctx, req, rsp)
|
||||||
te := time.Since(ts)
|
te := time.Since(ts)
|
||||||
|
w.opts.Meter.Counter(ServerRequestInflight, labels...).Dec()
|
||||||
|
|
||||||
lopts := make([]meter.Option, 0, 2)
|
w.opts.Meter.Summary(ServerRequestLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
lopts = append(lopts, meter.Label(labelEndpoint, endpoint))
|
w.opts.Meter.Histogram(ServerRequestDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
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 {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelSuccess))
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
} else {
|
} else {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelFailure))
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
}
|
}
|
||||||
w.opts.Meter.Counter(ServerRequestTotal, lopts...).Inc()
|
w.opts.Meter.Counter(ServerRequestTotal, labels...).Inc()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -232,22 +281,24 @@ 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()
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(SubscribeMessageInflight, labels...).Inc()
|
||||||
ts := time.Now()
|
ts := time.Now()
|
||||||
err := fn(ctx, msg)
|
err := fn(ctx, msg)
|
||||||
te := time.Since(ts)
|
te := time.Since(ts)
|
||||||
|
w.opts.Meter.Counter(SubscribeMessageInflight, labels...).Dec()
|
||||||
|
|
||||||
lopts := make([]meter.Option, 0, 2)
|
w.opts.Meter.Summary(SubscribeMessageLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
lopts = append(lopts, meter.Label(labelEndpoint, endpoint))
|
w.opts.Meter.Histogram(SubscribeMessageDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
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 {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelSuccess))
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
} else {
|
} else {
|
||||||
lopts = append(lopts, meter.Label(labelStatus, labelFailure))
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
}
|
}
|
||||||
w.opts.Meter.Counter(SubscribeMessageTotal, lopts...).Inc()
|
w.opts.Meter.Counter(SubscribeMessageTotal, labels...).Inc()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -14,19 +14,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type memorySocket struct {
|
type memorySocket struct {
|
||||||
recv chan *Message
|
|
||||||
send chan *Message
|
|
||||||
// sock exit
|
|
||||||
exit chan bool
|
|
||||||
// listener exit
|
|
||||||
lexit chan bool
|
|
||||||
|
|
||||||
local string
|
|
||||||
remote string
|
|
||||||
|
|
||||||
// for send/recv transport.Timeout
|
|
||||||
timeout time.Duration
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
recv chan *Message
|
||||||
|
exit chan bool
|
||||||
|
lexit chan bool
|
||||||
|
send chan *Message
|
||||||
|
local string
|
||||||
|
remote string
|
||||||
|
timeout time.Duration
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,19 +31,19 @@ type memoryClient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type memoryListener struct {
|
type memoryListener struct {
|
||||||
addr string
|
lopts ListenOptions
|
||||||
|
ctx context.Context
|
||||||
exit chan bool
|
exit chan bool
|
||||||
conn chan *memorySocket
|
conn chan *memorySocket
|
||||||
lopts ListenOptions
|
addr string
|
||||||
topts Options
|
topts Options
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
ctx context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type memoryTransport struct {
|
type memoryTransport struct {
|
||||||
opts Options
|
|
||||||
sync.RWMutex
|
|
||||||
listeners map[string]*memoryListener
|
listeners map[string]*memoryListener
|
||||||
|
opts Options
|
||||||
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *memorySocket) Recv(m *Message) error {
|
func (ms *memorySocket) Recv(m *Message) error {
|
||||||
|
@@ -27,9 +27,9 @@ func TestMemoryTransport(t *testing.T) {
|
|||||||
if len(os.Getenv("INTEGRATION_TESTS")) == 0 {
|
if len(os.Getenv("INTEGRATION_TESTS")) == 0 {
|
||||||
t.Logf("Server Received %s", string(m.Body))
|
t.Logf("Server Received %s", string(m.Body))
|
||||||
}
|
}
|
||||||
if err := sock.Send(&Message{
|
if cerr := sock.Send(&Message{
|
||||||
Body: []byte(`pong`),
|
Body: []byte(`pong`),
|
||||||
}); err != nil {
|
}); cerr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,6 @@ func TestMemoryTransport(t *testing.T) {
|
|||||||
t.Logf("Client Received %s", string(m.Body))
|
t.Logf("Client Received %s", string(m.Body))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListener(t *testing.T) {
|
func TestListener(t *testing.T) {
|
||||||
|
@@ -13,26 +13,24 @@ import (
|
|||||||
|
|
||||||
// Options struct holds the transport options
|
// 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
|
|
||||||
// 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 sets the 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
|
||||||
@@ -53,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
|
||||||
@@ -85,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
|
||||||
|
@@ -12,27 +12,37 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type tunBroker struct {
|
type tunBroker struct {
|
||||||
opts broker.Options
|
|
||||||
tunnel tunnel.Tunnel
|
tunnel tunnel.Tunnel
|
||||||
|
opts broker.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
type tunSubscriber struct {
|
type tunSubscriber struct {
|
||||||
topic string
|
|
||||||
handler broker.Handler
|
|
||||||
opts broker.SubscribeOptions
|
|
||||||
|
|
||||||
closed chan bool
|
|
||||||
listener tunnel.Listener
|
listener tunnel.Listener
|
||||||
|
handler broker.Handler
|
||||||
|
closed chan bool
|
||||||
|
topic string
|
||||||
|
opts broker.SubscribeOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
type tunBatchSubscriber struct {
|
||||||
|
listener tunnel.Listener
|
||||||
|
handler broker.BatchHandler
|
||||||
|
closed chan bool
|
||||||
|
topic string
|
||||||
|
opts broker.SubscribeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
type tunEvent struct {
|
type tunEvent struct {
|
||||||
topic string
|
|
||||||
message *broker.Message
|
message *broker.Message
|
||||||
|
topic string
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to access tunnel from options context
|
// used to access tunnel from options context
|
||||||
type tunnelKey struct{}
|
type (
|
||||||
type tunnelAddr struct{}
|
tunnelKey struct{}
|
||||||
|
tunnelAddr struct{}
|
||||||
|
)
|
||||||
|
|
||||||
func (t *tunBroker) Init(opts ...broker.Option) error {
|
func (t *tunBroker) Init(opts ...broker.Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
@@ -61,6 +71,36 @@ func (t *tunBroker) Disconnect(ctx context.Context) error {
|
|||||||
return t.tunnel.Close(ctx)
|
return t.tunnel.Close(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tunBroker) BatchPublish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error {
|
||||||
|
// TODO: this is probably inefficient, we might want to just maintain an open connection
|
||||||
|
// it may be easier to add broadcast to the tunnel
|
||||||
|
topicMap := make(map[string]tunnel.Session)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for _, msg := range msgs {
|
||||||
|
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||||
|
c, ok := topicMap[topic]
|
||||||
|
if !ok {
|
||||||
|
c, err := t.tunnel.Dial(ctx, topic, tunnel.DialMode(tunnel.Multicast))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
topicMap[topic] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.Send(&transport.Message{
|
||||||
|
Header: msg.Header,
|
||||||
|
Body: msg.Body,
|
||||||
|
}); err != nil {
|
||||||
|
// msg.SetError(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tunBroker) Publish(ctx context.Context, topic string, m *broker.Message, opts ...broker.PublishOption) error {
|
func (t *tunBroker) Publish(ctx context.Context, topic string, m *broker.Message, opts ...broker.PublishOption) error {
|
||||||
// TODO: this is probably inefficient, we might want to just maintain an open connection
|
// TODO: this is probably inefficient, we might want to just maintain an open connection
|
||||||
// it may be easier to add broadcast to the tunnel
|
// it may be easier to add broadcast to the tunnel
|
||||||
@@ -76,6 +116,26 @@ func (t *tunBroker) Publish(ctx context.Context, topic string, m *broker.Message
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tunBroker) BatchSubscribe(ctx context.Context, topic string, h broker.BatchHandler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
||||||
|
l, err := t.tunnel.Listen(ctx, topic, tunnel.ListenMode(tunnel.Multicast))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tunSub := &tunBatchSubscriber{
|
||||||
|
topic: topic,
|
||||||
|
handler: h,
|
||||||
|
opts: broker.NewSubscribeOptions(opts...),
|
||||||
|
closed: make(chan bool),
|
||||||
|
listener: l,
|
||||||
|
}
|
||||||
|
|
||||||
|
// start processing
|
||||||
|
go tunSub.run()
|
||||||
|
|
||||||
|
return tunSub, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tunBroker) Subscribe(ctx context.Context, topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
func (t *tunBroker) Subscribe(ctx context.Context, topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
||||||
l, err := t.tunnel.Listen(ctx, topic, tunnel.ListenMode(tunnel.Multicast))
|
l, err := t.tunnel.Listen(ctx, topic, tunnel.ListenMode(tunnel.Multicast))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -100,6 +160,49 @@ func (t *tunBroker) String() string {
|
|||||||
return "tunnel"
|
return "tunnel"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tunBatchSubscriber) run() {
|
||||||
|
for {
|
||||||
|
// accept a new connection
|
||||||
|
c, err := t.listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case <-t.closed:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// receive message
|
||||||
|
m := new(transport.Message)
|
||||||
|
if err := c.Recv(m); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Error(t.opts.Context, err.Error())
|
||||||
|
}
|
||||||
|
if err = c.Close(); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Error(t.opts.Context, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the connection
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
evts := broker.Events{&tunEvent{
|
||||||
|
topic: t.topic,
|
||||||
|
message: &broker.Message{
|
||||||
|
Header: m.Header,
|
||||||
|
Body: m.Body,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
// handle the message
|
||||||
|
go t.handler(evts)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tunSubscriber) run() {
|
func (t *tunSubscriber) run() {
|
||||||
for {
|
for {
|
||||||
// accept a new connection
|
// accept a new connection
|
||||||
@@ -141,6 +244,24 @@ func (t *tunSubscriber) run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tunBatchSubscriber) Options() broker.SubscribeOptions {
|
||||||
|
return t.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunBatchSubscriber) Topic() string {
|
||||||
|
return t.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunBatchSubscriber) Unsubscribe(ctx context.Context) error {
|
||||||
|
select {
|
||||||
|
case <-t.closed:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
close(t.closed)
|
||||||
|
return t.listener.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tunSubscriber) Options() broker.SubscribeOptions {
|
func (t *tunSubscriber) Options() broker.SubscribeOptions {
|
||||||
return t.opts
|
return t.opts
|
||||||
}
|
}
|
||||||
@@ -172,7 +293,11 @@ func (t *tunEvent) Ack() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunEvent) Error() error {
|
func (t *tunEvent) Error() error {
|
||||||
return nil
|
return t.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunEvent) SetError(err error) {
|
||||||
|
t.err = err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBroker returns new tunnel broker
|
// NewBroker returns new tunnel broker
|
||||||
|
@@ -22,23 +22,24 @@ 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,16 +62,16 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Id sets the tunnel id
|
// ID sets the tunnel id
|
||||||
func Id(id string) Option {
|
func ID(id string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Id = id
|
o.ID = id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +164,7 @@ func DialWait(b bool) DialOption {
|
|||||||
// NewOptions returns router default options with filled values
|
// 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(),
|
||||||
Address: DefaultAddress,
|
Address: DefaultAddress,
|
||||||
Token: DefaultToken,
|
Token: DefaultToken,
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
|
@@ -10,9 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type tunTransport struct {
|
type tunTransport struct {
|
||||||
|
tunnel tunnel.Tunnel
|
||||||
options transport.Options
|
options transport.Options
|
||||||
|
|
||||||
tunnel tunnel.Tunnel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type tunnelKey struct{}
|
type tunnelKey struct{}
|
||||||
@@ -88,7 +87,7 @@ func NewTransport(opts ...transport.Option) transport.Transport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// initialise
|
// initialise
|
||||||
//t.Init(opts...)
|
// t.Init(opts...)
|
||||||
|
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
@@ -9,10 +9,8 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/network/transport"
|
"github.com/unistack-org/micro/v3/network/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultTunnel contains default tunnel implementation
|
||||||
// DefaultTunnel contains default tunnel implementation
|
var DefaultTunnel Tunnel
|
||||||
DefaultTunnel Tunnel
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Unicast send over one link
|
// Unicast send over one link
|
||||||
|
69
options.go
69
options.go
@@ -17,39 +17,48 @@ 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/profiler"
|
|
||||||
// "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
|
||||||
@@ -67,8 +76,8 @@ func NewOptions(opts ...Option) Options {
|
|||||||
Meters: []meter.Meter{meter.DefaultMeter},
|
Meters: []meter.Meter{meter.DefaultMeter},
|
||||||
Configs: []config.Config{config.DefaultConfig},
|
Configs: []config.Config{config.DefaultConfig},
|
||||||
Stores: []store.Store{store.DefaultStore},
|
Stores: []store.Store{store.DefaultStore},
|
||||||
//Runtime runtime.Runtime
|
// Runtime runtime.Runtime
|
||||||
//Profile profile.Profile
|
// Profile profile.Profile
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
|
@@ -11,15 +11,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type httpProfile struct {
|
type httpProfile struct {
|
||||||
|
server *http.Server
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
running bool
|
running bool
|
||||||
server *http.Server
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// DefaultAddress for http profiler
|
||||||
// DefaultAddress for http profiler
|
var DefaultAddress = ":6060"
|
||||||
DefaultAddress = ":6060"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Start the profiler
|
// Start the profiler
|
||||||
func (h *httpProfile) Start() error {
|
func (h *httpProfile) Start() error {
|
||||||
|
@@ -13,16 +13,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type profiler struct {
|
type profiler struct {
|
||||||
opts profile.Options
|
exit chan bool
|
||||||
|
cpuFile *os.File
|
||||||
|
memFile *os.File
|
||||||
|
opts profile.Options
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
running bool
|
running bool
|
||||||
exit chan bool
|
|
||||||
|
|
||||||
// where the cpu profile is written
|
|
||||||
cpuFile *os.File
|
|
||||||
// where the mem profile is written
|
|
||||||
memFile *os.File
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *profiler) writeHeap(f *os.File) {
|
func (p *profiler) writeHeap(f *os.File) {
|
||||||
|
@@ -11,10 +11,8 @@ type Profiler interface {
|
|||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// DefaultProfiler holds the default profiler
|
||||||
// DefaultProfiler holds the default profiler
|
var DefaultProfiler Profiler = NewProfiler()
|
||||||
DefaultProfiler Profiler = NewProfiler()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Options holds the options for profiler
|
// Options holds the options for profiler
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
@@ -11,20 +11,20 @@ import (
|
|||||||
|
|
||||||
// Options for proxy
|
// Options for proxy
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// Specific endpoint to always call
|
// Tracer used for tracing
|
||||||
Endpoint string
|
|
||||||
// The default client to use
|
|
||||||
Client client.Client
|
|
||||||
// The default router to use
|
|
||||||
Router router.Router
|
|
||||||
// Extra links for different clients
|
|
||||||
Links map[string]client.Client
|
|
||||||
// Logger
|
|
||||||
Logger logger.Logger
|
|
||||||
// Meter
|
|
||||||
Meter meter.Meter
|
|
||||||
// Tracer
|
|
||||||
Tracer tracer.Tracer
|
Tracer tracer.Tracer
|
||||||
|
// Client for communication
|
||||||
|
Client client.Client
|
||||||
|
// Router for routing
|
||||||
|
Router router.Router
|
||||||
|
// Logger used for logging
|
||||||
|
Logger logger.Logger
|
||||||
|
// Meter used for metrics
|
||||||
|
Meter meter.Meter
|
||||||
|
// Links holds the communication links
|
||||||
|
Links map[string]client.Client
|
||||||
|
// Endpoint holds the destination address
|
||||||
|
Endpoint string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func signature
|
// Option func signature
|
||||||
|
@@ -7,10 +7,8 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/server"
|
"github.com/unistack-org/micro/v3/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultEndpoint holds default proxy address
|
||||||
// DefaultEndpoint holds default proxy address
|
var DefaultEndpoint = "localhost:9090"
|
||||||
DefaultEndpoint = "localhost:9090"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Proxy can be used as a proxy server for micro services
|
// Proxy can be used as a proxy server for micro services
|
||||||
type Proxy interface {
|
type Proxy interface {
|
||||||
|
@@ -3,7 +3,6 @@ package register
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
@@ -11,20 +10,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ExtractValue from reflect.Type from specified depth
|
// ExtractValue from reflect.Type from specified depth
|
||||||
func ExtractValue(v reflect.Type, d int) *Value {
|
func ExtractValue(v reflect.Type, d int) string {
|
||||||
if d == 3 {
|
if d == 3 {
|
||||||
return nil
|
return ""
|
||||||
}
|
}
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Kind() == reflect.Ptr {
|
if v.Kind() == reflect.Ptr {
|
||||||
v = v.Elem()
|
v = v.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v.Name()) == 0 {
|
// slices and maps don't have a defined name
|
||||||
return nil
|
if (v.Kind() == reflect.Slice || v.Kind() == reflect.Map) || len(v.Name()) == 0 {
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the rune character
|
// get the rune character
|
||||||
@@ -32,48 +32,10 @@ func ExtractValue(v reflect.Type, d int) *Value {
|
|||||||
|
|
||||||
// crude check for is unexported field
|
// crude check for is unexported field
|
||||||
if unicode.IsLower(a) {
|
if unicode.IsLower(a) {
|
||||||
return nil
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
arg := &Value{
|
return v.Name()
|
||||||
Name: v.Name(),
|
|
||||||
Type: v.Name(),
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
f := v.Field(i)
|
|
||||||
val := ExtractValue(f.Type, d+1)
|
|
||||||
if val == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we can find a json tag use it
|
|
||||||
if tags := f.Tag.Get("json"); len(tags) > 0 {
|
|
||||||
parts := strings.Split(tags, ",")
|
|
||||||
if parts[0] == "-" || parts[0] == "omitempty" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val.Name = parts[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there's no name default it
|
|
||||||
if len(val.Name) == 0 {
|
|
||||||
val.Name = v.Field(i).Name
|
|
||||||
}
|
|
||||||
|
|
||||||
arg.Values = append(arg.Values, val)
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
p := v.Elem()
|
|
||||||
if p.Kind() == reflect.Ptr {
|
|
||||||
p = p.Elem()
|
|
||||||
}
|
|
||||||
arg.Type = "[]" + p.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
return arg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractEndpoint extract *Endpoint from reflect.Method
|
// ExtractEndpoint extract *Endpoint from reflect.Method
|
||||||
@@ -105,7 +67,7 @@ func ExtractEndpoint(method reflect.Method) *Endpoint {
|
|||||||
|
|
||||||
request := ExtractValue(reqType, 0)
|
request := ExtractValue(reqType, 0)
|
||||||
response := ExtractValue(rspType, 0)
|
response := ExtractValue(rspType, 0)
|
||||||
if request == nil || response == nil {
|
if request == "" || response == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +86,7 @@ func ExtractEndpoint(method reflect.Method) *Endpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExtractSubValue exctact *Value from reflect.Type
|
// ExtractSubValue exctact *Value from reflect.Type
|
||||||
func ExtractSubValue(typ reflect.Type) *Value {
|
func ExtractSubValue(typ reflect.Type) string {
|
||||||
var reqType reflect.Type
|
var reqType reflect.Type
|
||||||
switch typ.NumIn() {
|
switch typ.NumIn() {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -134,7 +96,7 @@ func ExtractSubValue(typ reflect.Type) *Value {
|
|||||||
case 3:
|
case 3:
|
||||||
reqType = typ.In(2)
|
reqType = typ.In(2)
|
||||||
default:
|
default:
|
||||||
return nil
|
return ""
|
||||||
}
|
}
|
||||||
return ExtractValue(reqType, 0)
|
return ExtractValue(reqType, 0)
|
||||||
}
|
}
|
||||||
|
@@ -36,28 +36,21 @@ func TestExtractEndpoint(t *testing.T) {
|
|||||||
t.Fatalf("Expected handler Test, got %s", endpoints[0].Name)
|
t.Fatalf("Expected handler Test, got %s", endpoints[0].Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if endpoints[0].Request == nil {
|
if endpoints[0].Request == "" {
|
||||||
t.Fatal("Expected non nil Request")
|
t.Fatal("Expected non nil Request")
|
||||||
}
|
}
|
||||||
|
|
||||||
if endpoints[0].Response == nil {
|
if endpoints[0].Response == "" {
|
||||||
t.Fatal("Expected non nil Request")
|
t.Fatal("Expected non nil Request")
|
||||||
}
|
}
|
||||||
|
|
||||||
if endpoints[0].Request.Name != "TestRequest" {
|
if endpoints[0].Request != "TestRequest" {
|
||||||
t.Fatalf("Expected TestRequest got %s", endpoints[0].Request.Name)
|
t.Fatalf("Expected TestRequest got %s", endpoints[0].Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
if endpoints[0].Response.Name != "TestResponse" {
|
if endpoints[0].Response != "TestResponse" {
|
||||||
t.Fatalf("Expected TestResponse got %s", endpoints[0].Response.Name)
|
t.Fatalf("Expected TestResponse got %s", endpoints[0].Response)
|
||||||
}
|
|
||||||
|
|
||||||
if endpoints[0].Request.Type != "TestRequest" {
|
|
||||||
t.Fatalf("Expected TestRequest type got %s", endpoints[0].Request.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
if endpoints[0].Response.Type != "TestResponse" {
|
|
||||||
t.Fatalf("Expected TestResponse type got %s", endpoints[0].Response.Type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Logf("XXX %#+v\n", endpoints[0])
|
||||||
}
|
}
|
||||||
|
@@ -16,9 +16,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
*Node
|
|
||||||
TTL time.Duration
|
|
||||||
LastSeen time.Time
|
LastSeen time.Time
|
||||||
|
*Node
|
||||||
|
TTL time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type record struct {
|
type record struct {
|
||||||
@@ -30,10 +30,9 @@ type record struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type memory struct {
|
type memory struct {
|
||||||
opts Options
|
|
||||||
// records is a KV map with domain name as the key and a services map as the value
|
|
||||||
records map[string]services
|
records map[string]services
|
||||||
watchers map[string]*watcher
|
watchers map[string]*watcher
|
||||||
|
opts Options
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +64,7 @@ func (m *memory) ttlPrune() {
|
|||||||
for id, n := range record.Nodes {
|
for id, n := range record.Nodes {
|
||||||
if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL {
|
if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL {
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Register TTL expired for node %s of service %s", n.Id, service)
|
m.opts.Logger.Debugf(m.opts.Context, "Register TTL expired for node %s of service %s", n.ID, service)
|
||||||
}
|
}
|
||||||
delete(m.records[domain][service][version].Nodes, id)
|
delete(m.records[domain][service][version].Nodes, id)
|
||||||
}
|
}
|
||||||
@@ -162,7 +161,7 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio
|
|||||||
|
|
||||||
for _, n := range s.Nodes {
|
for _, n := range s.Nodes {
|
||||||
// check if already exists
|
// check if already exists
|
||||||
if _, ok := srvs[s.Name][s.Version].Nodes[n.Id]; ok {
|
if _, ok := srvs[s.Name][s.Version].Nodes[n.ID]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,9 +176,9 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio
|
|||||||
metadata["domain"] = options.Domain
|
metadata["domain"] = options.Domain
|
||||||
|
|
||||||
// add the node
|
// add the node
|
||||||
srvs[s.Name][s.Version].Nodes[n.Id] = &node{
|
srvs[s.Name][s.Version].Nodes[n.ID] = &node{
|
||||||
Node: &Node{
|
Node: &Node{
|
||||||
Id: n.Id,
|
ID: n.ID,
|
||||||
Address: n.Address,
|
Address: n.Address,
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
},
|
},
|
||||||
@@ -201,8 +200,8 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio
|
|||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Updated registration for service: %s, version: %s", s.Name, s.Version)
|
m.opts.Logger.Debugf(m.opts.Context, "Updated registration for service: %s, version: %s", s.Name, s.Version)
|
||||||
}
|
}
|
||||||
srvs[s.Name][s.Version].Nodes[n.Id].TTL = options.TTL
|
srvs[s.Name][s.Version].Nodes[n.ID].TTL = options.TTL
|
||||||
srvs[s.Name][s.Version].Nodes[n.Id].LastSeen = time.Now()
|
srvs[s.Name][s.Version].Nodes[n.ID].LastSeen = time.Now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,11 +241,11 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO
|
|||||||
|
|
||||||
// deregister all of the service nodes from this version
|
// deregister all of the service nodes from this version
|
||||||
for _, n := range s.Nodes {
|
for _, n := range s.Nodes {
|
||||||
if _, ok := version.Nodes[n.Id]; ok {
|
if _, ok := version.Nodes[n.ID]; ok {
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Register removed node from service: %s, version: %s", s.Name, s.Version)
|
m.opts.Logger.Debugf(m.opts.Context, "Register removed node from service: %s, version: %s", s.Name, s.Version)
|
||||||
}
|
}
|
||||||
delete(version.Nodes, n.Id)
|
delete(version.Nodes, n.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,9 +368,9 @@ func (m *memory) ListServices(ctx context.Context, opts ...ListOption) ([]*Servi
|
|||||||
// serialize the result, each version counts as an individual service
|
// serialize the result, each version counts as an individual service
|
||||||
var result []*Service
|
var result []*Service
|
||||||
|
|
||||||
for domain, service := range services {
|
for _, service := range services {
|
||||||
for _, version := range service {
|
for _, version := range service {
|
||||||
result = append(result, recordToService(version, domain))
|
result = append(result, recordToService(version, options.Domain))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,10 +404,10 @@ func (m *memory) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
id string
|
|
||||||
wo WatchOptions
|
|
||||||
res chan *Result
|
res chan *Result
|
||||||
exit chan bool
|
exit chan bool
|
||||||
|
wo WatchOptions
|
||||||
|
id string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *watcher) Next() (*Result, error) {
|
func (m *watcher) Next() (*Result, error) {
|
||||||
@@ -458,7 +457,7 @@ func serviceToRecord(s *Service, ttl time.Duration) *record {
|
|||||||
|
|
||||||
nodes := make(map[string]*node, len(s.Nodes))
|
nodes := make(map[string]*node, len(s.Nodes))
|
||||||
for _, n := range s.Nodes {
|
for _, n := range s.Nodes {
|
||||||
nodes[n.Id] = &node{
|
nodes[n.ID] = &node{
|
||||||
Node: n,
|
Node: n,
|
||||||
TTL: ttl,
|
TTL: ttl,
|
||||||
LastSeen: time.Now(),
|
LastSeen: time.Now(),
|
||||||
@@ -490,40 +489,31 @@ func recordToService(r *record, domain string) *Service {
|
|||||||
|
|
||||||
endpoints := make([]*Endpoint, len(r.Endpoints))
|
endpoints := make([]*Endpoint, len(r.Endpoints))
|
||||||
for i, e := range r.Endpoints {
|
for i, e := range r.Endpoints {
|
||||||
request := new(Value)
|
md := make(map[string]string, len(e.Metadata))
|
||||||
if e.Request != nil {
|
|
||||||
*request = *e.Request
|
|
||||||
}
|
|
||||||
response := new(Value)
|
|
||||||
if e.Response != nil {
|
|
||||||
*response = *e.Response
|
|
||||||
}
|
|
||||||
|
|
||||||
metadata := make(map[string]string, len(e.Metadata))
|
|
||||||
for k, v := range e.Metadata {
|
for k, v := range e.Metadata {
|
||||||
metadata[k] = v
|
md[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoints[i] = &Endpoint{
|
endpoints[i] = &Endpoint{
|
||||||
Name: e.Name,
|
Name: e.Name,
|
||||||
Request: request,
|
Request: e.Request,
|
||||||
Response: response,
|
Response: e.Response,
|
||||||
Metadata: metadata,
|
Metadata: md,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := make([]*Node, len(r.Nodes))
|
nodes := make([]*Node, len(r.Nodes))
|
||||||
i := 0
|
i := 0
|
||||||
for _, n := range r.Nodes {
|
for _, n := range r.Nodes {
|
||||||
metadata := make(map[string]string, len(n.Metadata))
|
md := make(map[string]string, len(n.Metadata))
|
||||||
for k, v := range n.Metadata {
|
for k, v := range n.Metadata {
|
||||||
metadata[k] = v
|
md[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes[i] = &Node{
|
nodes[i] = &Node{
|
||||||
Id: n.Id,
|
ID: n.ID,
|
||||||
Address: n.Address,
|
Address: n.Address,
|
||||||
Metadata: metadata,
|
Metadata: md,
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@@ -8,72 +8,70 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var testData = map[string][]*Service{
|
||||||
testData = map[string][]*Service{
|
"foo": {
|
||||||
"foo": {
|
{
|
||||||
{
|
Name: "foo",
|
||||||
Name: "foo",
|
Version: "1.0.0",
|
||||||
Version: "1.0.0",
|
Nodes: []*Node{
|
||||||
Nodes: []*Node{
|
{
|
||||||
{
|
ID: "foo-1.0.0-123",
|
||||||
Id: "foo-1.0.0-123",
|
Address: "localhost:9999",
|
||||||
Address: "localhost:9999",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Id: "foo-1.0.0-321",
|
|
||||||
Address: "localhost:9999",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
ID: "foo-1.0.0-321",
|
||||||
Name: "foo",
|
Address: "localhost:9999",
|
||||||
Version: "1.0.1",
|
|
||||||
Nodes: []*Node{
|
|
||||||
{
|
|
||||||
Id: "foo-1.0.1-321",
|
|
||||||
Address: "localhost:6666",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "foo",
|
|
||||||
Version: "1.0.3",
|
|
||||||
Nodes: []*Node{
|
|
||||||
{
|
|
||||||
Id: "foo-1.0.3-345",
|
|
||||||
Address: "localhost:8888",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"bar": {
|
{
|
||||||
{
|
Name: "foo",
|
||||||
Name: "bar",
|
Version: "1.0.1",
|
||||||
Version: "default",
|
Nodes: []*Node{
|
||||||
Nodes: []*Node{
|
{
|
||||||
{
|
ID: "foo-1.0.1-321",
|
||||||
Id: "bar-1.0.0-123",
|
Address: "localhost:6666",
|
||||||
Address: "localhost:9999",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Id: "bar-1.0.0-321",
|
|
||||||
Address: "localhost:9999",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Version: "latest",
|
|
||||||
Nodes: []*Node{
|
|
||||||
{
|
|
||||||
Id: "bar-1.0.1-321",
|
|
||||||
Address: "localhost:6666",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
)
|
Name: "foo",
|
||||||
|
Version: "1.0.3",
|
||||||
|
Nodes: []*Node{
|
||||||
|
{
|
||||||
|
ID: "foo-1.0.3-345",
|
||||||
|
Address: "localhost:8888",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Version: "default",
|
||||||
|
Nodes: []*Node{
|
||||||
|
{
|
||||||
|
ID: "bar-1.0.0-123",
|
||||||
|
Address: "localhost:9999",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "bar-1.0.0-321",
|
||||||
|
Address: "localhost:9999",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Version: "latest",
|
||||||
|
Nodes: []*Node{
|
||||||
|
{
|
||||||
|
ID: "bar-1.0.1-321",
|
||||||
|
Address: "localhost:6666",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func TestMemoryRegistry(t *testing.T) {
|
func TestMemoryRegistry(t *testing.T) {
|
||||||
|
@@ -12,20 +12,22 @@ import (
|
|||||||
|
|
||||||
// Options holds options for register
|
// Options holds options for register
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
// Tracer used for tracing
|
||||||
Addrs []string
|
|
||||||
Timeout time.Duration
|
|
||||||
TLSConfig *tls.Config
|
|
||||||
|
|
||||||
// Logger that will be used
|
|
||||||
Logger logger.Logger
|
|
||||||
// Meter that will be used
|
|
||||||
Meter meter.Meter
|
|
||||||
// Tracer
|
|
||||||
Tracer tracer.Tracer
|
Tracer tracer.Tracer
|
||||||
// Other options for implementations of the interface
|
// Context holds external options
|
||||||
// can be stored in a context
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
|
// Logged used for logging
|
||||||
|
Logger logger.Logger
|
||||||
|
// Meter used for metrics
|
||||||
|
Meter meter.Meter
|
||||||
|
// TLSConfig holds tls.TLSConfig options
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
// Name holds the name of register
|
||||||
|
Name string
|
||||||
|
// Addrs specifies register addrs
|
||||||
|
Addrs []string
|
||||||
|
// Timeout specifies timeout
|
||||||
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions returns options that filled by opts
|
// NewOptions returns options that filled by opts
|
||||||
@@ -42,15 +44,12 @@ func NewOptions(opts ...Option) Options {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint: golint
|
||||||
// RegisterOptions holds options for register method
|
// RegisterOptions holds options for register method
|
||||||
type RegisterOptions struct {
|
type RegisterOptions struct {
|
||||||
TTL time.Duration
|
Context context.Context
|
||||||
// Other options for implementations of the interface
|
Domain string
|
||||||
// can be stored in a context
|
TTL time.Duration
|
||||||
Context context.Context
|
|
||||||
// Domain to register the service in
|
|
||||||
Domain string
|
|
||||||
// Attempts specify attempts for register
|
|
||||||
Attempts int
|
Attempts int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +197,7 @@ func TLSConfig(t *tls.Config) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint: golint
|
||||||
// RegisterAttempts specifies register atempts count
|
// RegisterAttempts specifies register atempts count
|
||||||
func RegisterAttempts(t int) RegisterOption {
|
func RegisterAttempts(t int) RegisterOption {
|
||||||
return func(o *RegisterOptions) {
|
return func(o *RegisterOptions) {
|
||||||
@@ -205,6 +205,7 @@ func RegisterAttempts(t int) RegisterOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint: golint
|
||||||
// RegisterTTL specifies register ttl
|
// RegisterTTL specifies register ttl
|
||||||
func RegisterTTL(t time.Duration) RegisterOption {
|
func RegisterTTL(t time.Duration) RegisterOption {
|
||||||
return func(o *RegisterOptions) {
|
return func(o *RegisterOptions) {
|
||||||
@@ -212,6 +213,7 @@ func RegisterTTL(t time.Duration) RegisterOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint: golint
|
||||||
// RegisterContext sets the register context
|
// RegisterContext sets the register context
|
||||||
func RegisterContext(ctx context.Context) RegisterOption {
|
func RegisterContext(ctx context.Context) RegisterOption {
|
||||||
return func(o *RegisterOptions) {
|
return func(o *RegisterOptions) {
|
||||||
@@ -219,6 +221,7 @@ func RegisterContext(ctx context.Context) RegisterOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint: golint
|
||||||
// RegisterDomain secifies register domain
|
// RegisterDomain secifies register domain
|
||||||
func RegisterDomain(d string) RegisterOption {
|
func RegisterDomain(d string) RegisterOption {
|
||||||
return func(o *RegisterOptions) {
|
return func(o *RegisterOptions) {
|
||||||
|
@@ -52,30 +52,24 @@ type Service struct {
|
|||||||
|
|
||||||
// Node holds node register info
|
// Node holds node register info
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Id string `json:"id"`
|
|
||||||
Address string `json:"address"`
|
|
||||||
Metadata metadata.Metadata `json:"metadata"`
|
Metadata metadata.Metadata `json:"metadata"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Address string `json:"address"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Endpoint holds endpoint register info
|
// Endpoint holds endpoint register info
|
||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
Name string `json:"name"`
|
Request string `json:"request"`
|
||||||
Request *Value `json:"request"`
|
Response string `json:"response"`
|
||||||
Response *Value `json:"response"`
|
|
||||||
Metadata metadata.Metadata `json:"metadata"`
|
Metadata metadata.Metadata `json:"metadata"`
|
||||||
}
|
Name string `json:"name"`
|
||||||
|
|
||||||
// Value holds additional kv stuff
|
|
||||||
type Value struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Values []*Value `json:"values"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func signature
|
// Option func signature
|
||||||
type Option func(*Options)
|
type Option func(*Options)
|
||||||
|
|
||||||
// RegisterOption option is used to register service
|
// RegisterOption option is used to register service
|
||||||
|
// nolint: golint
|
||||||
type RegisterOption func(*RegisterOptions)
|
type RegisterOption func(*RegisterOptions)
|
||||||
|
|
||||||
// WatchOption option is used to watch service changes
|
// WatchOption option is used to watch service changes
|
||||||
|
@@ -7,14 +7,17 @@ import "time"
|
|||||||
type Watcher interface {
|
type Watcher interface {
|
||||||
// Next is a blocking call
|
// Next is a blocking call
|
||||||
Next() (*Result, error)
|
Next() (*Result, error)
|
||||||
|
// Stop stops the watcher
|
||||||
Stop()
|
Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result is returned by a call to Next on
|
// Result is returned by a call to Next on
|
||||||
// the watcher. Actions can be create, update, delete
|
// the watcher. Actions can be create, update, delete
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Action string
|
// Service holds register service
|
||||||
Service *Service
|
Service *Service
|
||||||
|
// Action holds the action
|
||||||
|
Action string
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventType defines register event type
|
// EventType defines register event type
|
||||||
@@ -45,12 +48,12 @@ func (t EventType) String() string {
|
|||||||
|
|
||||||
// Event is register event
|
// Event is register event
|
||||||
type Event struct {
|
type Event struct {
|
||||||
// Id is register id
|
|
||||||
Id string
|
|
||||||
// Type defines type of event
|
|
||||||
Type EventType
|
|
||||||
// Timestamp is event timestamp
|
// Timestamp is event timestamp
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
// Service is register service
|
// Service is register service
|
||||||
Service *Service
|
Service *Service
|
||||||
|
// ID is register id
|
||||||
|
ID string
|
||||||
|
// Type defines type of event
|
||||||
|
Type EventType
|
||||||
}
|
}
|
||||||
|
@@ -12,9 +12,9 @@ import (
|
|||||||
|
|
||||||
// Resolver is a DNS network resolve
|
// Resolver is a DNS network resolve
|
||||||
type Resolver struct {
|
type Resolver struct {
|
||||||
// The resolver address to use
|
|
||||||
Address string
|
|
||||||
goresolver *net.Resolver
|
goresolver *net.Resolver
|
||||||
|
// Address of resolver to use
|
||||||
|
Address string
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
91
router/dns.go
Normal file
91
router/dns.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRouter returns an initialized dns router
|
||||||
|
func NewRouter(opts ...Option) Router {
|
||||||
|
options := NewOptions(opts...)
|
||||||
|
return &dns{options}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dns struct {
|
||||||
|
options Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dns) Init(opts ...Option) error {
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&d.options)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dns) Options() Options {
|
||||||
|
return d.options
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dns) Table() Table {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dns) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dns) Lookup(opts ...QueryOption) ([]Route, error) {
|
||||||
|
options := NewQuery(opts...)
|
||||||
|
// check to see if we have the port provided in the service, e.g. go-micro-srv-foo:8000
|
||||||
|
host, port, err := net.SplitHostPort(options.Service)
|
||||||
|
if err == nil {
|
||||||
|
// lookup the service using A records
|
||||||
|
ips, err := net.LookupHost(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, _ := strconv.Atoi(port)
|
||||||
|
|
||||||
|
// convert the ip addresses to routes
|
||||||
|
result := make([]Route, len(ips))
|
||||||
|
for i, ip := range ips {
|
||||||
|
result[i] = Route{
|
||||||
|
Service: options.Service,
|
||||||
|
Address: fmt.Sprintf("%s:%d", ip, uint16(p)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// we didn't get the port so we'll lookup the service using SRV records. If we can't lookup the
|
||||||
|
// service using the SRV record, we return the error.
|
||||||
|
_, nodes, err := net.LookupSRV(options.Service, "tcp", d.options.Network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the nodes (net services) to routes
|
||||||
|
result := make([]Route, len(nodes))
|
||||||
|
for i, n := range nodes {
|
||||||
|
result[i] = Route{
|
||||||
|
Service: options.Service,
|
||||||
|
Address: fmt.Sprintf("%s:%d", n.Target, n.Port),
|
||||||
|
Network: d.options.Network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dns) Watch(opts ...WatchOption) (Watcher, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dns) Name() string {
|
||||||
|
return d.options.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dns) String() string {
|
||||||
|
return "dns"
|
||||||
|
}
|
@@ -10,23 +10,15 @@ import (
|
|||||||
|
|
||||||
// Options are router options
|
// Options are router options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Name string
|
Logger logger.Logger
|
||||||
// Id is router id
|
Context context.Context
|
||||||
Id string
|
|
||||||
// Address is router address
|
|
||||||
Address string
|
|
||||||
// Gateway is network gateway
|
|
||||||
Gateway string
|
|
||||||
// Network is network address
|
|
||||||
Network string
|
|
||||||
// Register is the local register
|
|
||||||
Register register.Register
|
Register register.Register
|
||||||
// Precache routes
|
Name string
|
||||||
|
Gateway string
|
||||||
|
Network string
|
||||||
|
Id string
|
||||||
|
Address string
|
||||||
Precache bool
|
Precache bool
|
||||||
// Logger
|
|
||||||
Logger logger.Logger
|
|
||||||
// Context for additional options
|
|
||||||
Context context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Id sets Router Id
|
// Id sets Router Id
|
||||||
|
@@ -15,10 +15,10 @@ var (
|
|||||||
|
|
||||||
// Route is network route
|
// Route is network route
|
||||||
type Route struct {
|
type Route struct {
|
||||||
|
// Metadata for the route
|
||||||
|
Metadata metadata.Metadata
|
||||||
// Service is destination service name
|
// Service is destination service name
|
||||||
Service string
|
Service string
|
||||||
// Address is service node address
|
|
||||||
Address string
|
|
||||||
// Gateway is route gateway
|
// Gateway is route gateway
|
||||||
Gateway string
|
Gateway string
|
||||||
// Network is network address
|
// Network is network address
|
||||||
@@ -27,10 +27,10 @@ type Route struct {
|
|||||||
Router string
|
Router string
|
||||||
// Link is network link
|
// Link is network link
|
||||||
Link string
|
Link string
|
||||||
|
// Address is service node address
|
||||||
|
Address string
|
||||||
// Metric is the route cost metric
|
// Metric is the route cost metric
|
||||||
Metric int64
|
Metric int64
|
||||||
// Metadata for the route
|
|
||||||
Metadata metadata.Metadata
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns route hash sum.
|
// Hash returns route hash sum.
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultRouter is the global default router
|
// DefaultRouter is the global default router
|
||||||
DefaultRouter Router
|
DefaultRouter Router = NewRouter()
|
||||||
// DefaultNetwork is default micro network
|
// DefaultNetwork is default micro network
|
||||||
DefaultNetwork = "micro"
|
DefaultNetwork = "micro"
|
||||||
// ErrRouteNotFound is returned when no route was found in the routing table
|
// ErrRouteNotFound is returned when no route was found in the routing table
|
||||||
|
@@ -5,10 +5,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// ErrWatcherStopped is returned when routing table watcher has been stopped
|
||||||
// ErrWatcherStopped is returned when routing table watcher has been stopped
|
var ErrWatcherStopped = errors.New("watcher stopped")
|
||||||
ErrWatcherStopped = errors.New("watcher stopped")
|
|
||||||
)
|
|
||||||
|
|
||||||
// EventType defines routing table event
|
// EventType defines routing table event
|
||||||
type EventType int
|
type EventType int
|
||||||
@@ -38,14 +36,14 @@ func (t EventType) String() string {
|
|||||||
|
|
||||||
// Event is returned by a call to Next on the watcher.
|
// Event is returned by a call to Next on the watcher.
|
||||||
type Event struct {
|
type Event struct {
|
||||||
// Unique id of the event
|
|
||||||
Id string
|
|
||||||
// Type defines type of event
|
|
||||||
Type EventType
|
|
||||||
// Timestamp is event timestamp
|
// Timestamp is event timestamp
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
|
// Id of the event
|
||||||
|
Id string
|
||||||
// Route is table route
|
// Route is table route
|
||||||
Route Route
|
Route Route
|
||||||
|
// Type defines type of event
|
||||||
|
Type EventType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watcher defines routing table watcher interface
|
// Watcher defines routing table watcher interface
|
||||||
|
@@ -10,18 +10,12 @@ import (
|
|||||||
|
|
||||||
// Options configure runtime
|
// Options configure runtime
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// Scheduler for updates
|
|
||||||
Scheduler Scheduler
|
Scheduler Scheduler
|
||||||
// Service type to manage
|
Client client.Client
|
||||||
Type string
|
Logger logger.Logger
|
||||||
// Source of the services repository
|
Type string
|
||||||
Source string
|
Source string
|
||||||
// Base image to use
|
Image string
|
||||||
Image string
|
|
||||||
// Client to use when making requests
|
|
||||||
Client client.Client
|
|
||||||
// Logger
|
|
||||||
Logger logger.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func signature
|
// Option func signature
|
||||||
@@ -77,42 +71,26 @@ type ReadOption func(o *ReadOptions)
|
|||||||
|
|
||||||
// CreateOptions configure runtime services
|
// CreateOptions configure runtime services
|
||||||
type CreateOptions struct {
|
type CreateOptions struct {
|
||||||
// Command to execut
|
Context context.Context
|
||||||
Command []string
|
Output io.Writer
|
||||||
// Args to pass into command
|
|
||||||
Args []string
|
|
||||||
// Environment to configure
|
|
||||||
Env []string
|
|
||||||
// Log output
|
|
||||||
Output io.Writer
|
|
||||||
// Type of service to create
|
|
||||||
Type string
|
|
||||||
// Retries before failing deploy
|
|
||||||
Retries int
|
|
||||||
// Specify the image to use
|
|
||||||
Image string
|
|
||||||
// Namespace to create the service in
|
|
||||||
Namespace string
|
|
||||||
// Specify the context to use
|
|
||||||
Context context.Context
|
|
||||||
// Secrets to use
|
|
||||||
Secrets map[string]string
|
|
||||||
// Resources to allocate the service
|
|
||||||
Resources *Resources
|
Resources *Resources
|
||||||
|
Secrets map[string]string
|
||||||
|
Image string
|
||||||
|
Namespace string
|
||||||
|
Type string
|
||||||
|
Command []string
|
||||||
|
Args []string
|
||||||
|
Env []string
|
||||||
|
Retries int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOptions queries runtime services
|
// ReadOptions queries runtime services
|
||||||
type ReadOptions struct {
|
type ReadOptions struct {
|
||||||
// Service name
|
Context context.Context
|
||||||
Service string
|
Service string
|
||||||
// Version queries services with given version
|
Version string
|
||||||
Version string
|
Type string
|
||||||
// Type of service
|
|
||||||
Type string
|
|
||||||
// Namespace the service is running in
|
|
||||||
Namespace string
|
Namespace string
|
||||||
// Specify the context to use
|
|
||||||
Context context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateType sets the type of service to create
|
// CreateType sets the type of service to create
|
||||||
@@ -238,12 +216,9 @@ type UpdateOption func(o *UpdateOptions)
|
|||||||
|
|
||||||
// UpdateOptions struct
|
// UpdateOptions struct
|
||||||
type UpdateOptions struct {
|
type UpdateOptions struct {
|
||||||
// Namespace the service is running in
|
Context context.Context
|
||||||
|
Secrets map[string]string
|
||||||
Namespace string
|
Namespace string
|
||||||
// Specify the context to use
|
|
||||||
Context context.Context
|
|
||||||
// Secrets to use
|
|
||||||
Secrets map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateSecret sets a secret to provide the service with
|
// UpdateSecret sets a secret to provide the service with
|
||||||
@@ -276,10 +251,8 @@ type DeleteOption func(o *DeleteOptions)
|
|||||||
|
|
||||||
// DeleteOptions struct
|
// DeleteOptions struct
|
||||||
type DeleteOptions struct {
|
type DeleteOptions struct {
|
||||||
// Namespace the service is running in
|
Context context.Context
|
||||||
Namespace string
|
Namespace string
|
||||||
// Specify the context to use
|
|
||||||
Context context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteNamespace sets the namespace
|
// DeleteNamespace sets the namespace
|
||||||
@@ -301,14 +274,10 @@ type LogsOption func(o *LogsOptions)
|
|||||||
|
|
||||||
// LogsOptions configure runtime logging
|
// LogsOptions configure runtime logging
|
||||||
type LogsOptions struct {
|
type LogsOptions struct {
|
||||||
// How many existing lines to show
|
Context context.Context
|
||||||
Count int64
|
|
||||||
// Stream new lines?
|
|
||||||
Stream bool
|
|
||||||
// Namespace the service is running in
|
|
||||||
Namespace string
|
Namespace string
|
||||||
// Specify the context to use
|
Count int64
|
||||||
Context context.Context
|
Stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogsCount confiures how many existing lines to show
|
// LogsCount confiures how many existing lines to show
|
||||||
|
@@ -8,10 +8,8 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/metadata"
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// ErrAlreadyExists error
|
||||||
// ErrAlreadyExists error
|
var ErrAlreadyExists = errors.New("already exists")
|
||||||
ErrAlreadyExists = errors.New("already exists")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Runtime is a service runtime manager
|
// Runtime is a service runtime manager
|
||||||
type Runtime interface {
|
type Runtime interface {
|
||||||
@@ -37,15 +35,20 @@ type Runtime interface {
|
|||||||
|
|
||||||
// Logs returns a log stream
|
// Logs returns a log stream
|
||||||
type Logs interface {
|
type Logs interface {
|
||||||
|
// Error returns error
|
||||||
Error() error
|
Error() error
|
||||||
|
// Chan return chan log
|
||||||
Chan() chan Log
|
Chan() chan Log
|
||||||
|
// Stop stops the log stream
|
||||||
Stop() error
|
Stop() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log is a log message
|
// Log is a log message
|
||||||
type Log struct {
|
type Log struct {
|
||||||
Message string
|
// Metadata holds metadata
|
||||||
Metadata metadata.Metadata
|
Metadata metadata.Metadata
|
||||||
|
// Message holds the message
|
||||||
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scheduler is a runtime service scheduler
|
// Scheduler is a runtime service scheduler
|
||||||
@@ -84,28 +87,28 @@ func (t EventType) String() string {
|
|||||||
|
|
||||||
// Event is notification event
|
// Event is notification event
|
||||||
type Event struct {
|
type Event struct {
|
||||||
// ID of the event
|
// Timestamp of event
|
||||||
ID string
|
|
||||||
// Type is event type
|
|
||||||
Type EventType
|
|
||||||
// Timestamp is event timestamp
|
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
// Service the event relates to
|
// Service the event relates to
|
||||||
Service *Service
|
Service *Service
|
||||||
// Options to use when processing the event
|
// Options to use when processing the event
|
||||||
Options *CreateOptions
|
Options *CreateOptions
|
||||||
|
// ID of the event
|
||||||
|
ID string
|
||||||
|
// Type is event type
|
||||||
|
Type EventType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service is runtime service
|
// Service is runtime service
|
||||||
type Service struct {
|
type Service struct {
|
||||||
|
// Metadata stores metadata
|
||||||
|
Metadata metadata.Metadata
|
||||||
// Name of the service
|
// Name of the service
|
||||||
Name string
|
Name string
|
||||||
// Version of the service
|
// Version of the service
|
||||||
Version string
|
Version string
|
||||||
// url location of source
|
// Name of the service
|
||||||
Source string
|
Source string
|
||||||
// Metadata stores metadata
|
|
||||||
Metadata metadata.Metadata
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resources which are allocated to a serivce
|
// Resources which are allocated to a serivce
|
||||||
|
@@ -5,10 +5,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// ErrNoneAvailable is returned by select when no routes were provided to select from
|
||||||
// ErrNoneAvailable is returned by select when no routes were provided to select from
|
var ErrNoneAvailable = errors.New("none available")
|
||||||
ErrNoneAvailable = errors.New("none available")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Selector selects a route from a pool
|
// Selector selects a route from a pool
|
||||||
type Selector interface {
|
type Selector interface {
|
||||||
|
59
server/errors.go
Normal file
59
server/errors.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import "github.com/unistack-org/micro/v3/errors"
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewError(id string) *Error {
|
||||||
|
return &Error{id}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) BadRequest(format string, a ...interface{}) error {
|
||||||
|
return errors.BadRequest(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Unauthorized(format string, a ...interface{}) error {
|
||||||
|
return errors.Unauthorized(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Forbidden(format string, a ...interface{}) error {
|
||||||
|
return errors.Forbidden(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) NotFound(format string, a ...interface{}) error {
|
||||||
|
return errors.NotFound(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) MethodNotAllowed(format string, a ...interface{}) error {
|
||||||
|
return errors.MethodNotAllowed(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Timeout(format string, a ...interface{}) error {
|
||||||
|
return errors.Timeout(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Conflict(format string, a ...interface{}) error {
|
||||||
|
return errors.Conflict(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) InternalServerError(format string, a ...interface{}) error {
|
||||||
|
return errors.InternalServerError(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) NotImplemented(format string, a ...interface{}) error {
|
||||||
|
return errors.NotImplemented(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) BadGateway(format string, a ...interface{}) error {
|
||||||
|
return errors.BadGateway(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) ServiceUnavailable(format string, a ...interface{}) error {
|
||||||
|
return errors.ServiceUnavailable(e.id, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) GatewayTimeout(format string, a ...interface{}) error {
|
||||||
|
return errors.GatewayTimeout(e.id, format, a...)
|
||||||
|
}
|
19
server/errors_test.go
Normal file
19
server/errors_test.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestError(t *testing.T) {
|
||||||
|
e := NewError("svc1")
|
||||||
|
err := e.BadRequest("%s", "test")
|
||||||
|
merr, ok := err.(*errors.Error)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("error not *errors.Error")
|
||||||
|
}
|
||||||
|
if merr.Id != "svc1" {
|
||||||
|
t.Fatal("id != svc1")
|
||||||
|
}
|
||||||
|
}
|
3
server/generate.go
Normal file
3
server/generate.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
//go:generate protoc -I./health -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:./health health/health.proto
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user