forked from milvus-io/milvus
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement the
hook
interceptor (milvus-io#19294)
Signed-off-by: SimFG <[email protected]> Signed-off-by: SimFG <[email protected]>
- Loading branch information
Showing
9 changed files
with
293 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package hook | ||
|
||
type Hook interface { | ||
Init(params map[string]string) error | ||
Mock(req interface{}, fullMethod string) (bool, interface{}, error) | ||
Before(req interface{}, fullMethod string) error | ||
After(result interface{}, err error, fullMethod string) error | ||
Release() | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package proxy | ||
|
||
import ( | ||
"context" | ||
"plugin" | ||
|
||
"github.com/milvus-io/milvus/api/hook" | ||
|
||
"go.uber.org/zap" | ||
|
||
"google.golang.org/grpc" | ||
) | ||
|
||
type defaultHook struct { | ||
} | ||
|
||
func (d defaultHook) Init(params map[string]string) error { | ||
return nil | ||
} | ||
|
||
func (d defaultHook) Mock(req interface{}, fullMethod string) (bool, interface{}, error) { | ||
return false, nil, nil | ||
} | ||
|
||
func (d defaultHook) Before(req interface{}, fullMethod string) error { | ||
return nil | ||
} | ||
|
||
func (d defaultHook) After(result interface{}, err error, fullMethod string) error { | ||
return nil | ||
} | ||
|
||
func (d defaultHook) Release() {} | ||
|
||
var hoo hook.Hook | ||
|
||
func initHook() { | ||
path := Params.ProxyCfg.SoPath | ||
if path == "" { | ||
hoo = defaultHook{} | ||
return | ||
} | ||
|
||
logger.Debug("start to load plugin", zap.String("path", path)) | ||
p, err := plugin.Open(path) | ||
if err != nil { | ||
exit("fail to open the plugin", err) | ||
} | ||
logger.Debug("plugin open") | ||
|
||
h, err := p.Lookup("MilvusHook") | ||
if err != nil { | ||
exit("fail to the 'MilvusHook' object in the plugin", err) | ||
} | ||
|
||
var ok bool | ||
hoo, ok = h.(hook.Hook) | ||
if !ok { | ||
exit("fail to convert the `Hook` interface", nil) | ||
} | ||
if err = hoo.Init(Params.HookCfg.SoConfig); err != nil { | ||
exit("fail to init configs for the hoo", err) | ||
} | ||
} | ||
|
||
func exit(errMsg string, err error) { | ||
logger.Panic("hoo error", zap.String("path", Params.ProxyCfg.SoPath), zap.String("msg", errMsg), zap.Error(err)) | ||
} | ||
|
||
func UnaryServerHookInterceptor() grpc.UnaryServerInterceptor { | ||
initHook() | ||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { | ||
var ( | ||
fullMethod = info.FullMethod | ||
isMock bool | ||
mockResp interface{} | ||
realResp interface{} | ||
realErr error | ||
err error | ||
) | ||
|
||
if isMock, mockResp, err = hoo.Mock(req, fullMethod); isMock { | ||
return mockResp, err | ||
} | ||
|
||
if err = hoo.Before(req, fullMethod); err != nil { | ||
return nil, err | ||
} | ||
realResp, realErr = handler(ctx, req) | ||
if err = hoo.After(realResp, realErr, fullMethod); err != nil { | ||
return nil, err | ||
} | ||
return realResp, realErr | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package proxy | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"testing" | ||
|
||
"google.golang.org/grpc" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestInitHook(t *testing.T) { | ||
Params.ProxyCfg.SoPath = "" | ||
initHook() | ||
assert.IsType(t, defaultHook{}, hoo) | ||
|
||
Params.ProxyCfg.SoPath = "/a/b/hook.so" | ||
assert.Panics(t, func() { | ||
initHook() | ||
}) | ||
Params.ProxyCfg.SoPath = "" | ||
} | ||
|
||
type mockHook struct { | ||
defaultHook | ||
mockRes interface{} | ||
mockErr error | ||
} | ||
|
||
func (m mockHook) Mock(req interface{}, fullMethod string) (bool, interface{}, error) { | ||
return true, m.mockRes, m.mockErr | ||
} | ||
|
||
type req struct { | ||
method string | ||
} | ||
|
||
type beforeMock struct { | ||
defaultHook | ||
method string | ||
err error | ||
} | ||
|
||
func (b beforeMock) Before(r interface{}, fullMethod string) error { | ||
re, ok := r.(*req) | ||
if !ok { | ||
return errors.New("r is invalid type") | ||
} | ||
re.method = b.method | ||
return b.err | ||
} | ||
|
||
type resp struct { | ||
method string | ||
} | ||
|
||
type afterMock struct { | ||
defaultHook | ||
method string | ||
err error | ||
} | ||
|
||
func (a afterMock) After(r interface{}, err error, fullMethod string) error { | ||
re, ok := r.(*resp) | ||
if !ok { | ||
return errors.New("r is invalid type") | ||
} | ||
re.method = a.method | ||
return a.err | ||
} | ||
|
||
func TestHookInterceptor(t *testing.T) { | ||
var ( | ||
ctx = context.Background() | ||
info = &grpc.UnaryServerInfo{ | ||
FullMethod: "test", | ||
} | ||
interceptor = UnaryServerHookInterceptor() | ||
mockHoo = mockHook{mockRes: "mock", mockErr: errors.New("mock")} | ||
r = &req{method: "req"} | ||
re = &resp{method: "resp"} | ||
beforeHoo = beforeMock{method: "before", err: errors.New("before")} | ||
afterHoo = afterMock{method: "after", err: errors.New("after")} | ||
|
||
res interface{} | ||
err error | ||
) | ||
|
||
hoo = mockHoo | ||
res, err = interceptor(ctx, "request", info, func(ctx context.Context, req interface{}) (interface{}, error) { | ||
return nil, nil | ||
}) | ||
assert.Equal(t, res, mockHoo.mockRes) | ||
assert.Equal(t, err, mockHoo.mockErr) | ||
|
||
hoo = beforeHoo | ||
_, err = interceptor(ctx, r, info, func(ctx context.Context, req interface{}) (interface{}, error) { | ||
return nil, nil | ||
}) | ||
assert.Equal(t, r.method, beforeHoo.method) | ||
assert.Equal(t, err, beforeHoo.err) | ||
|
||
hoo = afterHoo | ||
_, err = interceptor(ctx, r, info, func(ctx context.Context, r interface{}) (interface{}, error) { | ||
return re, nil | ||
}) | ||
assert.Equal(t, re.method, afterHoo.method) | ||
assert.Equal(t, err, afterHoo.err) | ||
|
||
hoo = defaultHook{} | ||
res, err = interceptor(ctx, r, info, func(ctx context.Context, r interface{}) (interface{}, error) { | ||
return &resp{ | ||
method: r.(*req).method, | ||
}, nil | ||
}) | ||
assert.Equal(t, res.(*resp).method, r.method) | ||
assert.NoError(t, err) | ||
} | ||
|
||
func TestDefaultHook(t *testing.T) { | ||
d := defaultHook{} | ||
assert.NoError(t, d.Init(nil)) | ||
assert.NotPanics(t, func() { | ||
d.Release() | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.