Feature grpc version echo api (#3)

added grpc verion and echo apis

---------

Co-authored-by: Prashant Dwivedi <prashantdwivedi194@gmail.com>
This commit is contained in:
Jay Kaku
2023-11-20 16:46:42 +05:30
committed by JayKaku
parent c305843105
commit 22097353d2
17 changed files with 1193 additions and 0 deletions

View File

@@ -138,6 +138,8 @@ func main() {
var grpcServer *go_grpc.Server
if grpcCfg.Port > 0 {
grpcSrv, _ := grpc.NewServer(&grpcCfg, logger)
//grpcinfoSrv, _ := grpc.NewInfoServer(&grpcCfg)
grpcServer = grpcSrv.ListenAndServe()
}

17
pkg/api/grpc/echo.go Normal file
View File

@@ -0,0 +1,17 @@
package grpc
import (
"context"
"log"
"github.com/stefanprodan/podinfo/pkg/api/grpc/echo"
)
type echoServer struct {
echo.UnimplementedEchoServiceServer
}
func (s *echoServer) Echo (ctx context.Context, message *echo.Message) (*echo.Message, error){
log.Printf("Received message body from client: %s", message.Body)
return &echo.Message {Body: message.Body}, nil
}

View File

@@ -0,0 +1,146 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v4.24.3
// source: echo/echo.proto
package echo
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
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 Message struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Body string `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
}
func (x *Message) Reset() {
*x = Message{}
if protoimpl.UnsafeEnabled {
mi := &file_echo_echo_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Message) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Message) ProtoMessage() {}
func (x *Message) ProtoReflect() protoreflect.Message {
mi := &file_echo_echo_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 Message.ProtoReflect.Descriptor instead.
func (*Message) Descriptor() ([]byte, []int) {
return file_echo_echo_proto_rawDescGZIP(), []int{0}
}
func (x *Message) GetBody() string {
if x != nil {
return x.Body
}
return ""
}
var File_echo_echo_proto protoreflect.FileDescriptor
var file_echo_echo_proto_rawDesc = []byte{
0x0a, 0x0f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x04, 0x65, 0x63, 0x68, 0x6f, 0x22, 0x1d, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x32, 0x35, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x0d, 0x2e,
0x65, 0x63, 0x68, 0x6f, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0d, 0x2e, 0x65,
0x63, 0x68, 0x6f, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a,
0x06, 0x2e, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_echo_echo_proto_rawDescOnce sync.Once
file_echo_echo_proto_rawDescData = file_echo_echo_proto_rawDesc
)
func file_echo_echo_proto_rawDescGZIP() []byte {
file_echo_echo_proto_rawDescOnce.Do(func() {
file_echo_echo_proto_rawDescData = protoimpl.X.CompressGZIP(file_echo_echo_proto_rawDescData)
})
return file_echo_echo_proto_rawDescData
}
var file_echo_echo_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_echo_echo_proto_goTypes = []interface{}{
(*Message)(nil), // 0: echo.Message
}
var file_echo_echo_proto_depIdxs = []int32{
0, // 0: echo.EchoService.Echo:input_type -> echo.Message
0, // 1: echo.EchoService.Echo:output_type -> echo.Message
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_echo_echo_proto_init() }
func file_echo_echo_proto_init() {
if File_echo_echo_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_echo_echo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Message); 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_echo_echo_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_echo_echo_proto_goTypes,
DependencyIndexes: file_echo_echo_proto_depIdxs,
MessageInfos: file_echo_echo_proto_msgTypes,
}.Build()
File_echo_echo_proto = out.File
file_echo_echo_proto_rawDesc = nil
file_echo_echo_proto_goTypes = nil
file_echo_echo_proto_depIdxs = nil
}

View File

@@ -0,0 +1,14 @@
syntax = "proto3";
option go_package = "./echo";
package echo;
message Message {
string body = 1;
}
service EchoService {
rpc Echo(Message) returns (Message) {}
}

View File

@@ -0,0 +1,109 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v4.24.3
// source: echo/echo.proto
package echo
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// 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
const (
EchoService_Echo_FullMethodName = "/echo.EchoService/Echo"
)
// EchoServiceClient is the client API for EchoService 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 EchoServiceClient interface {
Echo(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error)
}
type echoServiceClient struct {
cc grpc.ClientConnInterface
}
func NewEchoServiceClient(cc grpc.ClientConnInterface) EchoServiceClient {
return &echoServiceClient{cc}
}
func (c *echoServiceClient) Echo(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
out := new(Message)
err := c.cc.Invoke(ctx, EchoService_Echo_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// EchoServiceServer is the server API for EchoService service.
// All implementations must embed UnimplementedEchoServiceServer
// for forward compatibility
type EchoServiceServer interface {
Echo(context.Context, *Message) (*Message, error)
mustEmbedUnimplementedEchoServiceServer()
}
// UnimplementedEchoServiceServer must be embedded to have forward compatible implementations.
type UnimplementedEchoServiceServer struct {
}
func (UnimplementedEchoServiceServer) Echo(context.Context, *Message) (*Message, error) {
return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented")
}
func (UnimplementedEchoServiceServer) mustEmbedUnimplementedEchoServiceServer() {}
// UnsafeEchoServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to EchoServiceServer will
// result in compilation errors.
type UnsafeEchoServiceServer interface {
mustEmbedUnimplementedEchoServiceServer()
}
func RegisterEchoServiceServer(s grpc.ServiceRegistrar, srv EchoServiceServer) {
s.RegisterService(&EchoService_ServiceDesc, srv)
}
func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Message)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EchoServiceServer).Echo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: EchoService_Echo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).Echo(ctx, req.(*Message))
}
return interceptor(ctx, in, info, handler)
}
// EchoService_ServiceDesc is the grpc.ServiceDesc for EchoService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var EchoService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "echo.EchoService",
HandlerType: (*EchoServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Echo",
Handler: _EchoService_Echo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "echo/echo.proto",
}

69
pkg/api/grpc/echo_test.go Normal file
View File

@@ -0,0 +1,69 @@
package grpc
import (
"context"
"log"
"net"
"regexp"
"testing"
"github.com/stefanprodan/podinfo/pkg/api/grpc/echo"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
"google.golang.org/grpc/test/bufconn"
)
func TestGrpcEcho(t *testing.T) {
// Server initialization
// bufconn => uses in-memory connection instead of system network I/O
lis := bufconn.Listen(1024*1024)
t.Cleanup(func() {
lis.Close()
})
srv := grpc.NewServer()
t.Cleanup(func() {
srv.Stop()
})
echo.RegisterEchoServiceServer(srv, &echoServer{})
go func(){
if err := srv.Serve(lis); err != nil {
log.Fatalf("srv.Serve %v", err)
}
}()
// - Test
dialer := func(context.Context, string) (net.Conn, error){
return lis.Dial()
}
ctx := context.Background()
conn, err := grpc.DialContext(ctx, "", grpc.WithContextDialer(dialer), grpc.WithInsecure())
t.Cleanup(func() {
conn.Close()
})
if err != nil {
t.Fatalf("grpc.DialContext %v", err)
}
client := echo.NewEchoServiceClient(conn)
res , err := client.Echo(context.Background(), &echo.Message{Body:"test123-test"})
// Check the status code is what we expect.
if _, ok := status.FromError(err); !ok {
t.Errorf("Echo returned type %T, want %T", err, status.Error)
}
// Check the response body is what we expect.
expected := ".*body.*test123-test.*"
r := regexp.MustCompile(expected)
if !r.MatchString(res.String()) {
t.Fatalf("Returned unexpected body:\ngot \n%v \nwant \n%s",
res, expected)
}
}

31
pkg/api/grpc/mock_grpc.go Normal file
View File

@@ -0,0 +1,31 @@
package grpc
import (
"go.uber.org/zap"
)
func NewMockGrpcServer() *Server {
config := &Config{
Port: 9999,
// ServerShutdownTimeout: 5 * time.Second,
// HttpServerTimeout: 30 * time.Second,
BackendURL: []string{},
ConfigPath: "/config",
DataPath: "/data",
// HttpClientTimeout: 30 * time.Second,
UIColor: "blue",
UIPath: ".ui",
UIMessage: "Greetings",
Hostname: "localhost",
}
logger, _ := zap.NewDevelopment()
return &Server{
//router: mux.NewRouter(),
logger: logger,
config: config,
//tracer: trace.NewNoopTracerProvider().Tracer("mock"),
}
}

24
pkg/api/grpc/panic.go Normal file
View File

@@ -0,0 +1,24 @@
package grpc
import (
"context"
// "log"
"os"
pb "github.com/stefanprodan/podinfo/pkg/api/grpc/panic"
"go.uber.org/zap"
)
type PanicServer struct {
pb.UnimplementedPanicServiceServer
config *Config
logger *zap.Logger
}
func (s *PanicServer) Panic(ctx context.Context, req *pb.PanicRequest) (*pb.PanicResponse, error) {
s.logger.Info("Panic command received")
os.Exit(225)
return &pb.PanicResponse{}, nil
}

View File

@@ -0,0 +1,189 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.6.1
// source: panic.proto
package panic
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
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 PanicRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *PanicRequest) Reset() {
*x = PanicRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_panic_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PanicRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PanicRequest) ProtoMessage() {}
func (x *PanicRequest) ProtoReflect() protoreflect.Message {
mi := &file_panic_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 PanicRequest.ProtoReflect.Descriptor instead.
func (*PanicRequest) Descriptor() ([]byte, []int) {
return file_panic_proto_rawDescGZIP(), []int{0}
}
type PanicResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *PanicResponse) Reset() {
*x = PanicResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_panic_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PanicResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PanicResponse) ProtoMessage() {}
func (x *PanicResponse) ProtoReflect() protoreflect.Message {
mi := &file_panic_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 PanicResponse.ProtoReflect.Descriptor instead.
func (*PanicResponse) Descriptor() ([]byte, []int) {
return file_panic_proto_rawDescGZIP(), []int{1}
}
var File_panic_proto protoreflect.FileDescriptor
var file_panic_proto_rawDesc = []byte{
0x0a, 0x0b, 0x70, 0x61, 0x6e, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70,
0x61, 0x6e, 0x69, 0x63, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x44, 0x0a, 0x0c, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x12, 0x13,
0x2e, 0x70, 0x61, 0x6e, 0x69, 0x63, 0x2e, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x61, 0x6e, 0x69, 0x63, 0x2e, 0x50, 0x61, 0x6e, 0x69,
0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x2e,
0x2f, 0x70, 0x61, 0x6e, 0x69, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_panic_proto_rawDescOnce sync.Once
file_panic_proto_rawDescData = file_panic_proto_rawDesc
)
func file_panic_proto_rawDescGZIP() []byte {
file_panic_proto_rawDescOnce.Do(func() {
file_panic_proto_rawDescData = protoimpl.X.CompressGZIP(file_panic_proto_rawDescData)
})
return file_panic_proto_rawDescData
}
var file_panic_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_panic_proto_goTypes = []interface{}{
(*PanicRequest)(nil), // 0: panic.PanicRequest
(*PanicResponse)(nil), // 1: panic.PanicResponse
}
var file_panic_proto_depIdxs = []int32{
0, // 0: panic.PanicService.Panic:input_type -> panic.PanicRequest
1, // 1: panic.PanicService.Panic:output_type -> panic.PanicResponse
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_panic_proto_init() }
func file_panic_proto_init() {
if File_panic_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_panic_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PanicRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_panic_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PanicResponse); 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_panic_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_panic_proto_goTypes,
DependencyIndexes: file_panic_proto_depIdxs,
MessageInfos: file_panic_proto_msgTypes,
}.Build()
File_panic_proto = out.File
file_panic_proto_rawDesc = nil
file_panic_proto_goTypes = nil
file_panic_proto_depIdxs = nil
}

View File

@@ -0,0 +1,17 @@
syntax = "proto3";
option go_package = "./panic";
package panic;
// The greeting service definition.
service PanicService {
rpc Panic (PanicRequest) returns (PanicResponse) {}
}
message PanicRequest {
}
message PanicResponse {
}

View File

@@ -0,0 +1,105 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.6.1
// source: panic.proto
package panic
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// 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
// PanicServiceClient is the client API for PanicService 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 PanicServiceClient interface {
Panic(ctx context.Context, in *PanicRequest, opts ...grpc.CallOption) (*PanicResponse, error)
}
type panicServiceClient struct {
cc grpc.ClientConnInterface
}
func NewPanicServiceClient(cc grpc.ClientConnInterface) PanicServiceClient {
return &panicServiceClient{cc}
}
func (c *panicServiceClient) Panic(ctx context.Context, in *PanicRequest, opts ...grpc.CallOption) (*PanicResponse, error) {
out := new(PanicResponse)
err := c.cc.Invoke(ctx, "/panic.PanicService/Panic", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// PanicServiceServer is the server API for PanicService service.
// All implementations must embed UnimplementedPanicServiceServer
// for forward compatibility
type PanicServiceServer interface {
Panic(context.Context, *PanicRequest) (*PanicResponse, error)
mustEmbedUnimplementedPanicServiceServer()
}
// UnimplementedPanicServiceServer must be embedded to have forward compatible implementations.
type UnimplementedPanicServiceServer struct {
}
func (UnimplementedPanicServiceServer) Panic(context.Context, *PanicRequest) (*PanicResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Panic not implemented")
}
func (UnimplementedPanicServiceServer) mustEmbedUnimplementedPanicServiceServer() {}
// UnsafePanicServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PanicServiceServer will
// result in compilation errors.
type UnsafePanicServiceServer interface {
mustEmbedUnimplementedPanicServiceServer()
}
func RegisterPanicServiceServer(s grpc.ServiceRegistrar, srv PanicServiceServer) {
s.RegisterService(&PanicService_ServiceDesc, srv)
}
func _PanicService_Panic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PanicRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PanicServiceServer).Panic(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panic.PanicService/Panic",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PanicServiceServer).Panic(ctx, req.(*PanicRequest))
}
return interceptor(ctx, in, info, handler)
}
// PanicService_ServiceDesc is the grpc.ServiceDesc for PanicService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var PanicService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "panic.PanicService",
HandlerType: (*PanicServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Panic",
Handler: _PanicService_Panic_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "panic.proto",
}

View File

@@ -4,13 +4,21 @@ import (
"fmt"
"net"
"github.com/stefanprodan/podinfo/pkg/api/grpc/echo"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/reflection"
"github.com/stefanprodan/podinfo/pkg/api/grpc/panic"
"github.com/stefanprodan/podinfo/pkg/api/grpc/version"
)
type Server struct {
logger *zap.Logger
config *Config
@@ -19,6 +27,32 @@ type Server struct {
type Config struct {
Port int `mapstructure:"grpc-port"`
ServiceName string `mapstructure:"grpc-service-name"`
BackendURL []string `mapstructure:"backend-url"`
UILogo string `mapstructure:"ui-logo"`
UIMessage string `mapstructure:"ui-message"`
UIColor string `mapstructure:"ui-color"`
UIPath string `mapstructure:"ui-path"`
DataPath string `mapstructure:"data-path"`
ConfigPath string `mapstructure:"config-path"`
CertPath string `mapstructure:"cert-path"`
Host string `mapstructure:"host"`
//Port string `mapstructure:"port"`
SecurePort string `mapstructure:"secure-port"`
PortMetrics int `mapstructure:"port-metrics"`
Hostname string `mapstructure:"hostname"`
H2C bool `mapstructure:"h2c"`
RandomDelay bool `mapstructure:"random-delay"`
RandomDelayUnit string `mapstructure:"random-delay-unit"`
RandomDelayMin int `mapstructure:"random-delay-min"`
RandomDelayMax int `mapstructure:"random-delay-max"`
RandomError bool `mapstructure:"random-error"`
Unhealthy bool `mapstructure:"unhealthy"`
Unready bool `mapstructure:"unready"`
JWTSecret string `mapstructure:"jwt-secret"`
CacheServer string `mapstructure:"cache-server"`
}
func NewServer(config *Config, logger *zap.Logger) (*Server, error) {
@@ -30,6 +64,7 @@ func NewServer(config *Config, logger *zap.Logger) (*Server, error) {
return srv, nil
}
func (s *Server) ListenAndServe() *grpc.Server {
listener, err := net.Listen("tcp", fmt.Sprintf(":%v", s.config.Port))
if err != nil {
@@ -38,6 +73,14 @@ func (s *Server) ListenAndServe() *grpc.Server {
srv := grpc.NewServer()
server := health.NewServer()
// Register grpc apis for refection
echo.RegisterEchoServiceServer(srv, &echoServer{})
version.RegisterVersionServiceServer(srv, &VersionServer{config: s.config, logger: s.logger})
panic.RegisterPanicServiceServer(srv, &PanicServer{config: s.config, logger: s.logger})
reflection.Register(srv)
grpc_health_v1.RegisterHealthServer(srv, server)
server.SetServingStatus(s.config.ServiceName, grpc_health_v1.HealthCheckResponse_SERVING)

21
pkg/api/grpc/version.go Normal file
View File

@@ -0,0 +1,21 @@
package grpc
import (
"context"
pb "github.com/stefanprodan/podinfo/pkg/api/grpc/version"
"github.com/stefanprodan/podinfo/pkg/version"
"go.uber.org/zap"
)
type VersionServer struct {
pb.UnimplementedVersionServiceServer
config *Config
logger *zap.Logger
}
func (s *VersionServer) Version(ctx context.Context, req *pb.VersionRequest) (*pb.VersionResponse, error) {
return &pb.VersionResponse{Version: version.VERSION, Commit: version.REVISION}, nil
}

View File

@@ -0,0 +1,211 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.6.1
// source: version.proto
package version
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
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 VersionRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *VersionRequest) Reset() {
*x = VersionRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_version_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *VersionRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VersionRequest) ProtoMessage() {}
func (x *VersionRequest) ProtoReflect() protoreflect.Message {
mi := &file_version_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 VersionRequest.ProtoReflect.Descriptor instead.
func (*VersionRequest) Descriptor() ([]byte, []int) {
return file_version_proto_rawDescGZIP(), []int{0}
}
type VersionResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
Commit string `protobuf:"bytes,2,opt,name=commit,proto3" json:"commit,omitempty"`
}
func (x *VersionResponse) Reset() {
*x = VersionResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_version_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *VersionResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VersionResponse) ProtoMessage() {}
func (x *VersionResponse) ProtoReflect() protoreflect.Message {
mi := &file_version_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 VersionResponse.ProtoReflect.Descriptor instead.
func (*VersionResponse) Descriptor() ([]byte, []int) {
return file_version_proto_rawDescGZIP(), []int{1}
}
func (x *VersionResponse) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
func (x *VersionResponse) GetCommit() string {
if x != nil {
return x.Commit
}
return ""
}
var File_version_proto protoreflect.FileDescriptor
var file_version_proto_rawDesc = []byte{
0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x10, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x0f, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x32,
0x50, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0x3e, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x2e, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_version_proto_rawDescOnce sync.Once
file_version_proto_rawDescData = file_version_proto_rawDesc
)
func file_version_proto_rawDescGZIP() []byte {
file_version_proto_rawDescOnce.Do(func() {
file_version_proto_rawDescData = protoimpl.X.CompressGZIP(file_version_proto_rawDescData)
})
return file_version_proto_rawDescData
}
var file_version_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_version_proto_goTypes = []interface{}{
(*VersionRequest)(nil), // 0: version.VersionRequest
(*VersionResponse)(nil), // 1: version.VersionResponse
}
var file_version_proto_depIdxs = []int32{
0, // 0: version.VersionService.Version:input_type -> version.VersionRequest
1, // 1: version.VersionService.Version:output_type -> version.VersionResponse
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_version_proto_init() }
func file_version_proto_init() {
if File_version_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_version_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VersionRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_version_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VersionResponse); 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_version_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_version_proto_goTypes,
DependencyIndexes: file_version_proto_depIdxs,
MessageInfos: file_version_proto_msgTypes,
}.Build()
File_version_proto = out.File
file_version_proto_rawDesc = nil
file_version_proto_goTypes = nil
file_version_proto_depIdxs = nil
}

View File

@@ -0,0 +1,18 @@
syntax = "proto3";
option go_package = "./version";
package version;
service VersionService {
rpc Version (VersionRequest) returns (VersionResponse) {}
}
message VersionRequest {}
message VersionResponse {
string version = 1;
string commit = 2;
}

View File

@@ -0,0 +1,105 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.6.1
// source: version.proto
package version
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// 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
// VersionServiceClient is the client API for VersionService 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 VersionServiceClient interface {
Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error)
}
type versionServiceClient struct {
cc grpc.ClientConnInterface
}
func NewVersionServiceClient(cc grpc.ClientConnInterface) VersionServiceClient {
return &versionServiceClient{cc}
}
func (c *versionServiceClient) Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error) {
out := new(VersionResponse)
err := c.cc.Invoke(ctx, "/version.VersionService/Version", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// VersionServiceServer is the server API for VersionService service.
// All implementations must embed UnimplementedVersionServiceServer
// for forward compatibility
type VersionServiceServer interface {
Version(context.Context, *VersionRequest) (*VersionResponse, error)
mustEmbedUnimplementedVersionServiceServer()
}
// UnimplementedVersionServiceServer must be embedded to have forward compatible implementations.
type UnimplementedVersionServiceServer struct {
}
func (UnimplementedVersionServiceServer) Version(context.Context, *VersionRequest) (*VersionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Version not implemented")
}
func (UnimplementedVersionServiceServer) mustEmbedUnimplementedVersionServiceServer() {}
// UnsafeVersionServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to VersionServiceServer will
// result in compilation errors.
type UnsafeVersionServiceServer interface {
mustEmbedUnimplementedVersionServiceServer()
}
func RegisterVersionServiceServer(s grpc.ServiceRegistrar, srv VersionServiceServer) {
s.RegisterService(&VersionService_ServiceDesc, srv)
}
func _VersionService_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(VersionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(VersionServiceServer).Version(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/version.VersionService/Version",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(VersionServiceServer).Version(ctx, req.(*VersionRequest))
}
return interceptor(ctx, in, info, handler)
}
// VersionService_ServiceDesc is the grpc.ServiceDesc for VersionService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var VersionService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "version.VersionService",
HandlerType: (*VersionServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Version",
Handler: _VersionService_Version_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "version.proto",
}

View File

@@ -0,0 +1,72 @@
package grpc
import (
"context"
"fmt"
"log"
"net"
"regexp"
"testing"
"github.com/stefanprodan/podinfo/pkg/api/grpc/version"
v "github.com/stefanprodan/podinfo/pkg/version"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
"google.golang.org/grpc/test/bufconn"
)
func TestGrpcVersion(t *testing.T) {
// Server initialization
// bufconn => uses in-memory connection instead of system network I/O
lis := bufconn.Listen(1024*1024)
t.Cleanup(func() {
lis.Close()
})
srv := grpc.NewServer()
t.Cleanup(func() {
srv.Stop()
})
version.RegisterVersionServiceServer(srv, &VersionServer{})
go func(){
if err := srv.Serve(lis); err != nil {
log.Fatalf("srv.Serve %v", err)
}
}()
// - Test
dialer := func(context.Context, string) (net.Conn, error){
return lis.Dial()
}
ctx := context.Background()
conn, err := grpc.DialContext(ctx, "", grpc.WithContextDialer(dialer), grpc.WithInsecure())
t.Cleanup(func() {
conn.Close()
})
if err != nil {
t.Fatalf("grpc.DialContext %v", err)
}
client := version.NewVersionServiceClient(conn)
res , err := client.Version(context.Background(), &version.VersionRequest{})
// Check the status code is what we expect.
if _, ok := status.FromError(err); !ok {
t.Errorf("Version returned type %T, want %T", err, status.Error)
}
// Check the response body is what we expect.
expected := fmt.Sprintf(".*%s.*", v.VERSION)
r := regexp.MustCompile(expected)
if !r.MatchString(res.String()) {
t.Fatalf("Returned unexpected body:\ngot \n%v \nwant \n%s",
res, expected)
}
}