6 Commits

8 changed files with 60 additions and 17 deletions

View File

@ -1,10 +1,12 @@
# TODO
- [ ] Finish implementing GRPC service support
- [ ] Expand config test case to cover GRPC config
- [ ] Expand tracing
## Done
- [x] Test and troubleshoot GRPC Gateway supprot
- [x] Finish implementing GRPC service support
- [x] Unit tests
- [x] Pattern for extending config
- [x] HTTP Logging Middleware

View File

@ -44,13 +44,11 @@ func (a *App) MustRun() {
// Prepare GRPC first. The GRPC server may update its opts
// with a prepared GRPC-Gateway runtime.ServeMux if any of its services
// have added GRPC-Gateway handlers. If present, serve under api path
if a.cfg.GRPCEnabled() {
a.runGRPC(ctx)
}
a.runGRPC(ctx)
if a.cfg.HTTPEnabled() {
a.runHTTP(ctx)
}
// Second, prepare and run HTTP server, embedding grpc-gateway
// runtime.ServeMux if set by runGRPC()
a.runHTTP(ctx)
// Monitor app lifecycle
go a.monitor()

View File

@ -3,14 +3,26 @@ package app
import (
"context"
"github.com/rs/zerolog"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
optsgrpc "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
optshttp "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
)
func (a *App) runGRPC(ctx context.Context) {
if a.GRPC == nil {
a.GRPC = &optsgrpc.AppGRPC{
GRPCDone: make(chan error),
}
}
if !a.cfg.GRPCEnabled() {
zerolog.Ctx(ctx).Info().Msg("skipping disabled GRPC server")
return
}
span := trace.SpanFromContext(ctx)
if err := a.initGRPC(ctx); err != nil {
span.RecordError(err)
@ -21,10 +33,10 @@ func (a *App) runGRPC(ctx context.Context) {
if a.GRPC.GetGatewayMux() != nil {
if a.HTTP.Handlers == nil {
a.HTTP.Handlers = make([]opts.HTTPHandler, 0, 1)
a.HTTP.Handlers = make([]optshttp.HTTPHandler, 0, 1)
}
a.HTTP.Handlers = append(a.HTTP.Handlers, opts.HTTPHandler{
a.HTTP.Handlers = append(a.HTTP.Handlers, optshttp.HTTPHandler{
Prefix: a.cfg.GRPC.GRPCGatewayPath,
StripPrefix: true,
Handler: a.GRPC.GetGatewayMux(),

View File

@ -3,12 +3,26 @@ package app
import (
"context"
"github.com/rs/zerolog"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
optshttp "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
)
func (a *App) runHTTP(ctx context.Context) {
if a.HTTP == nil {
a.HTTP = &optshttp.AppHTTP{
HTTPDone: make(chan any),
}
}
if !a.cfg.HTTPEnabled() {
zerolog.Ctx(ctx).Info().Msg("skipping disabled HTTP Server")
return
}
span := trace.SpanFromContext(ctx)
if err := a.initHTTP(ctx); err != nil {
span.RecordError(err)

View File

@ -2,6 +2,7 @@ package grpc
import (
"context"
"errors"
"fmt"
"net"
@ -18,7 +19,7 @@ func (a *appGRPCServer) registerServiceGatewayHandlers(ctx context.Context, serv
if len(service.GwRegistrationFuncs) < 1 {
return
} else if a.gatewayMux == nil {
a.gatewayMux = runtime.NewServeMux()
a.gatewayMux = runtime.NewServeMux(a.opts.GRPCGatewayOpts...)
}
ctx, span := a.tracer.Start(ctx, "appgrpc.init.prepare.service.gwHandlers", trace.WithAttributes(
@ -29,8 +30,15 @@ func (a *appGRPCServer) registerServiceGatewayHandlers(ctx context.Context, serv
clientConn := a.GetClientConn(ctx)
var errs error
for _, registerGW := range service.GwRegistrationFuncs {
registerGW(ctx, a.gatewayMux, clientConn)
errs = errors.Join(errs, registerGW(ctx, a.gatewayMux, clientConn))
a.logger.Debug().Any("fwo", a.gatewayMux.GetForwardResponseOptions()).
Msg("calling gateway registration func")
}
if errs != nil {
panic(errs)
}
}
@ -50,6 +58,7 @@ func (a *appGRPCServer) GetClientConn(ctx context.Context) *grpc.ClientConn {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
panic(err)
}
return clientConn

View File

@ -10,8 +10,8 @@ import (
)
type GRPCOpts struct {
*config.GRPCConfig
*AppGRPC
*config.GRPCConfig // Settings configurable by env or yaml
*AppGRPC // Settings provided in code
}
type AppGRPC struct {
@ -19,7 +19,8 @@ type AppGRPC struct {
UnaryInterceptors []grpc.UnaryServerInterceptor
StreamInterceptors []grpc.StreamServerInterceptor
GRPCOpts []grpc.ServerOption
GRPCDialOpts []grpc.DialOption // Map ServerOptions to DialOpts for GRPC Gateway support
GRPCDialOpts []grpc.DialOption // Map ServerOptions to DialOpts for GRPC Gateway support
GRPCGatewayOpts []runtime.ServeMuxOption // Configure grpc-gateway ServeMux
GRPCDone <-chan error
gatewayMux *runtime.ServeMux
}

View File

@ -8,7 +8,6 @@ import (
semconv "go.opentelemetry.io/otel/semconv/v1.27.0"
)
// TODO: Implement
func (a *appGRPCServer) runGRPCServer(spanCtx context.Context) error {
_, span := a.tracer.Start(spanCtx, "appgrpc.init.start")
defer span.End()

View File

@ -7,6 +7,7 @@ import (
"net"
"net/http"
"os"
"strings"
"time"
"github.com/prometheus/client_golang/prometheus/promhttp"
@ -60,8 +61,15 @@ func prepHTTPServer(opts *opts.AppHTTP) *http.Server {
// Inject extra handlers if given
// Used for grpc-gateway runtime.ServeMux handlers
for _, h := range opts.Handlers {
// prefix must end in / to route sub-paths
if !strings.HasSuffix(h.Prefix, "/") {
h.Prefix = h.Prefix + "/"
}
// if enabled, the path prefix is stripped before
// requests are sent to the handler
if h.StripPrefix {
h.Handler = http.StripPrefix(h.Prefix, h.Handler)
h.Handler = http.StripPrefix(h.Prefix[:len(h.Prefix)-1], h.Handler)
}
mux.Handle(h.Prefix, h.Handler)
}