Merge master

This commit is contained in:
Ben Toogood 2020-05-26 15:52:21 +01:00
commit 5712cc9c62
17 changed files with 577 additions and 148 deletions

View File

@ -42,7 +42,7 @@ type Auth interface {
// Revoke access to a resource // Revoke access to a resource
Revoke(rule *Rule) error Revoke(rule *Rule) error
// Rules returns all the rules used to verify requests // Rules returns all the rules used to verify requests
Rules() ([]*Rule, error) Rules(...RulesOption) ([]*Rule, error)
// String returns the name of the implementation // String returns the name of the implementation
String() string String() string
} }

View File

@ -68,7 +68,7 @@ func (n *noop) Revoke(rule *Rule) error {
} }
// Rules used to verify requests // Rules used to verify requests
func (n *noop) Rules() ([]*Rule, error) { func (n *noop) Rules(opts ...RulesOption) ([]*Rule, error) {
return []*Rule{}, nil return []*Rule{}, nil
} }

View File

@ -105,7 +105,7 @@ func (j *jwt) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyO
return rules.Verify(j.rules, acc, res) return rules.Verify(j.rules, acc, res)
} }
func (j *jwt) Rules() ([]*auth.Rule, error) { func (j *jwt) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
j.Lock() j.Lock()
defer j.Unlock() defer j.Unlock()
return j.rules, nil return j.rules, nil

View File

@ -1,6 +1,7 @@
package auth package auth
import ( import (
"context"
"time" "time"
"github.com/micro/go-micro/v2/auth/provider" "github.com/micro/go-micro/v2/auth/provider"
@ -226,13 +227,25 @@ func NewTokenOptions(opts ...TokenOption) TokenOptions {
} }
type VerifyOptions struct { type VerifyOptions struct {
Namespace string Context context.Context
} }
type VerifyOption func(o *VerifyOptions) type VerifyOption func(o *VerifyOptions)
func VerifyNamespace(ns string) VerifyOption { func VerifyContext(ctx context.Context) VerifyOption {
return func(o *VerifyOptions) { return func(o *VerifyOptions) {
o.Namespace = ns o.Context = ctx
}
}
type RulesOptions struct {
Context context.Context
}
type RulesOption func(o *RulesOptions)
func RulesContext(ctx context.Context) RulesOption {
return func(o *RulesOptions) {
o.Context = ctx
} }
} }

View File

@ -3,7 +3,6 @@ package service
import ( import (
"context" "context"
"strings" "strings"
"sync"
"time" "time"
"github.com/micro/go-micro/v2/auth" "github.com/micro/go-micro/v2/auth"
@ -12,19 +11,14 @@ import (
"github.com/micro/go-micro/v2/auth/token" "github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/auth/token/jwt" "github.com/micro/go-micro/v2/auth/token/jwt"
"github.com/micro/go-micro/v2/client" "github.com/micro/go-micro/v2/client"
log "github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/util/jitter"
) )
// svc is the service implementation of the Auth interface // svc is the service implementation of the Auth interface
type svc struct { type svc struct {
options auth.Options options auth.Options
auth pb.AuthService auth pb.AuthService
rule pb.RulesService rules pb.RulesService
jwt token.Provider jwt token.Provider
rules map[string][]*auth.Rule
sync.Mutex
} }
func (s *svc) String() string { func (s *svc) String() string {
@ -41,7 +35,7 @@ func (s *svc) Init(opts ...auth.Option) {
} }
s.auth = pb.NewAuthService("go.micro.auth", s.options.Client) s.auth = pb.NewAuthService("go.micro.auth", s.options.Client)
s.rule = pb.NewRulesService("go.micro.auth", s.options.Client) s.rules = pb.NewRulesService("go.micro.auth", s.options.Client)
// if we have a JWT public key passed as an option, // if we have a JWT public key passed as an option,
// we can decode tokens with the type "JWT" locally // we can decode tokens with the type "JWT" locally
@ -52,8 +46,6 @@ func (s *svc) Init(opts ...auth.Option) {
} }
func (s *svc) Options() auth.Options { func (s *svc) Options() auth.Options {
s.Lock()
defer s.Unlock()
return s.options return s.options
} }
@ -85,7 +77,7 @@ func (s *svc) Grant(rule *auth.Rule) error {
access = pb.Access_DENIED access = pb.Access_DENIED
} }
_, err := s.rule.Create(context.TODO(), &pb.CreateRequest{ _, err := s.rules.Create(context.TODO(), &pb.CreateRequest{
Rule: &pb.Rule{ Rule: &pb.Rule{
Id: rule.ID, Id: rule.ID,
Scope: rule.Scope, Scope: rule.Scope,
@ -99,25 +91,38 @@ func (s *svc) Grant(rule *auth.Rule) error {
}, },
}) })
if err == nil {
go s.loadRules(s.options.Namespace)
}
return err return err
} }
// Revoke access to a resource // Revoke access to a resource
func (s *svc) Revoke(rule *auth.Rule) error { func (s *svc) Revoke(rule *auth.Rule) error {
_, err := s.rule.Delete(context.TODO(), &pb.DeleteRequest{ _, err := s.rules.Delete(context.TODO(), &pb.DeleteRequest{
Id: rule.ID, Id: rule.ID,
}) })
go s.loadRules(s.options.Namespace)
return err return err
} }
func (s *svc) Rules() ([]*auth.Rule, error) { func (s *svc) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
return s.rules[s.options.Namespace], nil var options auth.RulesOptions
for _, o := range opts {
o(&options)
}
if options.Context == nil {
options.Context = context.TODO()
}
rsp, err := s.rules.List(options.Context, &pb.ListRequest{})
if err != nil {
return nil, err
}
rules := make([]*auth.Rule, len(rsp.Rules))
for i, r := range rsp.Rules {
rules[i] = serializeRule(r)
}
return rules, nil
} }
// Verify an account has access to a resource // Verify an account has access to a resource
@ -126,15 +131,13 @@ func (s *svc) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyO
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }
if len(options.Namespace) == 0 {
options.Namespace = s.options.Namespace rs, err := s.Rules(auth.RulesContext(options.Context))
if err != nil {
return err
} }
// load the rules if none are loaded return rules.Verify(rs, acc, res)
s.loadRulesIfEmpty(options.Namespace)
// verify the request using the rules
return rules.Verify(s.rules[options.Namespace], acc, res)
} }
// Inspect a token // Inspect a token
@ -170,53 +173,6 @@ func (s *svc) Token(opts ...auth.TokenOption) (*auth.Token, error) {
return serializeToken(rsp.Token), nil return serializeToken(rsp.Token), nil
} }
// loadRules retrieves the rules from the auth service. Since this implementation is used by micro
// clients, which support muti-tenancy we may have to persist rules in multiple namespaces.
func (s *svc) loadRules(namespace string) {
ctx := metadata.Set(context.TODO(), "Micro-Namespace", namespace)
rsp, err := s.rule.List(ctx, &pb.ListRequest{})
if err != nil {
log.Errorf("Error listing rules: %v", err)
return
}
rules := make([]*auth.Rule, 0, len(rsp.Rules))
for _, r := range rsp.Rules {
var access auth.Access
if r.Access == pb.Access_GRANTED {
access = auth.AccessGranted
} else {
access = auth.AccessDenied
}
rules = append(rules, &auth.Rule{
ID: r.Id,
Scope: r.Scope,
Access: access,
Priority: r.Priority,
Resource: &auth.Resource{
Type: r.Resource.Type,
Name: r.Resource.Name,
Endpoint: r.Resource.Endpoint,
},
})
}
s.Lock()
s.rules[namespace] = rules
s.Unlock()
}
func (s *svc) loadRulesIfEmpty(namespace string) {
s.Lock()
rules := s.rules
s.Unlock()
if _, ok := rules[namespace]; !ok {
s.loadRules(namespace)
}
}
func serializeToken(t *pb.Token) *auth.Token { func serializeToken(t *pb.Token) *auth.Token {
return &auth.Token{ return &auth.Token{
AccessToken: t.AccessToken, AccessToken: t.AccessToken,
@ -236,6 +192,27 @@ func serializeAccount(a *pb.Account) *auth.Account {
} }
} }
func serializeRule(r *pb.Rule) *auth.Rule {
var access auth.Access
if r.Access == pb.Access_GRANTED {
access = auth.AccessGranted
} else {
access = auth.AccessDenied
}
return &auth.Rule{
ID: r.Id,
Scope: r.Scope,
Access: access,
Priority: r.Priority,
Resource: &auth.Resource{
Type: r.Resource.Type,
Name: r.Resource.Name,
Endpoint: r.Resource.Endpoint,
},
}
}
// NewAuth returns a new instance of the Auth service // NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth { func NewAuth(opts ...auth.Option) auth.Auth {
options := auth.NewOptions(opts...) options := auth.NewOptions(opts...)
@ -243,26 +220,9 @@ func NewAuth(opts ...auth.Option) auth.Auth {
options.Client = client.DefaultClient options.Client = client.DefaultClient
} }
service := &svc{ return &svc{
auth: pb.NewAuthService("go.micro.auth", options.Client), auth: pb.NewAuthService("go.micro.auth", options.Client),
rule: pb.NewRulesService("go.micro.auth", options.Client), rules: pb.NewRulesService("go.micro.auth", options.Client),
rules: make(map[string][]*auth.Rule),
options: options, options: options,
} }
// load rules periodically from the auth service
go func() {
ruleTimer := time.NewTicker(time.Second * 30)
for {
<-ruleTimer.C
time.Sleep(jitter.Do(time.Second * 5))
for ns := range service.rules {
service.loadRules(ns)
}
}
}()
return service
} }

