Co-authored-by: Gorbunov Kirill Andreevich <kgorbunov@mtsbank.ru>
Reviewed-on: #5
Co-authored-by: Кирилл Горбунов <kirya_gorbunov_2015@mail.ru>
Co-committed-by: Кирилл Горбунов <kirya_gorbunov_2015@mail.ru>
This commit is contained in:
Кирилл Горбунов 2024-11-24 12:57:53 +03:00 committed by Василий Толстов
parent c1103c714a
commit ce57938ec2
6 changed files with 147 additions and 42 deletions

1
.gitignore vendored
View File

@ -25,3 +25,4 @@ config.yaml
servicechecker
*.protoset
authn.yaml
.idea

View File

@ -29,11 +29,8 @@ import (
"go.unistack.org/servicechecker/pkg/config"
"go.unistack.org/servicechecker/pkg/grpcconn"
"go.unistack.org/servicechecker/pkg/httpconn"
"go.unistack.org/servicechecker/pkg/protoset"
"go.unistack.org/servicechecker/pkg/scheduler"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/dynamicpb"
)
var (
@ -131,7 +128,7 @@ func main() {
client.Codec("application/grpc+json", jsonpbcodec.NewCodec()),
client.ContentType("application/grpc"),
client.Retries(0),
// client.TLSConfig(&tls.Config{InsecureSkipVerify: true}),
// client.TLSConfig(&tls.Config{InsecureSkipVerify: true}),
)
if err = gcli.Init(); err != nil {
l.Fatal(ctx, "failed to init grpc client", err)
@ -144,7 +141,7 @@ func main() {
client.Codec("application/json", jsonpbcodec.NewCodec()),
client.ContentType("application/json"),
client.Retries(0),
// client.TLSConfig(&tls.Config{InsecureSkipVerify: true}),
// client.TLSConfig(&tls.Config{InsecureSkipVerify: true}),
)
if err = hcli.Init(); err != nil {
l.Fatal(ctx, "failed to init http client", err)
@ -307,51 +304,28 @@ func newGRPCTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri
var rsp interface{}
var treq client.Request
var labels []string
p := protoset.NewProtoSet()
if task.GRPC.Protoset != "" {
pkg, svc, mth := grpcconn.ServiceMethod(task.GRPC.Endpoint)
protosetBuf, err := os.ReadFile(task.GRPC.Protoset)
if err != nil {
l.Error(ctx, "failed to unmarshal protoset file", err)
return nil, nil, err
}
fdset := &descriptorpb.FileDescriptorSet{}
if err = protocodec.NewCodec().Unmarshal(protosetBuf, fdset); err != nil {
l.Error(ctx, "failed to unmarshal protoset file", err)
if err = p.AddProtoset(task.GRPC.Addr, svc, protosetBuf); err != nil {
l.Error(ctx, "failed add protoset", err)
return nil, nil, err
}
pfileoptions := protodesc.FileOptions{AllowUnresolvable: true}
pfiles, err := pfileoptions.NewFiles(fdset)
req, rsp, err = p.GetMessage(task.GRPC.Addr, pkg, svc, mth)
if err != nil {
l.Error(ctx, "failed to use protoset file", err)
l.Error(ctx, "failed get req, rsp from protoset", err)
return nil, nil, err
}
pkg, svc, mth := grpcconn.ServiceMethod(task.GRPC.Endpoint)
pdesc, err := pfiles.FindDescriptorByName(protoreflect.FullName(pkg + "." + svc))
if err != nil {
l.Error(ctx, "failed to find service "+pkg+"."+svc)
return nil, nil, err
}
sdesc, ok := pdesc.(protoreflect.ServiceDescriptor)
if !ok {
err = fmt.Errorf("failed to find service " + pkg + "." + svc)
l.Error(ctx, "unable to find service in protoset", err)
return nil, nil, err
}
mdesc := sdesc.Methods().ByName(protoreflect.Name(mth))
if mdesc == nil {
err = fmt.Errorf("unknown method " + mth)
l.Error(ctx, "failed to find method", err)
return nil, nil, err
}
req = dynamicpb.NewMessageType(mdesc.Input()).New()
rsp = dynamicpb.NewMessageType(mdesc.Output()).New()
if err = jsonpbcodec.NewCodec().Unmarshal([]byte(task.GRPC.Data), req); err != nil {
l.Error(ctx, "failed to unmarshal", err)
return nil, nil, err

5
go.mod
View File

@ -6,13 +6,13 @@ require (
github.com/go-co-op/gocron/v2 v2.12.3
github.com/google/gnostic v0.7.0
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.9.0
go.unistack.org/micro-client-grpc/v3 v3.11.10
go.unistack.org/micro-client-http/v3 v3.9.14
go.unistack.org/micro-codec-json/v3 v3.10.1
go.unistack.org/micro-codec-jsonpb/v3 v3.10.3
go.unistack.org/micro-codec-proto/v3 v3.10.2
go.unistack.org/micro-codec-yaml/v3 v3.10.2
go.unistack.org/micro-config-file/v3 v3.8.10
go.unistack.org/micro-meter-victoriametrics/v3 v3.8.9
go.unistack.org/micro-proto/v3 v3.4.1
go.unistack.org/micro-server-http/v3 v3.11.34
@ -21,9 +21,10 @@ require (
)
require (
dario.cat/mergo v1.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/valyala/fastrand v1.1.0 // indirect
github.com/valyala/histogram v1.2.0 // indirect

4
go.sum
View File

@ -593,8 +593,6 @@ cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vf
cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
@ -910,8 +908,6 @@ go.unistack.org/micro-codec-proto/v3 v3.10.2 h1:9iUQjBjTsd/RgIqB5rAQMZE0CYWngoW9
go.unistack.org/micro-codec-proto/v3 v3.10.2/go.mod h1:54e1jb6aLL9obJUwJjtVupE5zY4PugTcMSqWDhz9aC4=
go.unistack.org/micro-codec-yaml/v3 v3.10.2 h1:02I9XzhaBHqZU8Vd5e2zhf8j4foJ4muPT/x4gdR6E4c=
go.unistack.org/micro-codec-yaml/v3 v3.10.2/go.mod h1:A/tYj7x9CRhuin7WxeIvnuo8bMDrZYcJkogVYN8X7rU=
go.unistack.org/micro-config-file/v3 v3.8.10 h1:/IyD/i6I7Ic8jCNq7ZsTpWT8sToNG14gIFkSVPxbNpY=
go.unistack.org/micro-config-file/v3 v3.8.10/go.mod h1:w7uw5KxK3H2OrZwX4p0hQHbp9UzwDODYqJvdofySgxY=
go.unistack.org/micro-meter-victoriametrics/v3 v3.8.9 h1:ZXCS0eFiSdvcFYxpxV2Q77gfwAjpIRydwAEI1QBrwuQ=
go.unistack.org/micro-meter-victoriametrics/v3 v3.8.9/go.mod h1:xODJQ0Nu/F8k34D/z2ITL91OskI/C674XCkugAxmc3Q=
go.unistack.org/micro-proto/v3 v3.4.1 h1:UTjLSRz2YZuaHk9iSlVqqsA50JQNAEK2ZFboGqtEa9Q=

84
pkg/protoset/protoset.go Normal file
View File

@ -0,0 +1,84 @@
package protoset
import (
"context"
"errors"
"fmt"
"sync"
protocodec "go.unistack.org/micro-codec-proto/v3"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/dynamicpb"
)
var errNotFound = errors.New("file descriptor not found")
type ProtoSet struct {
mu sync.Mutex
files map[string]*protoregistry.Files
}
func NewProtoSet() *ProtoSet {
return &ProtoSet{
mu: sync.Mutex{},
files: make(map[string]*protoregistry.Files, 0),
}
}
func (p *ProtoSet) GetMessage(addr, pkg, svc, mth string) (protoreflect.Message, protoreflect.Message, error) {
if addr == "" || svc == "" || mth == "" || pkg == "" {
return nil, nil, errors.New("addr or service name is empty")
}
p.mu.Lock()
pfile, ok := p.files[addr+"|"+svc]
p.mu.Unlock()
if !ok || pfile == nil {
return nil, nil, errNotFound
}
pdesc, err := pfile.FindDescriptorByName(protoreflect.FullName(pkg + "." + svc))
if err != nil {
return nil, nil, fmt.Errorf("failed to find service %s.%s, err: %w", pkg, svc, err)
}
sdesc, ok := pdesc.(protoreflect.ServiceDescriptor)
if !ok {
return nil, nil, fmt.Errorf("failed to find service " + pkg + "." + svc)
}
mdesc := sdesc.Methods().ByName(protoreflect.Name(mth))
if mdesc == nil {
return nil, nil, fmt.Errorf("unknown method " + mth)
}
req := dynamicpb.NewMessageType(mdesc.Input()).New()
rsp := dynamicpb.NewMessageType(mdesc.Output()).New()
return req, rsp, nil
}
func (p *ProtoSet) AddProtoset(addr, svc string, data []byte) error {
fdset := &descriptorpb.FileDescriptorSet{}
if err := protocodec.NewCodec().Unmarshal(data, fdset); err != nil {
return fmt.Errorf("failed to unmarshal protoset file: %w", err)
}
pfileoptions := protodesc.FileOptions{AllowUnresolvable: true}
pfiles, err := pfileoptions.NewFiles(fdset)
if err != nil {
return fmt.Errorf("failed to use protoset file, err: %w", err)
}
p.mu.Lock()
p.files[addr+"|"+svc] = pfiles
p.mu.Unlock()
return nil
}
func (p *ProtoSet) AddReflection(ctx context.Context, service string, addr string) error {
return nil
}

View File

@ -0,0 +1,49 @@
package protoset
import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestProtoSet_1(t *testing.T) {
p := NewProtoSet()
data, err := os.ReadFile("path to .protoset")
assert.Nil(t, err)
err = p.AddProtoset("localhost:9090", "CardService", data)
assert.Nil(t, err)
req, rsp, err := p.GetMessage("localhost:9090", "card_proto", "CardService", "GetCardList")
assert.Nil(t, err)
assert.NotNil(t, req)
assert.NotNil(t, rsp)
fmt.Printf("req: %v, rsp: %v \n", req, rsp)
}
func TestProtoSet_2_bad(t *testing.T) {
p := NewProtoSet()
data, err := os.ReadFile("path to .protoset")
assert.Nil(t, err)
err = p.AddProtoset("localhost:9090", "CardService", data)
assert.Nil(t, err)
req, rsp, err := p.GetMessage("localhost:9090", "card_proto", "Card", "GetCardList")
assert.Error(t, err)
assert.Nil(t, req)
assert.Nil(t, rsp)
fmt.Printf("req: %v, rsp: %v \n", req, rsp)
}
func TestProtoSet_3_not_found(t *testing.T) {
p := NewProtoSet()
req, rsp, err := p.GetMessage("localhost:9090", "card_proto", "CardService", "GetCardList")
assert.ErrorIs(t, err, errNotFound)
assert.Nil(t, req)
assert.Nil(t, rsp)
fmt.Printf("req: %v, rsp: %v \n", req, rsp)
}