diff --git a/.gitignore b/.gitignore index c523002..0c52f0b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ config.yaml servicechecker *.protoset authn.yaml +.idea \ No newline at end of file diff --git a/cmd/servicechecker/main.go b/cmd/servicechecker/main.go index 0928ac9..fc40ce5 100644 --- a/cmd/servicechecker/main.go +++ b/cmd/servicechecker/main.go @@ -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 diff --git a/go.mod b/go.mod index e0d33ff..d284214 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 1e96d97..34360c3 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/pkg/protoset/protoset.go b/pkg/protoset/protoset.go new file mode 100644 index 0000000..f87c778 --- /dev/null +++ b/pkg/protoset/protoset.go @@ -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 +} diff --git a/pkg/protoset/protoset_test.go b/pkg/protoset/protoset_test.go new file mode 100644 index 0000000..f2b558b --- /dev/null +++ b/pkg/protoset/protoset_test.go @@ -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) +}