66
client/cache.go Normal file
View File

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

76
client/cache_test.go Normal file
View File

@ -0,0 +1,76 @@
package client
import (
"context"
"testing"
"time"
"github.com/micro/go-micro/v2/metadata"
)
func TestCache(t *testing.T) {
ctx := context.TODO()
req := NewRequest("go.micro.service.foo", "Foo.Bar", nil)
t.Run("CacheMiss", func(t *testing.T) {
if _, ok := NewCache().Get(ctx, &req); ok {
t.Errorf("Expected to get no result from Get")
}
})
t.Run("CacheHit", func(t *testing.T) {
c := NewCache()
rsp := "theresponse"
c.Set(ctx, &req, rsp, time.Minute)
if res, ok := c.Get(ctx, &req); !ok {
t.Errorf("Expected a result, got nothing")
} else if res != rsp {
t.Errorf("Expected '%v' result, got '%v'", rsp, res)
}
})
}
func TestCacheKey(t *testing.T) {
ctx := context.TODO()
req1 := NewRequest("go.micro.service.foo", "Foo.Bar", nil)
req2 := NewRequest("go.micro.service.foo", "Foo.Baz", nil)
req3 := NewRequest("go.micro.service.foo", "Foo.Baz", "customquery")
t.Run("IdenticalRequests", func(t *testing.T) {
key1 := key(ctx, &req1)
key2 := key(ctx, &req1)
if key1 != key2 {
t.Errorf("Expected the keys to match for identical requests and context")
}
})
t.Run("DifferentRequestEndpoints", func(t *testing.T) {
key1 := key(ctx, &req1)
key2 := key(ctx, &req2)
if key1 == key2 {
t.Errorf("Expected the keys to differ for different request endpoints")
}
})
t.Run("DifferentRequestBody", func(t *testing.T) {
key1 := key(ctx, &req2)
key2 := key(ctx, &req3)
if key1 == key2 {
t.Errorf("Expected the keys to differ for different request bodies")
}
})
t.Run("DifferentMetadata", func(t *testing.T) {
mdCtx := metadata.Set(context.TODO(), "foo", "bar")
key1 := key(mdCtx, &req1)
key2 := key(ctx, &req1)
if key1 == key2 {
t.Errorf("Expected the keys to differ for different metadata")
}
})
}

View File

