From 04deb65803c95c09520ba69e491b5c0b9ed2d1ee Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 10 Jul 2024 20:46:57 +0300 Subject: [PATCH] refactoring v3. cobra --- Dockerfile | 2 +- cmd/check.go | 63 ++++ cmd/root.go | 105 ++++++ go.mod | 3 + go.sum | 8 + go.work.sum | 2 +- internal/app/grpc_server.go | 80 +++++ internal/app/rest_server.go | 4 - internal/deps/builder.go | 40 +++ internal/deps/config.go | 7 + internal/deps/grpc_client.go | 27 ++ internal/deps/health_client.go | 16 + internal/deps/reflector.go | 12 + internal/deps/signal.go | 11 + internal/deps/srv_grpc.go | 12 + stub/stub.go => internal/deps/srv_rest.go | 46 ++- internal/deps/ui.go | 11 + internal/deps/zerolog.go | 25 ++ internal/domain/proto/params.go | 33 ++ internal/domain/servergen/gen.go | 190 ++++++++++ .../domain/servergen/gen_test.go | 4 +- internal/domain/waiter/const.go | 12 + .../muxmiddleware/content_type.go | 0 .../{pkg => infra}/muxmiddleware/logger.go | 0 .../muxmiddleware/resp_writer.go | 0 .../{pkg => infra}/muxmiddleware/utils.go | 0 internal/{pkg => infra}/patcher/writer.go | 0 .../{pkg => infra}/patcher/writer_test.go | 2 +- internal/infra/waiter/svc.go | 54 +++ main.go | 327 +----------------- protoc-gen-gripmock/server.tmpl | 4 +- 31 files changed, 738 insertions(+), 362 deletions(-) create mode 100644 cmd/check.go create mode 100644 cmd/root.go create mode 100644 internal/app/grpc_server.go create mode 100644 internal/deps/builder.go create mode 100644 internal/deps/config.go create mode 100644 internal/deps/grpc_client.go create mode 100644 internal/deps/health_client.go create mode 100644 internal/deps/reflector.go create mode 100644 internal/deps/signal.go create mode 100644 internal/deps/srv_grpc.go rename stub/stub.go => internal/deps/srv_rest.go (57%) create mode 100644 internal/deps/ui.go create mode 100644 internal/deps/zerolog.go create mode 100644 internal/domain/proto/params.go create mode 100644 internal/domain/servergen/gen.go rename main_test.go => internal/domain/servergen/gen_test.go (94%) create mode 100644 internal/domain/waiter/const.go rename internal/{pkg => infra}/muxmiddleware/content_type.go (100%) rename internal/{pkg => infra}/muxmiddleware/logger.go (100%) rename internal/{pkg => infra}/muxmiddleware/resp_writer.go (100%) rename internal/{pkg => infra}/muxmiddleware/utils.go (100%) rename internal/{pkg => infra}/patcher/writer.go (100%) rename internal/{pkg => infra}/patcher/writer_test.go (96%) create mode 100644 internal/infra/waiter/svc.go diff --git a/Dockerfile b/Dockerfile index 0f824c8d..e256a6fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,7 +40,7 @@ COPY . /go/src/github.com/bavix/gripmock WORKDIR /go/src/github.com/bavix/gripmock -RUN go install -v -ldflags "-X 'main.version=${version:-dev}' -s -w" +RUN go install -v -ldflags "-X 'github.com/bavix/gripmock/cmd.version=${version:-dev}' -s -w" EXPOSE 4770 4771 diff --git a/cmd/check.go b/cmd/check.go new file mode 100644 index 00000000..ebd69ba9 --- /dev/null +++ b/cmd/check.go @@ -0,0 +1,63 @@ +//nolint:gochecknoglobals +package cmd + +import ( + "errors" + "time" + + "github.com/rs/zerolog" + "github.com/spf13/cobra" + + "github.com/bavix/gripmock/internal/deps" + "github.com/bavix/gripmock/internal/domain/waiter" +) + +var ( + pingTimeout time.Duration + errServerIsNotRunning = errors.New("server is not running") +) + +const serviceName = "gripmock" + +var checkCmd = &cobra.Command{ + Use: "check", + Short: "The command checks whether the gripmock server is alive or dead by accessing it via the API", + RunE: func(cmd *cobra.Command, _ []string) error { + builder := deps.NewBuilder(deps.WithDefaultConfig()) + ctx, cancel := builder.SignalNotify(cmd.Context()) + defer cancel() + + ctx = builder.Logger(ctx) + + pingService, err := builder.PingService() + if err != nil { + zerolog.Ctx(ctx).Err(err).Msg("create ping service failed") + + return err + } + + status, err := pingService.PingWithTimeout(ctx, pingTimeout, serviceName) + if err != nil { + zerolog.Ctx(ctx).Err(err).Msg("unable to connect to server") + + return err + } + + if status != waiter.Serving { + zerolog.Ctx(ctx).Error().Uint32("code", uint32(status)).Msg("server is not running") + + return errServerIsNotRunning + } + + return nil + }, +} + +//nolint:gochecknoinits +func init() { + rootCmd.AddCommand(checkCmd) + + const defaultPingTimeout = time.Second * 5 + + checkCmd.Flags().DurationVarP(&pingTimeout, "timeout", "t", defaultPingTimeout, "timeout") +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 00000000..6b79df72 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,105 @@ +//nolint:gochecknoglobals +package cmd + +import ( + "context" + "errors" + "net/http" + "os" + + "github.com/rs/zerolog" + "github.com/spf13/cobra" + + "github.com/bavix/gripmock/internal/deps" + "github.com/bavix/gripmock/internal/domain/proto" +) + +var ( + outputFlag string + stubFlag string + importsFlag []string + version = "development" +) + +var rootCmd = &cobra.Command{ + Use: "gripmock", + Short: "gRPC Mock Server", + Version: version, + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + builder := deps.NewBuilder(deps.WithDefaultConfig()) + ctx, cancel := builder.SignalNotify(cmd.Context()) + defer cancel() + + ctx = builder.Logger(ctx) + + zerolog.Ctx(ctx).Info().Str("release", version).Msg("Starting GripMock") + + go func() { + if err := restServe(ctx, builder); err != nil { + zerolog.Ctx(ctx).Err(err).Msg("failed to start rest server") + } + }() + + return builder.GRPCServe(cmd.Context(), proto.NewProtocParam(args, outputFlag, importsFlag)) + }, +} + +func restServe(ctx context.Context, builder *deps.Builder) error { + srv, err := builder.RestServe(ctx, stubFlag) + if err != nil { + return err + } + + ch := make(chan error) + + go func() { + zerolog.Ctx(ctx). + Info(). + Str("addr", builder.Config().HTTPAddr). + Msg("Serving stub-manager") + + ch <- srv.ListenAndServe() + }() + + select { + case err = <-ch: + if errors.Is(err, http.ErrServerClosed) { + return nil + } + + return err + case <-ctx.Done(): + return ctx.Err() + } +} + +//nolint:gochecknoinits +func init() { + rootCmd.Flags().StringVarP( + &outputFlag, + "output", + "o", + os.Getenv("GOPATH")+"/src/grpc", + "Server generation directory server.go") + + rootCmd.Flags().StringVarP( + &stubFlag, + "stub", + "s", + "", + "Path where the stub files are (Optional)") + + rootCmd.Flags().StringSliceVarP( + &importsFlag, + "imports", + "i", + []string{"/protobuf", "/googleapis"}, + "Path to import proto-libraries") +} + +func Execute(ctx context.Context) { + if err := rootCmd.ExecuteContext(ctx); err != nil { + os.Exit(1) + } +} diff --git a/go.mod b/go.mod index 1c6667d1..26ed9fbf 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/jhump/protoreflect v1.16.0 github.com/oapi-codegen/runtime v1.1.1 github.com/rs/zerolog v1.33.0 + github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 golang.org/x/text v0.16.0 google.golang.org/genproto/googleapis/api v0.0.0-20240709173604-40e1e62336c5 @@ -36,11 +37,13 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/gripmock/deeply v1.1.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect golang.org/x/net v0.27.0 // indirect golang.org/x/sys v0.22.0 // indirect diff --git a/go.sum b/go.sum index 01f665fd..e839c329 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,7 @@ github.com/bufbuild/protocompile v0.14.0/go.mod h1:N6J1NYzkspJo3ZwyL4Xjvli86XOj1 github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA= github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cristalhq/base64 v0.1.2 h1:edsefYyYDiac7Ytdh2xdaiiSSJzcI2f0yIkdGEf1qY0= github.com/cristalhq/base64 v0.1.2/go.mod h1:sy4+2Hale2KbtSqkzpdMeYTP/IrB+HCvxVHWsh2VSYk= @@ -60,6 +61,8 @@ github.com/gripmock/shutdown v1.0.0 h1:ESDCCBeNHazgAstCpIskaORNWH3b+P03a2gznW+8I github.com/gripmock/shutdown v1.0.0/go.mod h1:YwyI7uYgIPPdR9k8QNHwzDI2mQNpUSu+WT9mvqVoty4= github.com/gripmock/stuber v1.0.4 h1:XqdZF/yz6cVPuAjahgBYFKOQjh8oNBTQMB3tXrnwe58= github.com/gripmock/stuber v1.0.4/go.mod h1:bYmpJTIc4Mwl4/2CM1cREOWLZfweFFg05xEUxeOwPCY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg= github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= @@ -90,6 +93,11 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99 github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/go.work.sum b/go.work.sum index 5f945297..3fdf29ed 100644 --- a/go.work.sum +++ b/go.work.sum @@ -35,6 +35,7 @@ github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLI github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= @@ -121,7 +122,6 @@ github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= diff --git a/internal/app/grpc_server.go b/internal/app/grpc_server.go new file mode 100644 index 00000000..ee046ba0 --- /dev/null +++ b/internal/app/grpc_server.go @@ -0,0 +1,80 @@ +package app + +import ( + "context" + "os" + "os/exec" + + "github.com/rs/zerolog" + + "github.com/bavix/gripmock/internal/domain/proto" + "github.com/bavix/gripmock/internal/domain/servergen" +) + +type GRPCServer struct { + params *proto.ProtocParam +} + +func NewGRPCServer(params *proto.ProtocParam) *GRPCServer { + return &GRPCServer{params} +} + +func (s *GRPCServer) Serve(ctx context.Context) error { + err := servergen.ServerGenerate(ctx, s.params) + if err != nil { + return err + } + + server, ch := s.newServer(ctx) + + // Wait for the gRPC server to exit or the context to be done. + select { + case err := <-ch: + return err + case <-ctx.Done(): + // If the context is done, check if there was an error. + if err := ctx.Err(); err != nil { + return err + } + + // Kill the gRPC server process. + if err := server.Process.Kill(); err != nil { + return err + } + } + + return nil +} + +// newServer runs the gRPC server in a separate process. +// +// ctx is the context.Context to use for the command. +// output is the output directory where the server.go file is located. +// It returns the exec.Cmd object representing the running process, and a channel +// that receives an error when the process exits. +func (s *GRPCServer) newServer(ctx context.Context) (*exec.Cmd, <-chan error) { + // Construct the command to run the gRPC server. + run := exec.CommandContext(ctx, "go", "run", s.params.Output()+"/server.go") //nolint:gosec + run.Env = os.Environ() + run.Stdout = os.Stdout + run.Stderr = os.Stderr + + // Start the command. + if err := run.Start(); err != nil { + zerolog.Ctx(ctx).Fatal().Err(err).Msg("unable to start gRPC service") + } + + // Log the process ID. + zerolog.Ctx(ctx).Info().Int("pid", run.Process.Pid).Msg("gRPC-service started") + + // Create a channel to receive the process exit error. + runErr := make(chan error) + + // Start a goroutine to wait for the process to exit and send the error + // to the channel. + go func() { + runErr <- run.Wait() + }() + + return run, runErr +} diff --git a/internal/app/rest_server.go b/internal/app/rest_server.go index eed29b5a..1a332045 100644 --- a/internal/app/rest_server.go +++ b/internal/app/rest_server.go @@ -15,8 +15,6 @@ import ( "github.com/google/uuid" "github.com/gripmock/stuber" - "golang.org/x/text/cases" - "golang.org/x/text/language" "github.com/bavix/features" "github.com/bavix/gripmock/internal/domain/rest" @@ -34,7 +32,6 @@ type RestServer struct { ok atomic.Bool stuber *stuber.Budgerigar convertor *yaml2json.Convertor - caser cases.Caser reflector *grpcreflector.GReflector } @@ -44,7 +41,6 @@ func NewRestServer(path string, reflector *grpcreflector.GReflector) (*RestServe server := &RestServer{ stuber: stuber.NewBudgerigar(features.New(stuber.MethodTitle)), convertor: yaml2json.New(), - caser: cases.Title(language.English, cases.NoLower), reflector: reflector, } diff --git a/internal/deps/builder.go b/internal/deps/builder.go new file mode 100644 index 00000000..0a973302 --- /dev/null +++ b/internal/deps/builder.go @@ -0,0 +1,40 @@ +package deps + +import ( + "github.com/gripmock/environment" + "github.com/gripmock/shutdown" +) + +type Option func(*Builder) + +type Builder struct { + config environment.Config + ender *shutdown.Shutdown +} + +func NewBuilder(opts ...Option) *Builder { + builder := &Builder{ender: shutdown.New(nil)} + for _, opt := range opts { + opt(builder) + } + + return builder +} + +func WithDefaultConfig() Option { + config, _ := environment.New() + + return WithConfig(config) +} + +func WithConfig(config environment.Config) Option { + return func(builder *Builder) { + builder.config = config + } +} + +func WithEnder(ender *shutdown.Shutdown) Option { + return func(builder *Builder) { + builder.ender = ender + } +} diff --git a/internal/deps/config.go b/internal/deps/config.go new file mode 100644 index 00000000..d83ad82c --- /dev/null +++ b/internal/deps/config.go @@ -0,0 +1,7 @@ +package deps + +import "github.com/gripmock/environment" + +func (b *Builder) Config() environment.Config { + return b.config +} diff --git a/internal/deps/grpc_client.go b/internal/deps/grpc_client.go new file mode 100644 index 00000000..0a1c1120 --- /dev/null +++ b/internal/deps/grpc_client.go @@ -0,0 +1,27 @@ +package deps + +import ( + "context" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func (b *Builder) dialOptions(_ bool) []grpc.DialOption { + return []grpc.DialOption{ + grpc.WithTransportCredentials(insecure.NewCredentials()), + } +} + +func (b *Builder) grpcClientConn(tls bool, dsn string) (*grpc.ClientConn, error) { + conn, err := grpc.NewClient(dsn, b.dialOptions(tls)...) + if err != nil { + return nil, err + } + + b.ender.Add(func(_ context.Context) error { + return conn.Close() + }) + + return conn, nil +} diff --git a/internal/deps/health_client.go b/internal/deps/health_client.go new file mode 100644 index 00000000..ccabc54f --- /dev/null +++ b/internal/deps/health_client.go @@ -0,0 +1,16 @@ +package deps + +import ( + healthv1 "google.golang.org/grpc/health/grpc_health_v1" + + "github.com/bavix/gripmock/internal/infra/waiter" +) + +func (b *Builder) PingService() (*waiter.Service, error) { + grpcConn, err := b.grpcClientConn(false, b.config.GRPCAddr) + if err != nil { + return nil, err + } + + return waiter.NewService(healthv1.NewHealthClient(grpcConn)), nil +} diff --git a/internal/deps/reflector.go b/internal/deps/reflector.go new file mode 100644 index 00000000..626ef6e7 --- /dev/null +++ b/internal/deps/reflector.go @@ -0,0 +1,12 @@ +package deps + +import "github.com/bavix/gripmock/pkg/grpcreflector" + +func (b *Builder) reflector() (*grpcreflector.GReflector, error) { + conn, err := b.grpcClientConn(false, b.config.GRPCAddr) + if err != nil { + return nil, err + } + + return grpcreflector.New(conn), nil +} diff --git a/internal/deps/signal.go b/internal/deps/signal.go new file mode 100644 index 00000000..45c0ee5b --- /dev/null +++ b/internal/deps/signal.go @@ -0,0 +1,11 @@ +package deps + +import ( + "context" + "os/signal" + "syscall" +) + +func (*Builder) SignalNotify(ctx context.Context) (context.Context, context.CancelFunc) { + return signal.NotifyContext(ctx, syscall.SIGTERM, syscall.SIGINT) +} diff --git a/internal/deps/srv_grpc.go b/internal/deps/srv_grpc.go new file mode 100644 index 00000000..91b1cc8f --- /dev/null +++ b/internal/deps/srv_grpc.go @@ -0,0 +1,12 @@ +package deps + +import ( + "context" + + "github.com/bavix/gripmock/internal/app" + "github.com/bavix/gripmock/internal/domain/proto" +) + +func (b *Builder) GRPCServe(ctx context.Context, param *proto.ProtocParam) error { + return app.NewGRPCServer(param).Serve(ctx) +} diff --git a/stub/stub.go b/internal/deps/srv_rest.go similarity index 57% rename from stub/stub.go rename to internal/deps/srv_rest.go index e887253a..b8304419 100644 --- a/stub/stub.go +++ b/internal/deps/srv_rest.go @@ -1,33 +1,37 @@ -package stub +package deps import ( "context" - "errors" "net" "net/http" "time" "github.com/gorilla/handlers" "github.com/gorilla/mux" - "github.com/gripmock/environment" - "github.com/rs/zerolog" - gripmockui "github.com/bavix/gripmock-ui" "github.com/bavix/gripmock/internal/app" "github.com/bavix/gripmock/internal/domain/rest" - "github.com/bavix/gripmock/internal/pkg/muxmiddleware" - "github.com/bavix/gripmock/pkg/grpcreflector" + "github.com/bavix/gripmock/internal/infra/muxmiddleware" ) -func RunRestServer( +func (b *Builder) RestServe( ctx context.Context, stubPath string, - config environment.Config, - reflector *grpcreflector.GReflector, -) { - apiServer, _ := app.NewRestServer(stubPath, reflector) +) (*http.Server, error) { + reflector, err := b.reflector() + if err != nil { + return nil, err + } + + apiServer, err := app.NewRestServer(stubPath, reflector) + if err != nil { + return nil, err + } - ui, _ := gripmockui.Assets() + ui, err := b.ui() + if err != nil { + return nil, err + } router := mux.NewRouter() rest.HandlerWithOptions(apiServer, rest.GorillaServerOptions{ @@ -42,8 +46,8 @@ func RunRestServer( const timeout = time.Millisecond * 25 - srv := &http.Server{ - Addr: config.HTTPAddr, + return &http.Server{ + Addr: b.config.HTTPAddr, ReadHeaderTimeout: timeout, BaseContext: func(_ net.Listener) context.Context { return ctx @@ -56,15 +60,5 @@ func RunRestServer( }), handlers.AllowedMethods([]string{http.MethodGet, http.MethodPost, http.MethodDelete}), )(router), - } - - zerolog.Ctx(ctx). - Info(). - Str("addr", config.HTTPAddr). - Msg("stub-manager started") - - // nosemgrep:go.lang.security.audit.net.use-tls.use-tls - if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { - zerolog.Ctx(ctx).Fatal().Err(err).Msg("stub manager completed") - } + }, nil } diff --git a/internal/deps/ui.go b/internal/deps/ui.go new file mode 100644 index 00000000..1e3dd537 --- /dev/null +++ b/internal/deps/ui.go @@ -0,0 +1,11 @@ +package deps + +import ( + "io/fs" + + gripmockui "github.com/bavix/gripmock-ui" +) + +func (b *Builder) ui() (fs.FS, error) { + return gripmockui.Assets() +} diff --git a/internal/deps/zerolog.go b/internal/deps/zerolog.go new file mode 100644 index 00000000..564c4fa7 --- /dev/null +++ b/internal/deps/zerolog.go @@ -0,0 +1,25 @@ +package deps + +import ( + "context" + "log" + "time" + + "github.com/rs/zerolog" +) + +func (b *Builder) Logger(ctx context.Context) context.Context { + level, err := zerolog.ParseLevel(b.config.LogLevel) + if err != nil { + log.Fatal(err) + } + + return zerolog.New(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) { + w.TimeFormat = time.RFC3339Nano + })). + Level(level). + With(). + Timestamp(). + Logger(). + WithContext(ctx) +} diff --git a/internal/domain/proto/params.go b/internal/domain/proto/params.go new file mode 100644 index 00000000..6bb9789a --- /dev/null +++ b/internal/domain/proto/params.go @@ -0,0 +1,33 @@ +package proto + +// ProtocParam represents the parameters for the protoc command. +type ProtocParam struct { + // output is the output directory for the generated files. + output string + + // protoPath is a list of paths to the proto files. + protoPath []string + + // imports is a list of import paths. + imports []string +} + +func NewProtocParam(protoPath []string, output string, imports []string) *ProtocParam { + return &ProtocParam{ + protoPath: protoPath, + output: output, + imports: imports, + } +} + +func (p *ProtocParam) ProtoPath() []string { + return p.protoPath +} + +func (p *ProtocParam) Imports() []string { + return p.imports +} + +func (p *ProtocParam) Output() string { + return p.output +} diff --git a/internal/domain/servergen/gen.go b/internal/domain/servergen/gen.go new file mode 100644 index 00000000..aaa6d0c1 --- /dev/null +++ b/internal/domain/servergen/gen.go @@ -0,0 +1,190 @@ +package servergen + +import ( + "context" + "errors" + "io" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + + "github.com/rs/zerolog" + + "github.com/bavix/gripmock/internal/domain/proto" + "github.com/bavix/gripmock/internal/infra/patcher" +) + +var errProtoNotFound = errors.New("proto not found") + +// ServerGenerate is a function that runs the protoc command with the given +// parameters. +// +// It takes a context.Context and a protocParam struct as parameters. The +// protocParam struct contains the protoPath, output, and imports fields that +// are used to configure the protoc command. +// +// It generates the protoc command arguments and runs the protoc command with +// the given parameters. If there is an error running the protoc command, it +// logs a fatal message. +func ServerGenerate(ctx context.Context, param *proto.ProtocParam) error { + // Check if output folder exists, if not create it + // nosemgrep:semgrep-go.os-error-is-not-exist + if _, err := os.Stat(param.Output()); os.IsNotExist(err) { + // Create output folder + if err := os.Mkdir(param.Output(), os.ModePerm); err != nil { + zerolog.Ctx(ctx).Fatal().Err(err).Msg("unable to create output folder") + } + } + + // Fix the go_package option for each proto file in the protoPath. + protoPath := fixGoPackage(ctx, param.ProtoPath()) + if len(protoPath) == 0 { + return errProtoNotFound + } + + // Get the proto directories based on the protoPath and imports. + protoDirs := getProtodirs(ctx, protoPath[0], param.Imports()) + + // Estimate the length of the args slice to prevent expanding it. + args := make([]string, 0, len(protoDirs)+len(protoPath)+2) //nolint:mnd + + // Append the -I option for each proto directory to the args slice. + for _, dir := range protoDirs { + args = append(args, "-I", dir) + } + + // Set the output directory for generated files to $GOPATH/src. + pbOutput := os.Getenv("GOPATH") + "/src" + + // Append the protoPath, --go_out, --go-grpc_out, and --gripmock_out options + // to the args slice. + args = append(args, protoPath...) + args = append(args, "--go_out="+pbOutput) + args = append(args, "--go-grpc_out="+pbOutput) + args = append(args, "--gripmock_out="+param.Output()) + + // Create a new exec.Cmd command with the protoc command and the args. + protoc := exec.Command("protoc", args...) + + // Set the environment variables for the command. + protoc.Env = os.Environ() + + // Set the stdout and stderr for the command. + protoc.Stdout = os.Stdout + protoc.Stderr = os.Stderr + + return protoc.Run() +} + +// fixGoPackage is a function that appends the go_package option to each +// proto file in the given protoPaths if the proto file doesn't already have +// one. +// +// It reads each proto file, creates a temporary file with the go_package option, +// and copies the contents of the original file to the temporary file. The +// temporary file is then returned as part of the results. +// +// ctx is the context.Context to use for the function. +// protoPaths is a slice of string paths to the proto files. +// fixGoPackage returns a slice of string paths to the temporary files. +func fixGoPackage(ctx context.Context, protoPaths []string) []string { + results := make([]string, 0, len(protoPaths)) + + for _, protoPath := range protoPaths { + pile, err := os.OpenFile(protoPath, os.O_RDONLY, 0o600) //nolint:mnd + if err != nil { + zerolog.Ctx(ctx).Err(err).Msgf("unable to open protofile %s", protoPath) + + continue + } + + defer pile.Close() + + packageName := "protogen/" + strings.Trim(filepath.Dir(protoPath), "/") + + if err := os.MkdirAll(packageName, 0o666); err != nil { //nolint:mnd + zerolog.Ctx(ctx).Err(err).Msgf("unable to create temp dir %s", protoPath) + + continue + } + + tmp, err := os.Create(filepath.Join(packageName, filepath.Base(protoPath))) + if err != nil { + zerolog.Ctx(ctx).Err(err).Msgf("unable to create temp file %s", protoPath) + + continue + } + + defer tmp.Close() + + if _, err = io.Copy(patcher.NewWriterWrapper(tmp, packageName), pile); err != nil { + zerolog.Ctx(ctx).Err(err).Msgf("unable to copy file %s", protoPath) + + continue + } + + results = append(results, tmp.Name()) + } + + return results +} + +// getProtodirs returns a list of proto directories based on the given protoPath +// and imports. +// +// It takes a context.Context and a protoPath string as well as a slice of strings +// representing the imports. The protoPath string is used to deduce the proto +// directory, and the imports are used to search for a proto directory prefix. +// +// The function returns a slice of strings representing the proto directories. +func getProtodirs(_ context.Context, protoPath string, imports []string) []string { + // Deduce the proto directory from the proto path. + splitPath := strings.Split(protoPath, "/") + protoDir := "" + + // If there are any elements in splitPath, join them up to the second-to-last + // element with path.Join to get the proto directory. + if len(splitPath) > 0 { + protoDir = path.Join(splitPath[:len(splitPath)-1]...) + } + + // Search for the proto directory prefix in the imports. + protoDirIdx := -1 + + for i := range imports { + // Join the "protogen" directory with the import directory to get the full + // directory path. + dir := path.Join("protogen", imports[i]) + + // If the proto directory starts with the full directory path, set the proto + // directory to the full directory path and set the index of the proto directory + // in the imports slice. + if strings.HasPrefix(protoDir, dir) { + protoDir = dir + protoDirIdx = i + + break + } + } + + // Create a slice to hold the proto directories. + protoDirs := make([]string, 0, len(imports)+1) + + // Append the proto directory to the slice. + protoDirs = append(protoDirs, protoDir) + + // Loop through the imports and append each directory to the slice, skipping + // any directories that have already been added. + for i, dir := range imports { + if i == protoDirIdx { + continue + } + + protoDirs = append(protoDirs, dir) + } + + // Return the slice of proto directories. + return protoDirs +} diff --git a/main_test.go b/internal/domain/servergen/gen_test.go similarity index 94% rename from main_test.go rename to internal/domain/servergen/gen_test.go index 85425f9e..a186253c 100644 --- a/main_test.go +++ b/internal/domain/servergen/gen_test.go @@ -1,4 +1,4 @@ -package main +package servergen //nolint:testpackage import ( "context" @@ -6,7 +6,7 @@ import ( "testing" ) -func Test_getProtodirs(t *testing.T) { +func TestGetProtodirs(t *testing.T) { type args struct { protoPath string imports []string diff --git a/internal/domain/waiter/const.go b/internal/domain/waiter/const.go new file mode 100644 index 00000000..172bbd0f --- /dev/null +++ b/internal/domain/waiter/const.go @@ -0,0 +1,12 @@ +package waiter + +import healthv1 "google.golang.org/grpc/health/grpc_health_v1" + +type ServingStatus uint32 + +const ( + Unknown = ServingStatus(healthv1.HealthCheckResponse_UNKNOWN) + Serving = ServingStatus(healthv1.HealthCheckResponse_SERVING) + NotServing = ServingStatus(healthv1.HealthCheckResponse_NOT_SERVING) + ServiceUnknown = ServingStatus(healthv1.HealthCheckResponse_SERVICE_UNKNOWN) +) diff --git a/internal/pkg/muxmiddleware/content_type.go b/internal/infra/muxmiddleware/content_type.go similarity index 100% rename from internal/pkg/muxmiddleware/content_type.go rename to internal/infra/muxmiddleware/content_type.go diff --git a/internal/pkg/muxmiddleware/logger.go b/internal/infra/muxmiddleware/logger.go similarity index 100% rename from internal/pkg/muxmiddleware/logger.go rename to internal/infra/muxmiddleware/logger.go diff --git a/internal/pkg/muxmiddleware/resp_writer.go b/internal/infra/muxmiddleware/resp_writer.go similarity index 100% rename from internal/pkg/muxmiddleware/resp_writer.go rename to internal/infra/muxmiddleware/resp_writer.go diff --git a/internal/pkg/muxmiddleware/utils.go b/internal/infra/muxmiddleware/utils.go similarity index 100% rename from internal/pkg/muxmiddleware/utils.go rename to internal/infra/muxmiddleware/utils.go diff --git a/internal/pkg/patcher/writer.go b/internal/infra/patcher/writer.go similarity index 100% rename from internal/pkg/patcher/writer.go rename to internal/infra/patcher/writer.go diff --git a/internal/pkg/patcher/writer_test.go b/internal/infra/patcher/writer_test.go similarity index 96% rename from internal/pkg/patcher/writer_test.go rename to internal/infra/patcher/writer_test.go index 678e74b5..52ada2a9 100644 --- a/internal/pkg/patcher/writer_test.go +++ b/internal/infra/patcher/writer_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/bavix/gripmock/internal/pkg/patcher" + "github.com/bavix/gripmock/internal/infra/patcher" ) func TestWriterWrapper_OptionUpdate(t *testing.T) { diff --git a/internal/infra/waiter/svc.go b/internal/infra/waiter/svc.go new file mode 100644 index 00000000..fc69bf5a --- /dev/null +++ b/internal/infra/waiter/svc.go @@ -0,0 +1,54 @@ +package waiter + +import ( + "context" + "time" + + "google.golang.org/grpc" + healthv1 "google.golang.org/grpc/health/grpc_health_v1" + + "github.com/bavix/gripmock/internal/domain/waiter" +) + +type Service struct { + client healthv1.HealthClient +} + +func NewService(client healthv1.HealthClient) *Service { + return &Service{client: client} +} + +func (s *Service) PingWithTimeout( + ctx context.Context, + timeout time.Duration, + service string, +) (waiter.ServingStatus, error) { + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + return s.Ping(ctx, service) +} + +func (s *Service) Ping(ctx context.Context, service string) (waiter.ServingStatus, error) { + check, err := s.client.Check( + ctx, + &healthv1.HealthCheckRequest{Service: service}, + grpc.WaitForReady(true), + ) + if err != nil { + return waiter.Unknown, err + } + + switch check.GetStatus() { + case healthv1.HealthCheckResponse_SERVING: + return waiter.Serving, nil + case healthv1.HealthCheckResponse_NOT_SERVING: + return waiter.NotServing, nil + case healthv1.HealthCheckResponse_SERVICE_UNKNOWN: + return waiter.ServiceUnknown, nil + case healthv1.HealthCheckResponse_UNKNOWN: + return waiter.Unknown, nil + default: + return waiter.Unknown, nil + } +} diff --git a/main.go b/main.go index 2c75e31f..8fdbfe4f 100644 --- a/main.go +++ b/main.go @@ -2,333 +2,10 @@ package main import ( "context" - "flag" - "io" - "log" - "os" - "os/exec" - "os/signal" - "path" - "path/filepath" - "strings" - "syscall" - "github.com/rs/zerolog" - - "github.com/bavix/gripmock/internal/pkg/patcher" - "github.com/bavix/gripmock/pkg/deps" - "github.com/bavix/gripmock/stub" + "github.com/bavix/gripmock/cmd" ) -var version = "development" - -//nolint:funlen,cyclop func main() { - outputPointer := flag.String("output", "", "directory to output server.go. Default is $GOPATH/src/grpc/") - flag.StringVar(outputPointer, "o", *outputPointer, "alias for -output") - - stubPath := flag.String("stub", "", "Path where the stub files are (Optional)") //nolint:lll,staticcheck - imports := flag.String("imports", "/protobuf,/googleapis", "comma separated imports path. default path /protobuf,/googleapis is where gripmock Dockerfile install WKT protos") //nolint:lll,staticcheck - - flag.Parse() - - ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) - defer cancel() - - builder, err := deps.New(ctx) - if err != nil { - log.Fatal(err) //nolint:gocritic - } - - logger := builder.Logger() - - ctx = logger.WithContext(ctx) - - // parse proto files - protoPaths := flag.Args() - - // Ensure at least one proto file is provided - if len(protoPaths) == 0 { - logger.Fatal().Msg("at least one proto file is required") - } - - // Start GripMock server - //nolint:godox - // TODO: move validation of required arguments to a separate service - logger.Info().Str("release", version).Msg("Starting GripMock") - - // Check if $GOPATH is set - if os.Getenv("GOPATH") == "" { - logger.Fatal().Msg("$GOPATH is empty") - } - - // Set output directory - output := *outputPointer - if output == "" { - // Default to $GOPATH/src/grpc if output is not provided - output = os.Getenv("GOPATH") + "/src/grpc" - } - - // For safety - output += "/" - - // Check if output folder exists, if not create it - // nosemgrep:semgrep-go.os-error-is-not-exist - if _, err := os.Stat(output); os.IsNotExist(err) { - // Create output folder - if err := os.Mkdir(output, os.ModePerm); err != nil { - logger.Fatal().Err(err).Msg("unable to create output folder") - } - } - - // Run the admin stub server in a separate goroutine. - // - // This goroutine runs the REST server that serves the stub files. - // It waits for the ready signal from the gRPC server goroutine. - // Once the gRPC server is ready, it starts the admin stub server. - go stub.RunRestServer(ctx, *stubPath, builder.Config(), builder.Reflector()) - - importDirs := strings.Split(*imports, ",") - - // Generate protoc-generated code and run the gRPC server. - // - // This section generates the protoc-generated code (pb.go) and runs the gRPC server. - // It creates the output directory if it does not exist. - // It then generates the protoc-generated code using the protocParam struct. - // Finally, it runs the gRPC server using the runGrpcServer function. - generateProtoc(ctx, protocParam{ - protoPath: protoPaths, - output: output, - imports: importDirs, - }) - - // And run - run, chErr := runGrpcServer(ctx, output) - - // Wait for the gRPC server to exit or the context to be done. - select { - case err := <-chErr: - // If the gRPC server exits with an error, log the error. - logger.Fatal().Err(err).Msg("gRPC server exited with an error") - case <-ctx.Done(): - // If the context is done, check if there was an error. - if err := ctx.Err(); err != nil { - logger.Err(err).Msg("an error has occurred") - } - - // Log that the gRPC server is stopping. - logger.Info().Msg("Stopping gRPC Server") - - // Kill the gRPC server process. - if err := run.Process.Kill(); err != nil { - logger.Fatal().Err(err).Msg("failed to kill process") - } - } -} - -// protocParam represents the parameters for the protoc command. -type protocParam struct { - // protoPath is a list of paths to the proto files. - protoPath []string - - // output is the output directory for the generated files. - output string - - // imports is a list of import paths. - imports []string -} - -// getProtodirs returns a list of proto directories based on the given protoPath -// and imports. -// -// It takes a context.Context and a protoPath string as well as a slice of strings -// representing the imports. The protoPath string is used to deduce the proto -// directory, and the imports are used to search for a proto directory prefix. -// -// The function returns a slice of strings representing the proto directories. -func getProtodirs(_ context.Context, protoPath string, imports []string) []string { - // Deduce the proto directory from the proto path. - splitPath := strings.Split(protoPath, "/") - protoDir := "" - - // If there are any elements in splitPath, join them up to the second-to-last - // element with path.Join to get the proto directory. - if len(splitPath) > 0 { - protoDir = path.Join(splitPath[:len(splitPath)-1]...) - } - - // Search for the proto directory prefix in the imports. - protoDirIdx := -1 - - for i := range imports { - // Join the "protogen" directory with the import directory to get the full - // directory path. - dir := path.Join("protogen", imports[i]) - - // If the proto directory starts with the full directory path, set the proto - // directory to the full directory path and set the index of the proto directory - // in the imports slice. - if strings.HasPrefix(protoDir, dir) { - protoDir = dir - protoDirIdx = i - - break - } - } - - // Create a slice to hold the proto directories. - protoDirs := make([]string, 0, len(imports)+1) - - // Append the proto directory to the slice. - protoDirs = append(protoDirs, protoDir) - - // Loop through the imports and append each directory to the slice, skipping - // any directories that have already been added. - for i, dir := range imports { - if i == protoDirIdx { - continue - } - - protoDirs = append(protoDirs, dir) - } - - // Return the slice of proto directories. - return protoDirs -} - -// generateProtoc is a function that runs the protoc command with the given -// parameters. -// -// It takes a context.Context and a protocParam struct as parameters. The -// protocParam struct contains the protoPath, output, and imports fields that -// are used to configure the protoc command. -// -// It generates the protoc command arguments and runs the protoc command with -// the given parameters. If there is an error running the protoc command, it -// logs a fatal message. -func generateProtoc(ctx context.Context, param protocParam) { - // Fix the go_package option for each proto file in the protoPath. - param.protoPath = fixGoPackage(ctx, param.protoPath) - - // Get the proto directories based on the protoPath and imports. - protodirs := getProtodirs(ctx, param.protoPath[0], param.imports) - - // Estimate the length of the args slice to prevent expanding it. - args := make([]string, 0, len(protodirs)+len(param.protoPath)+2) //nolint:mnd - - // Append the -I option for each proto directory to the args slice. - for _, dir := range protodirs { - args = append(args, "-I", dir) - } - - // Set the output directory for generated files to $GOPATH/src. - pbOutput := os.Getenv("GOPATH") + "/src" - - // Append the protoPath, --go_out, --go-grpc_out, and --gripmock_out options - // to the args slice. - args = append(args, param.protoPath...) - args = append(args, "--go_out="+pbOutput) - args = append(args, "--go-grpc_out="+pbOutput) - args = append(args, "--gripmock_out="+param.output) - - // Create a new exec.Cmd command with the protoc command and the args. - protoc := exec.Command("protoc", args...) - - // Set the environment variables for the command. - protoc.Env = os.Environ() - - // Set the stdout and stderr for the command. - protoc.Stdout = os.Stdout - protoc.Stderr = os.Stderr - - // Run the protoc command and log a fatal message if there is an error. - if err := protoc.Run(); err != nil { - zerolog.Ctx(ctx).Fatal().Err(err).Msg("fail on protoc") - } -} - -// fixGoPackage is a function that appends the go_package option to each -// proto file in the given protoPaths if the proto file doesn't already have -// one. -// -// It reads each proto file, creates a temporary file with the go_package option, -// and copies the contents of the original file to the temporary file. The -// temporary file is then returned as part of the results. -// -// ctx is the context.Context to use for the function. -// protoPaths is a slice of string paths to the proto files. -// fixGoPackage returns a slice of string paths to the temporary files. -func fixGoPackage(ctx context.Context, protoPaths []string) []string { - results := make([]string, 0, len(protoPaths)) - - for _, protoPath := range protoPaths { - pile, err := os.OpenFile(protoPath, os.O_RDONLY, 0o600) //nolint:mnd - if err != nil { - zerolog.Ctx(ctx).Err(err).Msgf("unable to open protofile %s", protoPath) - - continue - } - - defer pile.Close() - - packageName := "protogen/" + strings.Trim(filepath.Dir(protoPath), "/") - - if err := os.MkdirAll(packageName, 0o666); err != nil { //nolint:mnd - zerolog.Ctx(ctx).Err(err).Msgf("unable to create temp dir %s", protoPath) - - continue - } - - tmp, err := os.Create(filepath.Join(packageName, filepath.Base(protoPath))) - if err != nil { - zerolog.Ctx(ctx).Err(err).Msgf("unable to create temp file %s", protoPath) - - continue - } - - defer tmp.Close() - - if _, err = io.Copy(patcher.NewWriterWrapper(tmp, packageName), pile); err != nil { - zerolog.Ctx(ctx).Err(err).Msgf("unable to copy file %s", protoPath) - - continue - } - - results = append(results, tmp.Name()) - } - - return results -} - -// runGrpcServer runs the gRPC server in a separate process. -// -// ctx is the context.Context to use for the command. -// output is the output directory where the server.go file is located. -// It returns the exec.Cmd object representing the running process, and a channel -// that receives an error when the process exits. -func runGrpcServer(ctx context.Context, output string) (*exec.Cmd, <-chan error) { - // Construct the command to run the gRPC server. - run := exec.CommandContext(ctx, "go", "run", output+"server.go") //nolint:gosec - run.Env = os.Environ() - run.Stdout = os.Stdout - run.Stderr = os.Stderr - - // Start the command. - if err := run.Start(); err != nil { - zerolog.Ctx(ctx).Fatal().Err(err).Msg("unable to start gRPC service") - } - - // Log the process ID. - zerolog.Ctx(ctx).Info().Int("pid", run.Process.Pid).Msg("gRPC-service started") - - // Create a channel to receive the process exit error. - runErr := make(chan error) - - // Start a goroutine to wait for the process to exit and send the error - // to the channel. - go func() { - runErr <- run.Wait() - }() - - return run, runErr + cmd.Execute(context.Background()) } diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index 305f14ad..af52b74d 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -62,7 +62,7 @@ func main() { ) healthcheck := health.NewServer() - healthcheck.SetServingStatus("", healthgrpc.HealthCheckResponse_NOT_SERVING) + healthcheck.SetServingStatus("gripmock", healthgrpc.HealthCheckResponse_NOT_SERVING) {{ range .Services }} {{ template "register_services" . }} @@ -109,7 +109,7 @@ func main() { // If the API call is successful and the response is not nil, // set the gRPC server to SERVING state and log a message. if err == nil && resp.JSON200 != nil { - healthcheck.SetServingStatus("", healthgrpc.HealthCheckResponse_SERVING) + healthcheck.SetServingStatus("gripmock", healthgrpc.HealthCheckResponse_SERVING) builder.Logger().Info().Msg("gRPC server is ready to accept requests")