You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
kratos/internal/api/metadata/service.go

182 lines
4.5 KiB

package metadata
import (
"bytes"
"compress/gzip"
"context"
"fmt"
"io/ioutil"
"sync"
"github.com/golang/protobuf/proto"
dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
"google.golang.org/grpc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/descriptorpb"
)
// Service is description service
type Service struct {
grpcSer *grpc.Server
once sync.Once
services map[string]*descriptorpb.FileDescriptorSet
}
// NewService create desc Service
func NewService(grpcSrv ...*grpc.Server) *Service {
s := &Service{}
if len(grpcSrv) > 0 {
s.grpcSer = grpcSrv[0]
}
return s
}
// ListServices list the full name of all services
func (s *Service) ListServices(ctx context.Context) (services []string, err error) {
services = []string{}
s.once.Do(func() {
err = s.initServices()
})
for name := range s.services {
services = append(services, name)
}
return
}
// GetServiceMeta get the full fileDescriptorSet of service
func (s *Service) GetServiceMeta(ctx context.Context, name string) (fds *descriptorpb.FileDescriptorSet, err error) {
fds = &descriptorpb.FileDescriptorSet{}
s.once.Do(func() {
err = s.initServices()
})
if temp, ok := s.services[name]; ok {
fds = temp
}
return
}
func (s *Service) initServices() error {
serviceProto, err := s.listServices()
if err != nil {
s.services = make(map[string]*descriptorpb.FileDescriptorSet)
return err
}
s.services = serviceProto
return nil
}
func (s *Service) listServices() (map[string]*descriptorpb.FileDescriptorSet, error) {
services := make(map[string]*descriptorpb.FileDescriptorSet, 0)
if s.grpcSer != nil {
for svc, info := range s.grpcSer.GetServiceInfo() {
fdenc, ok := parseMetadata(info.Metadata)
if !ok {
return nil, fmt.Errorf("invalid service %s meta", svc)
}
fd, err := decodeFileDesc(fdenc)
if err != nil {
return nil, err
}
protoSet, err := allDependency(fd)
if err != nil {
return nil, err
}
services[svc] = &dpb.FileDescriptorSet{File: protoSet}
}
return services, nil
}
var err error
protoregistry.GlobalFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
if fd.Services() != nil && fd.Services().Len() > 0 {
for i := 0; i < fd.Services().Len(); i++ {
svc := string(fd.Services().Get(i).FullName())
fdp, e := fileDescriptorProto(fd.Path())
if e != nil {
err = e
return false
}
fdps, e := allDependency(fdp)
if e != nil {
err = e
return false
}
services[svc] = &dpb.FileDescriptorSet{File: fdps}
}
}
return true
})
return services, err
}
func fileDescriptorProto(path string) (*dpb.FileDescriptorProto, error) {
fdenc := proto.FileDescriptor(path)
fdDep, err := decodeFileDesc(fdenc)
if err != nil {
return nil, err
}
return fdDep, nil
}
func allDependency(fd *dpb.FileDescriptorProto) ([]*dpb.FileDescriptorProto, error) {
var files []*dpb.FileDescriptorProto
files = append(files, fd)
for _, dep := range fd.Dependency {
fdDep, err := fileDescriptorProto(dep)
if err != nil {
return nil, err
}
temp, err := allDependency(fdDep)
if err != nil {
return nil, err
}
files = append(files, temp...)
}
return files, nil
}
// parseMetadata finds the file descriptor bytes specified meta.
// For SupportPackageIsVersion4, m is the name of the proto file, we
// call proto.FileDescriptor to get the byte slice.
// For SupportPackageIsVersion3, m is a byte slice itself.
func parseMetadata(meta interface{}) ([]byte, bool) {
// Check if meta is the file name.
if fileNameForMeta, ok := meta.(string); ok {
return proto.FileDescriptor(fileNameForMeta), true
}
// Check if meta is the byte slice.
if enc, ok := meta.([]byte); ok {
return enc, true
}
return nil, false
}
// decodeFileDesc does decompression and unmarshalling on the given
// file descriptor byte slice.
func decodeFileDesc(enc []byte) (*dpb.FileDescriptorProto, error) {
raw, err := decompress(enc)
if err != nil {
return nil, fmt.Errorf("failed to decompress enc: %v", err)
}
fd := new(dpb.FileDescriptorProto)
if err := proto.Unmarshal(raw, fd); err != nil {
return nil, fmt.Errorf("bad descriptor: %v", err)
}
return fd, nil
}
// decompress does gzip decompression.
func decompress(b []byte) ([]byte, error) {
r, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {
return nil, fmt.Errorf("bad gzipped descriptor: %v", err)
}
out, err := ioutil.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("bad gzipped descriptor: %v", err)
}
return out, nil
}