@ -29,6 +29,9 @@ type Options struct {
PoolSize int PoolSize int
PoolTTL time.Duration PoolTTL time.Duration
// Response cache
Cache *Cache
// Middleware for client // Middleware for client
Wrappers []Wrapper Wrappers []Wrapper
@ -59,6 +62,8 @@ type CallOptions struct {
StreamTimeout time.Duration StreamTimeout time.Duration
// Use the services own auth token // Use the services own auth token
ServiceToken bool ServiceToken bool
// Duration to cache the response for
CacheExpiry time.Duration
// Middleware for low level call func // Middleware for low level call func
CallWrappers []CallWrapper CallWrappers []CallWrapper
@ -91,6 +96,7 @@ type RequestOptions struct {
func NewOptions(options ...Option) Options { func NewOptions(options ...Option) Options {
opts := Options{ opts := Options{
Cache: NewCache(),
Context: context.Background(), Context: context.Background(),
ContentType: DefaultContentType, ContentType: DefaultContentType,
Codecs: make(map[string]codec.NewCodec), Codecs: make(map[string]codec.NewCodec),
@ -324,6 +330,14 @@ func WithServiceToken() CallOption {
} }
} }
// WithCache is a CallOption which sets the duration the response
// shoull be cached for
func WithCache(c time.Duration) CallOption {
return func(o *CallOptions) {
o.CacheExpiry = c
}
}
func WithMessageContentType(ct string) MessageOption { func WithMessageContentType(ct string) MessageOption {
return func(o *MessageOptions) { return func(o *MessageOptions) {
o.ContentType = ct o.ContentType = ct

View File

@ -472,9 +472,13 @@ func (c *cmd) Before(ctx *cli.Context) error {
var serverOpts []server.Option var serverOpts []server.Option
var clientOpts []client.Option var clientOpts []client.Option
// setup a client to use when calling the runtime // setup a client to use when calling the runtime. It is important the auth client is wrapped
// after the cache client since the wrappers are applied in reverse order and the cache will use
// some of the headers set by the auth client.
authFn := func() auth.Auth { return *c.opts.Auth } authFn := func() auth.Auth { return *c.opts.Auth }
microClient := wrapper.AuthClient(authFn, grpc.NewClient()) cacheFn := func() *client.Cache { return (*c.opts.Client).Options().Cache }
microClient := wrapper.CacheClient(cacheFn, grpc.NewClient())
microClient = wrapper.AuthClient(authFn, microClient)
// Set the store // Set the store
if name := ctx.String("store"); len(name) > 0 { if name := ctx.String("store"); len(name) > 0 {

View File

@ -5,6 +5,7 @@ import (
"context" "context"
"time" "time"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/debug/log" "github.com/micro/go-micro/v2/debug/log"
proto "github.com/micro/go-micro/v2/debug/service/proto" proto "github.com/micro/go-micro/v2/debug/service/proto"
"github.com/micro/go-micro/v2/debug/stats" "github.com/micro/go-micro/v2/debug/stats"
@ -13,11 +14,12 @@ import (
) )
// NewHandler returns an instance of the Debug Handler // NewHandler returns an instance of the Debug Handler
func NewHandler() *Debug { func NewHandler(c client.Client) *Debug {
return &Debug{ return &Debug{
log: log.DefaultLog, log: log.DefaultLog,
stats: stats.DefaultStats, stats: stats.DefaultStats,
trace: trace.DefaultTracer, trace: trace.DefaultTracer,
cache: c.Options().Cache,
} }
} }
@ -30,6 +32,8 @@ type Debug struct {
stats stats.Stats stats stats.Stats
// the tracer // the tracer
trace trace.Tracer trace trace.Tracer
// the cache
cache *client.Cache
} }
func (d *Debug) Health(ctx context.Context, req *proto.HealthRequest, rsp *proto.HealthResponse) error { func (d *Debug) Health(ctx context.Context, req *proto.HealthRequest, rsp *proto.HealthResponse) error {
@ -164,3 +168,9 @@ func (d *Debug) Log(ctx context.Context, stream server.Stream) error {
return nil return nil
} }
// Cache returns all the key value pairs in the client cache
func (d *Debug) Cache(ctx context.Context, req *proto.CacheRequest, rsp *proto.CacheResponse) error {
rsp.Values = d.cache.List()
return nil
}

View File

@ -582,6 +582,76 @@ func (m *Span) GetType() SpanType {
return SpanType_INBOUND return SpanType_INBOUND
} }
type CacheRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CacheRequest) Reset() { *m = CacheRequest{} }
func (m *CacheRequest) String() string { return proto.CompactTextString(m) }
func (*CacheRequest) ProtoMessage() {}
func (*CacheRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_df91f41a5db378e6, []int{9}
}
func (m *CacheRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CacheRequest.Unmarshal(m, b)
}
func (m *CacheRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CacheRequest.Marshal(b, m, deterministic)
}
func (m *CacheRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CacheRequest.Merge(m, src)
}
func (m *CacheRequest) XXX_Size() int {
return xxx_messageInfo_CacheRequest.Size(m)
}
func (m *CacheRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CacheRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CacheRequest proto.InternalMessageInfo
type CacheResponse struct {
Values map[string]string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CacheResponse) Reset() { *m = CacheResponse{} }
func (m *CacheResponse) String() string { return proto.CompactTextString(m) }
func (*CacheResponse) ProtoMessage() {}
func (*CacheResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_df91f41a5db378e6, []int{10}
}
func (m *CacheResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CacheResponse.Unmarshal(m, b)
}
func (m *CacheResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CacheResponse.Marshal(b, m, deterministic)
}
func (m *CacheResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CacheResponse.Merge(m, src)
}
func (m *CacheResponse) XXX_Size() int {
return xxx_messageInfo_CacheResponse.Size(m)
}
func (m *CacheResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CacheResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CacheResponse proto.InternalMessageInfo
func (m *CacheResponse) GetValues() map[string]string {
if m != nil {
return m.Values
}
return nil
}
func init() { func init() {
proto.RegisterEnum("SpanType", SpanType_name, SpanType_value) proto.RegisterEnum("SpanType", SpanType_name, SpanType_value)
proto.RegisterType((*HealthRequest)(nil), "HealthRequest") proto.RegisterType((*HealthRequest)(nil), "HealthRequest")
@ -595,50 +665,56 @@ func init() {
proto.RegisterType((*TraceResponse)(nil), "TraceResponse") proto.RegisterType((*TraceResponse)(nil), "TraceResponse")
proto.RegisterType((*Span)(nil), "Span") proto.RegisterType((*Span)(nil), "Span")
proto.RegisterMapType((map[string]string)(nil), "Span.MetadataEntry") proto.RegisterMapType((map[string]string)(nil), "Span.MetadataEntry")
proto.RegisterType((*CacheRequest)(nil), "CacheRequest")
proto.RegisterType((*CacheResponse)(nil), "CacheResponse")
proto.RegisterMapType((map[string]string)(nil), "CacheResponse.ValuesEntry")
} }
func init() { proto.RegisterFile("debug/service/proto/debug.proto", fileDescriptor_df91f41a5db378e6) } func init() { proto.RegisterFile("debug/service/proto/debug.proto", fileDescriptor_df91f41a5db378e6) }
var fileDescriptor_df91f41a5db378e6 = []byte{ var fileDescriptor_df91f41a5db378e6 = []byte{
// 594 bytes of a gzipped FileDescriptorProto // 646 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xdb, 0x6e, 0xd3, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xdb, 0x6e, 0xd3, 0x4a,
0x10, 0x8d, 0xed, 0x38, 0xb1, 0xa7, 0x8d, 0xa9, 0x96, 0x8b, 0x2c, 0x73, 0x69, 0x65, 0x09, 0x29, 0x14, 0x8d, 0xed, 0x38, 0xb1, 0x77, 0x62, 0x9f, 0x6a, 0xce, 0x45, 0x96, 0x0f, 0xd0, 0xca, 0x12,
0x5c, 0xe4, 0x40, 0x79, 0x41, 0xf0, 0x86, 0x8a, 0x04, 0x52, 0x69, 0xa5, 0x6d, 0xfb, 0x01, 0x5b, 0x52, 0xb8, 0x68, 0x02, 0xe1, 0x85, 0xcb, 0x1b, 0x14, 0x09, 0xa4, 0xd2, 0x4a, 0xd3, 0x96, 0xf7,
0x7b, 0xe4, 0x1a, 0xea, 0x0b, 0xbb, 0xeb, 0x4a, 0xf9, 0x16, 0xbe, 0x80, 0x37, 0x7e, 0x86, 0xff, 0xa9, 0x3d, 0x4a, 0x03, 0xf5, 0x85, 0x99, 0x71, 0xa5, 0xbc, 0xf0, 0x23, 0xfc, 0x04, 0xff, 0x82,
0x41, 0x7b, 0x71, 0x1b, 0x0b, 0xa1, 0x3e, 0xf0, 0xb6, 0xe7, 0xec, 0xec, 0xc9, 0xcc, 0xc9, 0xf1, 0xf8, 0x1f, 0x34, 0x17, 0xb7, 0xb6, 0x10, 0xaa, 0x10, 0x6f, 0x5e, 0x6b, 0xaf, 0xd9, 0xd9, 0x7b,
0xc0, 0x6e, 0x81, 0xe7, 0x7d, 0xb9, 0x12, 0xc8, 0xaf, 0xaa, 0x1c, 0x57, 0x1d, 0x6f, 0x65, 0xbb, 0x69, 0x65, 0xc3, 0x6e, 0xc1, 0xce, 0xda, 0xf5, 0x52, 0x30, 0x7e, 0xb9, 0xc9, 0xd9, 0xb2, 0xe1,
0xd2, 0x5c, 0xa6, 0xcf, 0xe9, 0x33, 0x58, 0x7c, 0x42, 0x76, 0x29, 0x2f, 0x28, 0x7e, 0xef, 0x51, 0xb5, 0xac, 0x97, 0x9a, 0xc3, 0xfa, 0x3b, 0xbb, 0x07, 0xd1, 0x1b, 0x46, 0x2f, 0xe4, 0x39, 0x61,
0x48, 0x12, 0xc3, 0xdc, 0x56, 0xc7, 0xce, 0x9e, 0xb3, 0x0c, 0xe9, 0x00, 0xd3, 0x25, 0x44, 0x43, 0x9f, 0x5a, 0x26, 0x24, 0x4a, 0x60, 0x6a, 0xd5, 0x89, 0xb3, 0xe7, 0x2c, 0x42, 0xd2, 0xc1, 0x6c,
0xa9, 0xe8, 0xda, 0x46, 0x20, 0x79, 0x00, 0x33, 0x21, 0x99, 0xec, 0x85, 0x2d, 0xb5, 0x28, 0x5d, 0x01, 0x71, 0x27, 0x15, 0x4d, 0x5d, 0x09, 0x86, 0xfe, 0x83, 0x89, 0x90, 0x54, 0xb6, 0xc2, 0x4a,
0xc2, 0xf6, 0x89, 0x64, 0x52, 0xdc, 0xae, 0xf9, 0xdb, 0x81, 0x85, 0x2d, 0xb5, 0x9a, 0x8f, 0x20, 0x2d, 0xca, 0x16, 0x30, 0x3f, 0x96, 0x54, 0x8a, 0x9b, 0x7b, 0x7e, 0x77, 0x20, 0xb2, 0x52, 0xdb,
0x94, 0x55, 0x8d, 0x42, 0xb2, 0xba, 0xd3, 0xd5, 0x53, 0x7a, 0x43, 0x68, 0x25, 0xc9, 0xb8, 0xc4, 0xf3, 0x16, 0x84, 0x72, 0x53, 0x32, 0x21, 0x69, 0xd9, 0x68, 0xf5, 0x98, 0x5c, 0x13, 0xba, 0x93,
0x22, 0x76, 0xf5, 0xdd, 0x00, 0x55, 0x2f, 0x7d, 0xa7, 0x0a, 0x63, 0x4f, 0x5f, 0x58, 0xa4, 0xf8, 0xa4, 0x5c, 0xb2, 0x22, 0x71, 0x75, 0xad, 0x83, 0x6a, 0x96, 0xb6, 0x51, 0xc2, 0xc4, 0xd3, 0x05,
0x1a, 0xeb, 0x96, 0xaf, 0xe3, 0xa9, 0xe1, 0x0d, 0x52, 0x4a, 0xf2, 0x82, 0x23, 0x2b, 0x44, 0xec, 0x8b, 0x14, 0x5f, 0xb2, 0xb2, 0xe6, 0xdb, 0x64, 0x6c, 0x78, 0x83, 0x54, 0x27, 0x79, 0xce, 0x19,
0x1b, 0x25, 0x0b, 0x49, 0x04, 0x6e, 0x99, 0xc7, 0x33, 0x4d, 0xba, 0x65, 0x4e, 0x12, 0x08, 0xb8, 0x2d, 0x44, 0xe2, 0x9b, 0x4e, 0x16, 0xa2, 0x18, 0xdc, 0x75, 0x9e, 0x4c, 0x34, 0xe9, 0xae, 0x73,
0x19, 0x44, 0xc4, 0x73, 0xcd, 0x5e, 0x63, 0xa5, 0x8e, 0x9c, 0xb7, 0x5c, 0xc4, 0x81, 0x51, 0x37, 0x94, 0x42, 0xc0, 0xcd, 0x22, 0x22, 0x99, 0x6a, 0xf6, 0x0a, 0xab, 0xee, 0x8c, 0xf3, 0x9a, 0x8b,
0x28, 0xfd, 0x0a, 0x70, 0xd8, 0x96, 0xb7, 0xce, 0x6f, 0x1c, 0xe4, 0xc8, 0x6a, 0x3d, 0x4e, 0x40, 0x24, 0x30, 0xdd, 0x0d, 0xca, 0x3e, 0x00, 0x1c, 0xd4, 0xeb, 0x1b, 0xf7, 0x37, 0x0e, 0x72, 0x46,
0x2d, 0x22, 0xf7, 0xc0, 0xcf, 0xdb, 0xbe, 0x91, 0x7a, 0x18, 0x8f, 0x1a, 0xa0, 0x58, 0x51, 0x35, 0x4b, 0xbd, 0x4e, 0x40, 0x2c, 0x42, 0xff, 0x80, 0x9f, 0xd7, 0x6d, 0x25, 0xf5, 0x32, 0x1e, 0x31,
0x39, 0xea, 0x51, 0x3c, 0x6a, 0x40, 0xfa, 0xcb, 0x81, 0x19, 0xc5, 0xbc, 0xe5, 0xc5, 0xdf, 0xe6, 0x40, 0xb1, 0x62, 0x53, 0xe5, 0x4c, 0xaf, 0xe2, 0x11, 0x03, 0xb2, 0xaf, 0x0e, 0x4c, 0x08, 0xcb,
0x79, 0x9b, 0xe6, 0xbd, 0x86, 0xa0, 0x46, 0xc9, 0x0a, 0x26, 0x59, 0xec, 0xee, 0x79, 0xcb, 0xad, 0x6b, 0x5e, 0xfc, 0x6c, 0x9e, 0xd7, 0x37, 0xef, 0x31, 0x04, 0x25, 0x93, 0xb4, 0xa0, 0x92, 0x26,
0xfd, 0xfb, 0x99, 0x79, 0x98, 0x7d, 0xb1, 0xfc, 0xc7, 0x46, 0xf2, 0x35, 0xbd, 0x2e, 0x53, 0x9d, 0xee, 0x9e, 0xb7, 0x98, 0xad, 0xfe, 0xc5, 0xe6, 0x21, 0x7e, 0x67, 0xf9, 0xd7, 0x95, 0xe4, 0x5b,
0xd7, 0x28, 0x04, 0x2b, 0x8d, 0xad, 0x21, 0x1d, 0x60, 0xf2, 0x1e, 0x16, 0xa3, 0x47, 0x64, 0x07, 0x72, 0x25, 0x53, 0x93, 0x97, 0x4c, 0x08, 0xba, 0x36, 0xb6, 0x86, 0xa4, 0x83, 0xe9, 0x0b, 0x88,
0xbc, 0x6f, 0xb8, 0xb6, 0x03, 0xaa, 0xa3, 0x6a, 0xf7, 0x8a, 0x5d, 0xf6, 0xa8, 0x67, 0x0b, 0xa9, 0x06, 0x8f, 0xd0, 0x0e, 0x78, 0x1f, 0xd9, 0xd6, 0x2e, 0xa8, 0x3e, 0xd5, 0xb8, 0x97, 0xf4, 0xa2,
0x01, 0xef, 0xdc, 0xb7, 0x4e, 0xfa, 0x04, 0xb6, 0x4f, 0x39, 0xcb, 0x71, 0x30, 0x28, 0x02, 0xb7, 0x65, 0x7a, 0xb7, 0x90, 0x18, 0xf0, 0xdc, 0x7d, 0xea, 0x64, 0x77, 0x60, 0x7e, 0xc2, 0x69, 0xce,
0x2a, 0xec, 0x53, 0xb7, 0x2a, 0xd2, 0x97, 0xb0, 0xb0, 0xf7, 0x36, 0x15, 0x0f, 0xc1, 0x17, 0x1d, 0x3a, 0x83, 0x62, 0x70, 0x37, 0x85, 0x7d, 0xea, 0x6e, 0x8a, 0xec, 0x21, 0x44, 0xb6, 0x6e, 0x53,
0x6b, 0x54, 0xd0, 0x54, 0xdf, 0x7e, 0x76, 0xd2, 0xb1, 0x86, 0x1a, 0x2e, 0xfd, 0xe1, 0xc2, 0x54, 0xf1, 0x3f, 0xf8, 0xa2, 0xa1, 0x95, 0x0a, 0x9a, 0x9a, 0xdb, 0xc7, 0xc7, 0x0d, 0xad, 0x88, 0xe1,
0x61, 0xf5, 0x83, 0x52, 0x3d, 0xb3, 0x4a, 0x06, 0x58, 0x71, 0x77, 0x10, 0x57, 0x9e, 0x77, 0x8c, 0xb2, 0x2f, 0x2e, 0x8c, 0x15, 0x56, 0x3f, 0x28, 0xd5, 0x33, 0xdb, 0xc9, 0x00, 0xdb, 0xdc, 0xed,
0xa3, 0x35, 0x37, 0xa4, 0x16, 0x11, 0x02, 0xd3, 0x86, 0xd5, 0xc6, 0xdc, 0x90, 0xea, 0xf3, 0x66, 0x9a, 0x2b, 0xcf, 0x1b, 0xca, 0x99, 0x35, 0x37, 0x24, 0x16, 0x21, 0x04, 0xe3, 0x8a, 0x96, 0xc6,
0xde, 0xfc, 0x71, 0xde, 0x12, 0x08, 0x8a, 0x9e, 0x33, 0x59, 0xb5, 0x8d, 0xcd, 0xca, 0x35, 0x26, 0xdc, 0x90, 0xe8, 0xef, 0x7e, 0xde, 0xfc, 0x61, 0xde, 0x52, 0x08, 0x8a, 0x96, 0x53, 0xb9, 0xa9,
0xab, 0x0d, 0xa3, 0xe7, 0xba, 0xe1, 0xbb, 0xba, 0xe1, 0x7f, 0xda, 0xfc, 0x18, 0xa6, 0x72, 0xdd, 0x2b, 0x9b, 0x95, 0x2b, 0x8c, 0x96, 0x3d, 0xa3, 0xa7, 0x7a, 0xe0, 0xbf, 0xf5, 0xc0, 0xbf, 0xb4,
0xa1, 0x0e, 0x51, 0xb4, 0x1f, 0xea, 0xe2, 0xd3, 0x75, 0x87, 0x54, 0xd3, 0xff, 0xe5, 0xf5, 0xf3, 0xf9, 0x36, 0x8c, 0xe5, 0xb6, 0x61, 0x3a, 0x44, 0xf1, 0x2a, 0xd4, 0xe2, 0x93, 0x6d, 0xc3, 0x88,
0xa7, 0x10, 0x0c, 0x72, 0x64, 0x0b, 0xe6, 0x9f, 0x8f, 0x3e, 0x1c, 0x9f, 0x1d, 0x1d, 0xec, 0x4c, 0xa6, 0xff, 0xcc, 0xeb, 0x18, 0xe6, 0xaf, 0x68, 0x7e, 0xde, 0x79, 0x9d, 0x7d, 0x86, 0xc8, 0x62,
0xc8, 0x36, 0x04, 0xc7, 0x67, 0xa7, 0x06, 0x39, 0xfb, 0x3f, 0x1d, 0xf0, 0x0f, 0xd4, 0x62, 0x20, 0xeb, 0xed, 0x0a, 0x26, 0x5a, 0xdd, 0x99, 0x9b, 0xe2, 0x41, 0x1d, 0xbf, 0xd7, 0x45, 0x33, 0xb2,
0xbb, 0xe0, 0x1d, 0xb6, 0x25, 0xd9, 0xca, 0x6e, 0x12, 0x9c, 0xcc, 0x6d, 0x50, 0xd2, 0xc9, 0x2b, 0x55, 0xa6, 0xcf, 0x60, 0xd6, 0xa3, 0x7f, 0x67, 0x9e, 0xfb, 0x77, 0x21, 0xe8, 0xd6, 0x43, 0x33,
0x87, 0xbc, 0x80, 0x99, 0x59, 0x04, 0x24, 0xca, 0x46, 0xcb, 0x23, 0xb9, 0x93, 0x8d, 0x37, 0x44, 0x98, 0xbe, 0x3d, 0x7c, 0x79, 0x74, 0x7a, 0xb8, 0xbf, 0x33, 0x42, 0x73, 0x08, 0x8e, 0x4e, 0x4f,
0x3a, 0x21, 0x4b, 0xf0, 0xf5, 0x07, 0x4e, 0x16, 0xd9, 0xe6, 0x4e, 0x48, 0xa2, 0x6c, 0xf4, 0xdd, 0x0c, 0x72, 0x56, 0xdf, 0x1c, 0xf0, 0xf7, 0xd5, 0xa1, 0x42, 0xbb, 0xe0, 0x1d, 0xd4, 0x6b, 0x34,
0x9b, 0x4a, 0xfd, 0xa7, 0x93, 0x45, 0xb6, 0x19, 0x8e, 0x24, 0xca, 0x46, 0x59, 0x48, 0x27, 0xe7, 0xc3, 0xd7, 0xff, 0xa8, 0x74, 0x6a, 0x83, 0x9b, 0x8d, 0x1e, 0x39, 0xe8, 0x01, 0x4c, 0xcc, 0x61,
0x33, 0xbd, 0xbb, 0xde, 0xfc, 0x09, 0x00, 0x00, 0xff, 0xff, 0x6e, 0x42, 0x7f, 0x05, 0xde, 0x04, 0x42, 0x31, 0x1e, 0x1c, 0xb3, 0xf4, 0x2f, 0x3c, 0xbc, 0x58, 0xd9, 0x08, 0x2d, 0xc0, 0xd7, 0x07,
0x00, 0x00, 0x07, 0x45, 0xb8, 0x7f, 0xa3, 0xd2, 0x18, 0x0f, 0xee, 0x90, 0x51, 0xea, 0x10, 0xa2, 0x08, 0xf7,
0xc3, 0x9a, 0xc6, 0x78, 0x90, 0x4d, 0xa3, 0xd4, 0x96, 0xa1, 0x08, 0xf7, 0xad, 0x4e, 0xe3, 0xa1,
0x93, 0xd9, 0xe8, 0x6c, 0xa2, 0xaf, 0xee, 0x93, 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x22, 0x65,
0x99, 0x10, 0x98, 0x05, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -657,6 +733,7 @@ type DebugClient interface {
Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error) Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error)
Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error) Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error)
Trace(ctx context.Context, in *TraceRequest, opts ...grpc.CallOption) (*TraceResponse, error) Trace(ctx context.Context, in *TraceRequest, opts ...grpc.CallOption) (*TraceResponse, error)
Cache(ctx context.Context, in *CacheRequest, opts ...grpc.CallOption) (*CacheResponse, error)
} }
type debugClient struct { type debugClient struct {
@ -726,12 +803,22 @@ func (c *debugClient) Trace(ctx context.Context, in *TraceRequest, opts ...grpc.
return out, nil return out, nil
} }
func (c *debugClient) Cache(ctx context.Context, in *CacheRequest, opts ...grpc.CallOption) (*CacheResponse, error) {
out := new(CacheResponse)
err := c.cc.Invoke(ctx, "/Debug/Cache", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// DebugServer is the server API for Debug service. // DebugServer is the server API for Debug service.
type DebugServer interface { type DebugServer interface {
Log(*LogRequest, Debug_LogServer) error Log(*LogRequest, Debug_LogServer) error
Health(context.Context, *HealthRequest) (*HealthResponse, error) Health(context.Context, *HealthRequest) (*HealthResponse, error)
Stats(context.Context, *StatsRequest) (*StatsResponse, error) Stats(context.Context, *StatsRequest) (*StatsResponse, error)
Trace(context.Context, *TraceRequest) (*TraceResponse, error) Trace(context.Context, *TraceRequest) (*TraceResponse, error)
Cache(context.Context, *CacheRequest) (*CacheResponse, error)
} }
// UnimplementedDebugServer can be embedded to have forward compatible implementations. // UnimplementedDebugServer can be embedded to have forward compatible implementations.
@ -750,6 +837,9 @@ func (*UnimplementedDebugServer) Stats(ctx context.Context, req *StatsRequest) (
func (*UnimplementedDebugServer) Trace(ctx context.Context, req *TraceRequest) (*TraceResponse, error) { func (*UnimplementedDebugServer) Trace(ctx context.Context, req *TraceRequest) (*TraceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Trace not implemented") return nil, status.Errorf(codes.Unimplemented, "method Trace not implemented")
} }
func (*UnimplementedDebugServer) Cache(ctx context.Context, req *CacheRequest) (*CacheResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Cache not implemented")
}
func RegisterDebugServer(s *grpc.Server, srv DebugServer) { func RegisterDebugServer(s *grpc.Server, srv DebugServer) {
s.RegisterService(&_Debug_serviceDesc, srv) s.RegisterService(&_Debug_serviceDesc, srv)
@ -830,6 +920,24 @@ func _Debug_Trace_Handler(srv interface{}, ctx context.Context, dec func(interfa
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Debug_Cache_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CacheRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DebugServer).Cache(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Debug/Cache",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DebugServer).Cache(ctx, req.(*CacheRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Debug_serviceDesc = grpc.ServiceDesc{ var _Debug_serviceDesc = grpc.ServiceDesc{
ServiceName: "Debug", ServiceName: "Debug",
HandlerType: (*DebugServer)(nil), HandlerType: (*DebugServer)(nil),
@ -846,6 +954,10 @@ var _Debug_serviceDesc = grpc.ServiceDesc{
MethodName: "Trace", MethodName: "Trace",
Handler: _Debug_Trace_Handler, Handler: _Debug_Trace_Handler,
}, },
{
MethodName: "Cache",
Handler: _Debug_Cache_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {

View File

@ -46,6 +46,7 @@ type DebugService interface {
Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error) Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error)
Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error) Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error)
Trace(ctx context.Context, in *TraceRequest, opts ...client.CallOption) (*TraceResponse, error) Trace(ctx context.Context, in *TraceRequest, opts ...client.CallOption) (*TraceResponse, error)
Cache(ctx context.Context, in *CacheRequest, opts ...client.CallOption) (*CacheResponse, error)
} }
type debugService struct { type debugService struct {
@ -139,6 +140,16 @@ func (c *debugService) Trace(ctx context.Context, in *TraceRequest, opts ...clie
return out, nil return out, nil
} }
func (c *debugService) Cache(ctx context.Context, in *CacheRequest, opts ...client.CallOption) (*CacheResponse, error) {
req := c.c.NewRequest(c.name, "Debug.Cache", in)
out := new(CacheResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Debug service // Server API for Debug service
type DebugHandler interface { type DebugHandler interface {
@ -146,6 +157,7 @@ type DebugHandler interface {
Health(context.Context, *HealthRequest, *HealthResponse) error Health(context.Context, *HealthRequest, *HealthResponse) error
Stats(context.Context, *StatsRequest, *StatsResponse) error Stats(context.Context, *StatsRequest, *StatsResponse) error
Trace(context.Context, *TraceRequest, *TraceResponse) error Trace(context.Context, *TraceRequest, *TraceResponse) error
Cache(context.Context, *CacheRequest, *CacheResponse) error
} }
func RegisterDebugHandler(s server.Server, hdlr DebugHandler, opts ...server.HandlerOption) error { func RegisterDebugHandler(s server.Server, hdlr DebugHandler, opts ...server.HandlerOption) error {
@ -154,6 +166,7 @@ func RegisterDebugHandler(s server.Server, hdlr DebugHandler, opts ...server.Han
Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error
Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error
Trace(ctx context.Context, in *TraceRequest, out *TraceResponse) error Trace(ctx context.Context, in *TraceRequest, out *TraceResponse) error
Cache(ctx context.Context, in *CacheRequest, out *CacheResponse) error
} }
type Debug struct { type Debug struct {
debug debug
@ -217,3 +230,7 @@ func (h *debugHandler) Stats(ctx context.Context, in *StatsRequest, out *StatsRe
func (h *debugHandler) Trace(ctx context.Context, in *TraceRequest, out *TraceResponse) error { func (h *debugHandler) Trace(ctx context.Context, in *TraceRequest, out *TraceResponse) error {
return h.DebugHandler.Trace(ctx, in, out) return h.DebugHandler.Trace(ctx, in, out)
} }
func (h *debugHandler) Cache(ctx context.Context, in *CacheRequest, out *CacheResponse) error {
return h.DebugHandler.Cache(ctx, in, out)
}

View File

@ -1,10 +1,11 @@
syntax = "proto3"; syntax = "proto3";
service Debug { service Debug {
rpc Log(LogRequest) returns (stream Record) {}; rpc Log(LogRequest) returns (stream Record) {};
rpc Health(HealthRequest) returns (HealthResponse) {}; rpc Health(HealthRequest) returns (HealthResponse) {};
rpc Stats(StatsRequest) returns (StatsResponse) {}; rpc Stats(StatsRequest) returns (StatsResponse) {};
rpc Trace(TraceRequest) returns (TraceResponse) {}; rpc Trace(TraceRequest) returns (TraceResponse) {};
rpc Cache(CacheRequest) returns (CacheResponse) {};
} }
message HealthRequest { message HealthRequest {
@ -97,3 +98,9 @@ message Span {
map<string,string> metadata = 7; map<string,string> metadata = 7;
SpanType type = 8; SpanType type = 8;
} }
message CacheRequest {}
message CacheResponse {
map<string, string> values = 1;
}

View File

@ -35,13 +35,14 @@ func newService(opts ...Option) Service {
// service name // service name
serviceName := options.Server.Options().Name serviceName := options.Server.Options().Name
// authFn returns the auth, we pass as a function since auth // we pass functions to the wrappers since the values can change during initialisation
// has not yet been set at this point.
authFn := func() auth.Auth { return options.Server.Options().Auth } authFn := func() auth.Auth { return options.Server.Options().Auth }
cacheFn := func() *client.Cache { return options.Client.Options().Cache }
// wrap client to inject From-Service header on any calls // wrap client to inject From-Service header on any calls
options.Client = wrapper.FromService(serviceName, options.Client) options.Client = wrapper.FromService(serviceName, options.Client)
options.Client = wrapper.TraceCall(serviceName, trace.DefaultTracer, options.Client) options.Client = wrapper.TraceCall(serviceName, trace.DefaultTracer, options.Client)
options.Client = wrapper.CacheClient(cacheFn, options.Client)
options.Client = wrapper.AuthClient(authFn, options.Client) options.Client = wrapper.AuthClient(authFn, options.Client)
// wrap the server to provide handler stats // wrap the server to provide handler stats
@ -183,7 +184,7 @@ func (s *service) Run() error {
// register the debug handler // register the debug handler
s.opts.Server.Handle( s.opts.Server.Handle(
s.opts.Server.NewHandler( s.opts.Server.NewHandler(
handler.NewHandler(), handler.NewHandler(s.opts.Client),
server.InternalHandler(true), server.InternalHandler(true),
), ),
) )

View File

@ -5,6 +5,7 @@ import (
"context" "context"
"sync" "sync"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/debug/service/handler" "github.com/micro/go-micro/v2/debug/service/handler"
"github.com/micro/go-micro/v2/proxy" "github.com/micro/go-micro/v2/proxy"
"github.com/micro/go-micro/v2/server" "github.com/micro/go-micro/v2/server"
@ -42,7 +43,7 @@ func New(name string, p proxy.Proxy) *Server {
server.DefaultRouter.Handle( server.DefaultRouter.Handle(
// inject the debug handler // inject the debug handler
server.DefaultRouter.NewHandler( server.DefaultRouter.NewHandler(
handler.NewHandler(), handler.NewHandler(client.DefaultClient),
server.InternalHandler(true), server.InternalHandler(true),
), ),
) )

View File

@ -2,6 +2,7 @@ package wrapper
import ( import (
"context" "context"
"reflect"
"strings" "strings"
"github.com/micro/go-micro/v2/auth" "github.com/micro/go-micro/v2/auth"
@ -221,7 +222,7 @@ func AuthHandler(fn func() auth.Auth) server.HandlerWrapper {
} }
// Verify the caller has access to the resource // Verify the caller has access to the resource
err := a.Verify(account, res, auth.VerifyNamespace(ns)) err := a.Verify(account, res, auth.VerifyContext(ctx))
if err != nil && account != nil { if err != nil && account != nil {
return errors.Forbidden(req.Service(), "Forbidden call made to %v:%v by %v", req.Service(), req.Endpoint(), account.ID) return errors.Forbidden(req.Service(), "Forbidden call made to %v:%v by %v", req.Service(), req.Endpoint(), account.ID)
} else if err != nil { } else if err != nil {
@ -238,3 +239,55 @@ func AuthHandler(fn func() auth.Auth) server.HandlerWrapper {
} }
} }
} }
type cacheWrapper struct {
cacheFn func() *client.Cache
client.Client
}
// Call executes the request. If the CacheExpiry option was set, the response will be cached using
// a hash of the metadata and request as the key.
func (c *cacheWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
// parse the options
var options client.CallOptions
for _, o := range opts {
o(&options)
}
// if the client doesn't have a cacbe setup don't continue
cache := c.cacheFn()
if cache == nil {
return c.Client.Call(ctx, req, rsp, opts...)
}
// if the cache expiry is not set, execute the call without the cache
if options.CacheExpiry == 0 {
return c.Client.Call(ctx, req, rsp, opts...)
}
// if the response is nil don't call the cache since we can't assign the response
if rsp == nil {
return c.Client.Call(ctx, req, rsp, opts...)
}
// check to see if there is a response cached, if there is assign it
if r, ok := cache.Get(ctx, &req); ok {
val := reflect.ValueOf(rsp).Elem()
val.Set(reflect.ValueOf(r).Elem())
return nil
}
// don't cache the result if there was an error
if err := c.Client.Call(ctx, req, rsp, opts...); err != nil {
return err
}
// set the result in the cache
cache.Set(ctx, &req, rsp, options.CacheExpiry)
return nil
}
// CacheClient wraps requests with the cache wrapper
func CacheClient(cacheFn func() *client.Cache, c client.Client) client.Client {
return &cacheWrapper{cacheFn, c}
}

View File

@ -3,10 +3,13 @@ package wrapper
import ( import (
"context" "context"
"net/http" "net/http"
"reflect"
"testing" "testing"
"time"
"github.com/micro/go-micro/v2/auth" "github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/errors" "github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/metadata" "github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/server" "github.com/micro/go-micro/v2/server"
) )
@ -358,6 +361,98 @@ func TestAuthHandler(t *testing.T) {
} }
if !handlerCalled { if !handlerCalled {
t.Errorf("Expected the handler be called") t.Errorf("Expected the handler be called")
type testClient struct {
callCount int
callRsp interface{}
client.Client
}
func (c *testClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
c.callCount++
if c.callRsp != nil {
val := reflect.ValueOf(rsp).Elem()
val.Set(reflect.ValueOf(c.callRsp).Elem())
}
return nil
}
type testRsp struct {
value string
}
func TestCacheWrapper(t *testing.T) {
req := client.NewRequest("go.micro.service.foo", "Foo.Bar", nil)
t.Run("NilCache", func(t *testing.T) {
cli := new(testClient)
w := CacheClient(func() *client.Cache {
return nil
}, cli)
// perfroming two requests should increment the call count by two indicating the cache wasn't
// used even though the WithCache option was passed.
w.Call(context.TODO(), req, nil, client.WithCache(time.Minute))
w.Call(context.TODO(), req, nil, client.WithCache(time.Minute))
if cli.callCount != 2 {
t.Errorf("Expected the client to have been called twice")
}
})
t.Run("OptionNotSet", func(t *testing.T) {
cli := new(testClient)
cache := client.NewCache()
w := CacheClient(func() *client.Cache {
return cache
}, cli)
// perfroming two requests should increment the call count by two since we didn't pass the WithCache
// option to Call.
w.Call(context.TODO(), req, nil)
w.Call(context.TODO(), req, nil)
if cli.callCount != 2 {
t.Errorf("Expected the client to have been called twice")
}
})
t.Run("OptionSet", func(t *testing.T) {
val := "foo"
cli := &testClient{callRsp: &testRsp{value: val}}
cache := client.NewCache()
w := CacheClient(func() *client.Cache {
return cache
}, cli)
// perfroming two requests should increment the call count by once since the second request should
// have used the cache. The correct value should be set on both responses and no errors should
// be returned.
rsp1 := &testRsp{}
rsp2 := &testRsp{}
err1 := w.Call(context.TODO(), req, rsp1, client.WithCache(time.Minute))
err2 := w.Call(context.TODO(), req, rsp2, client.WithCache(time.Minute))
if err1 != nil {
t.Errorf("Expected nil error, got %v", err1)
}
if err2 != nil {
t.Errorf("Expected nil error, got %v", err2)
}
if rsp1.value != val {
t.Errorf("Expected %v to be assigned to the value, got %v", val, rsp1.value)
}
if rsp2.value != val {
t.Errorf("Expected %v to be assigned to the value, got %v", val, rsp2.value)
}
if cli.callCount != 1 {
t.Errorf("Expected the client to be called 1 time, was actually called %v time(s)", cli.callCount)
} }
}) })
} }