From 0e0be64cba20673a80c93dd5822190591050d3ec Mon Sep 17 00:00:00 2001 From: longxboy Date: Mon, 10 May 2021 10:44:39 +0800 Subject: [PATCH] add api metadata (#901) * add api metadata --- api/kratos/api/metadata.pb.go | 305 +++++++++++++++++++++++++++++ api/kratos/api/metadata.proto | 43 ++++ api/kratos/api/metadata_grpc.pb.go | 146 ++++++++++++++ api/kratos/api/metadata_http.pb.go | 90 +++++++++ internal/api/metadata/server.go | 36 ++++ internal/api/metadata/service.go | 182 +++++++++++++++++ transport/grpc/server.go | 6 + 7 files changed, 808 insertions(+) create mode 100644 api/kratos/api/metadata.pb.go create mode 100644 api/kratos/api/metadata.proto create mode 100644 api/kratos/api/metadata_grpc.pb.go create mode 100644 api/kratos/api/metadata_http.pb.go create mode 100644 internal/api/metadata/server.go create mode 100644 internal/api/metadata/service.go diff --git a/api/kratos/api/metadata.pb.go b/api/kratos/api/metadata.pb.go new file mode 100644 index 000000000..fa205fac2 --- /dev/null +++ b/api/kratos/api/metadata.pb.go @@ -0,0 +1,305 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.15.8 +// source: metadata.proto + +package api + +import ( + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + descriptorpb "google.golang.org/protobuf/types/descriptorpb" + anypb "google.golang.org/protobuf/types/known/anypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ListServicesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Services []string `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` +} + +func (x *ListServicesReply) Reset() { + *x = ListServicesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_metadata_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListServicesReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListServicesReply) ProtoMessage() {} + +func (x *ListServicesReply) ProtoReflect() protoreflect.Message { + mi := &file_metadata_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListServicesReply.ProtoReflect.Descriptor instead. +func (*ListServicesReply) Descriptor() ([]byte, []int) { + return file_metadata_proto_rawDescGZIP(), []int{0} +} + +func (x *ListServicesReply) GetServices() []string { + if x != nil { + return x.Services + } + return nil +} + +type GetServiceMetaRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *GetServiceMetaRequest) Reset() { + *x = GetServiceMetaRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_metadata_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetServiceMetaRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetServiceMetaRequest) ProtoMessage() {} + +func (x *GetServiceMetaRequest) ProtoReflect() protoreflect.Message { + mi := &file_metadata_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetServiceMetaRequest.ProtoReflect.Descriptor instead. +func (*GetServiceMetaRequest) Descriptor() ([]byte, []int) { + return file_metadata_proto_rawDescGZIP(), []int{1} +} + +func (x *GetServiceMetaRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GetServiceMetaReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProtoSet *descriptorpb.FileDescriptorSet `protobuf:"bytes,1,opt,name=protoSet,proto3" json:"protoSet,omitempty"` +} + +func (x *GetServiceMetaReply) Reset() { + *x = GetServiceMetaReply{} + if protoimpl.UnsafeEnabled { + mi := &file_metadata_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetServiceMetaReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetServiceMetaReply) ProtoMessage() {} + +func (x *GetServiceMetaReply) ProtoReflect() protoreflect.Message { + mi := &file_metadata_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetServiceMetaReply.ProtoReflect.Descriptor instead. +func (*GetServiceMetaReply) Descriptor() ([]byte, []int) { + return file_metadata_proto_rawDescGZIP(), []int{2} +} + +func (x *GetServiceMetaReply) GetProtoSet() *descriptorpb.FileDescriptorSet { + if x != nil { + return x.ProtoSet + } + return nil +} + +var File_metadata_proto protoreflect.FileDescriptor + +var file_metadata_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x0a, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x19, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2f, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x2b, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x55, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3e, 0x0a, 0x08, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x53, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x53, + 0x65, 0x74, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x53, 0x65, 0x74, 0x32, 0xda, 0x01, 0x0a, + 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x56, 0x0a, 0x0c, 0x4c, 0x69, 0x73, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x1a, + 0x1d, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x11, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x12, 0x09, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x12, 0x76, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, + 0x65, 0x74, 0x61, 0x12, 0x21, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, + 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, + 0x18, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x58, 0x0a, 0x15, 0x63, 0x6f, 0x6d, + 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, + 0x70, 0x69, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, + 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, + 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0xa2, 0x02, 0x09, 0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, + 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_metadata_proto_rawDescOnce sync.Once + file_metadata_proto_rawDescData = file_metadata_proto_rawDesc +) + +func file_metadata_proto_rawDescGZIP() []byte { + file_metadata_proto_rawDescOnce.Do(func() { + file_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_metadata_proto_rawDescData) + }) + return file_metadata_proto_rawDescData +} + +var file_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_metadata_proto_goTypes = []interface{}{ + (*ListServicesReply)(nil), // 0: kratos.api.ListServicesReply + (*GetServiceMetaRequest)(nil), // 1: kratos.api.GetServiceMetaRequest + (*GetServiceMetaReply)(nil), // 2: kratos.api.GetServiceMetaReply + (*descriptorpb.FileDescriptorSet)(nil), // 3: google.protobuf.FileDescriptorSet + (*anypb.Any)(nil), // 4: google.protobuf.Any +} +var file_metadata_proto_depIdxs = []int32{ + 3, // 0: kratos.api.GetServiceMetaReply.protoSet:type_name -> google.protobuf.FileDescriptorSet + 4, // 1: kratos.api.Metadata.ListServices:input_type -> google.protobuf.Any + 1, // 2: kratos.api.Metadata.GetServiceMeta:input_type -> kratos.api.GetServiceMetaRequest + 0, // 3: kratos.api.Metadata.ListServices:output_type -> kratos.api.ListServicesReply + 2, // 4: kratos.api.Metadata.GetServiceMeta:output_type -> kratos.api.GetServiceMetaReply + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_metadata_proto_init() } +func file_metadata_proto_init() { + if File_metadata_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListServicesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetServiceMetaRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_metadata_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetServiceMetaReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_metadata_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_metadata_proto_goTypes, + DependencyIndexes: file_metadata_proto_depIdxs, + MessageInfos: file_metadata_proto_msgTypes, + }.Build() + File_metadata_proto = out.File + file_metadata_proto_rawDesc = nil + file_metadata_proto_goTypes = nil + file_metadata_proto_depIdxs = nil +} diff --git a/api/kratos/api/metadata.proto b/api/kratos/api/metadata.proto new file mode 100644 index 000000000..4ed46b1d3 --- /dev/null +++ b/api/kratos/api/metadata.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +package kratos.api; + +import "google/protobuf/any.proto"; +import "google/protobuf/descriptor.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/go-kratos/kratos/v2/api/kratos/api;api"; +option java_multiple_files = true; +option java_package = "com.github.kratos.api"; +option objc_class_prefix = "KratosAPI"; + + +// Metadata is api defintion metadata service +service Metadata { + // ListServices list the full name of all services + rpc ListServices (google.protobuf.Any) returns (ListServicesReply) { + option (google.api.http) = { + get: "/services", + }; + } + + // GetServiceMeta get the full fileDescriptorSet of service + rpc GetServiceMeta (GetServiceMetaRequest) returns (GetServiceMetaReply) { + option (google.api.http) = { + get: "/service/{name}/metadata", + }; + } +} + +message ListServicesReply { + repeated string services = 1; +} + +message GetServiceMetaRequest { + string name = 1; +} + +message GetServiceMetaReply { + google.protobuf.FileDescriptorSet protoSet = 1; +} + diff --git a/api/kratos/api/metadata_grpc.pb.go b/api/kratos/api/metadata_grpc.pb.go new file mode 100644 index 000000000..6dc0aac54 --- /dev/null +++ b/api/kratos/api/metadata_grpc.pb.go @@ -0,0 +1,146 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.1.0 +// - protoc v3.15.8 +// source: metadata.proto + +package api + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + anypb "google.golang.org/protobuf/types/known/anypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// MetadataClient is the client API for Metadata service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type MetadataClient interface { + // ListServices list the full name of all services + ListServices(ctx context.Context, in *anypb.Any, opts ...grpc.CallOption) (*ListServicesReply, error) + // GetServiceMeta get the full fileDescriptorSet of service + GetServiceMeta(ctx context.Context, in *GetServiceMetaRequest, opts ...grpc.CallOption) (*GetServiceMetaReply, error) +} + +type metadataClient struct { + cc grpc.ClientConnInterface +} + +func NewMetadataClient(cc grpc.ClientConnInterface) MetadataClient { + return &metadataClient{cc} +} + +func (c *metadataClient) ListServices(ctx context.Context, in *anypb.Any, opts ...grpc.CallOption) (*ListServicesReply, error) { + out := new(ListServicesReply) + err := c.cc.Invoke(ctx, "/kratos.api.Metadata/ListServices", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *metadataClient) GetServiceMeta(ctx context.Context, in *GetServiceMetaRequest, opts ...grpc.CallOption) (*GetServiceMetaReply, error) { + out := new(GetServiceMetaReply) + err := c.cc.Invoke(ctx, "/kratos.api.Metadata/GetServiceMeta", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MetadataServer is the server API for Metadata service. +// All implementations must embed UnimplementedMetadataServer +// for forward compatibility +type MetadataServer interface { + // ListServices list the full name of all services + ListServices(context.Context, *anypb.Any) (*ListServicesReply, error) + // GetServiceMeta get the full fileDescriptorSet of service + GetServiceMeta(context.Context, *GetServiceMetaRequest) (*GetServiceMetaReply, error) + mustEmbedUnimplementedMetadataServer() +} + +// UnimplementedMetadataServer must be embedded to have forward compatible implementations. +type UnimplementedMetadataServer struct { +} + +func (UnimplementedMetadataServer) ListServices(context.Context, *anypb.Any) (*ListServicesReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListServices not implemented") +} +func (UnimplementedMetadataServer) GetServiceMeta(context.Context, *GetServiceMetaRequest) (*GetServiceMetaReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetServiceMeta not implemented") +} +func (UnimplementedMetadataServer) mustEmbedUnimplementedMetadataServer() {} + +// UnsafeMetadataServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to MetadataServer will +// result in compilation errors. +type UnsafeMetadataServer interface { + mustEmbedUnimplementedMetadataServer() +} + +func RegisterMetadataServer(s grpc.ServiceRegistrar, srv MetadataServer) { + s.RegisterService(&Metadata_ServiceDesc, srv) +} + +func _Metadata_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(anypb.Any) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MetadataServer).ListServices(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/kratos.api.Metadata/ListServices", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MetadataServer).ListServices(ctx, req.(*anypb.Any)) + } + return interceptor(ctx, in, info, handler) +} + +func _Metadata_GetServiceMeta_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetServiceMetaRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MetadataServer).GetServiceMeta(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/kratos.api.Metadata/GetServiceMeta", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MetadataServer).GetServiceMeta(ctx, req.(*GetServiceMetaRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Metadata_ServiceDesc is the grpc.ServiceDesc for Metadata service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Metadata_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "kratos.api.Metadata", + HandlerType: (*MetadataServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListServices", + Handler: _Metadata_ListServices_Handler, + }, + { + MethodName: "GetServiceMeta", + Handler: _Metadata_GetServiceMeta_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "metadata.proto", +} diff --git a/api/kratos/api/metadata_http.pb.go b/api/kratos/api/metadata_http.pb.go new file mode 100644 index 000000000..96473ec63 --- /dev/null +++ b/api/kratos/api/metadata_http.pb.go @@ -0,0 +1,90 @@ +// Code generated by protoc-gen-go-http. DO NOT EDIT. + +package api + +import ( + context "context" + http1 "github.com/go-kratos/kratos/v2/transport/http" + binding "github.com/go-kratos/kratos/v2/transport/http/binding" + mux "github.com/gorilla/mux" + anypb "google.golang.org/protobuf/types/known/anypb" + http "net/http" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the kratos package it is being compiled against. +var _ = new(http.Request) +var _ = new(context.Context) +var _ = binding.MapProto +var _ = mux.NewRouter + +const _ = http1.SupportPackageIsVersion1 + +type MetadataHandler interface { + GetServiceMeta(context.Context, *GetServiceMetaRequest) (*GetServiceMetaReply, error) + + ListServices(context.Context, *anypb.Any) (*ListServicesReply, error) +} + +func NewMetadataHandler(srv MetadataHandler, opts ...http1.HandleOption) http.Handler { + h := http1.DefaultHandleOptions() + for _, o := range opts { + o(&h) + } + r := mux.NewRouter() + + r.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) { + var in anypb.Any + if err := h.Decode(r, &in); err != nil { + h.Error(w, r, err) + return + } + + next := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.ListServices(ctx, req.(*anypb.Any)) + } + if h.Middleware != nil { + next = h.Middleware(next) + } + out, err := next(r.Context(), &in) + if err != nil { + h.Error(w, r, err) + return + } + reply := out.(*ListServicesReply) + if err := h.Encode(w, r, reply); err != nil { + h.Error(w, r, err) + } + }).Methods("GET") + + r.HandleFunc("/service/{name}/metadata", func(w http.ResponseWriter, r *http.Request) { + var in GetServiceMetaRequest + if err := h.Decode(r, &in); err != nil { + h.Error(w, r, err) + return + } + + if err := binding.MapProto(&in, mux.Vars(r)); err != nil { + h.Error(w, r, err) + return + } + + next := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.GetServiceMeta(ctx, req.(*GetServiceMetaRequest)) + } + if h.Middleware != nil { + next = h.Middleware(next) + } + out, err := next(r.Context(), &in) + if err != nil { + h.Error(w, r, err) + return + } + reply := out.(*GetServiceMetaReply) + if err := h.Encode(w, r, reply); err != nil { + h.Error(w, r, err) + } + }).Methods("GET") + + return r +} diff --git a/internal/api/metadata/server.go b/internal/api/metadata/server.go new file mode 100644 index 000000000..9790a6bf2 --- /dev/null +++ b/internal/api/metadata/server.go @@ -0,0 +1,36 @@ +package metadata + +import ( + "context" + + "github.com/go-kratos/kratos/v2/api/kratos/api" + "google.golang.org/grpc" + anypb "google.golang.org/protobuf/types/known/anypb" +) + +// Server is api meta server +type Server struct { + api.UnimplementedMetadataServer + s *Service +} + +// NewServer create server instance +func NewServer(grpcSrv ...*grpc.Server) *Server { + return &Server{s: NewService(grpcSrv...)} +} + +// ListServices return all services +func (s *Server) ListServices(ctx context.Context, in *anypb.Any) (*api.ListServicesReply, error) { + var reply api.ListServicesReply + var err error + reply.Services, err = s.s.ListServices(ctx) + return &reply, err +} + +// GetServiceMeta return service meta by name +func (s *Server) GetServiceMeta(ctx context.Context, in *api.GetServiceMetaRequest) (*api.GetServiceMetaReply, error) { + var reply api.GetServiceMetaReply + var err error + reply.ProtoSet, err = s.s.GetServiceMeta(ctx, in.Name) + return &reply, err +} diff --git a/internal/api/metadata/service.go b/internal/api/metadata/service.go new file mode 100644 index 000000000..bea77d1ce --- /dev/null +++ b/internal/api/metadata/service.go @@ -0,0 +1,182 @@ +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 +} diff --git a/transport/grpc/server.go b/transport/grpc/server.go index a524bc4c0..b74aa8212 100644 --- a/transport/grpc/server.go +++ b/transport/grpc/server.go @@ -6,6 +6,8 @@ import ( "net" "time" + "github.com/go-kratos/kratos/v2/api/kratos/api" + "github.com/go-kratos/kratos/v2/internal/api/metadata" "github.com/go-kratos/kratos/v2/internal/host" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/middleware" @@ -77,6 +79,7 @@ type Server struct { middleware middleware.Middleware grpcOpts []grpc.ServerOption health *health.Server + metaServer *metadata.Server } // NewServer creates a gRPC server by options. @@ -103,8 +106,11 @@ func NewServer(opts ...ServerOption) *Server { grpcOpts = append(grpcOpts, srv.grpcOpts...) } srv.Server = grpc.NewServer(grpcOpts...) + srv.metaServer = metadata.NewServer(srv.Server) // grpc health register healthpb.RegisterHealthServer(srv.Server, srv.health) + // api metadata register + api.RegisterMetadataServer(srv.Server, srv.metaServer) // reflection register reflection.Register(srv.Server) return srv