-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #340 from bavix/cobra
[3.x] refactoring v3. cobra
- Loading branch information
Showing
31 changed files
with
738 additions
and
362 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
Oops, something went wrong.