From f201ac1fca62fb601477822f7b46515d56e61f1c Mon Sep 17 00:00:00 2001 From: Ryan D McGuire Date: Tue, 25 Mar 2025 17:05:59 -0400 Subject: [PATCH] support runtime.ServeMux opts for grpc-gateway --- app.go | 10 ++++-- go.mod | 6 ++-- go.sum | 6 ++++ pkg/ambient/ambient.go | 12 +++++++ pkg/ambient/app.go | 33 +++++++++++++++++++ pkg/weather/recorder/recorder.go | 4 +++ .../recorder/recorders/memory/memory.go | 5 +++ pkg/weather/recorder/recorders/noop/noop.go | 2 ++ pkg/weather/recorder/recorders/recorders.go | 1 + pkg/weather/recorder/recorders/redis/redis.go | 5 +++ 10 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 pkg/ambient/app.go diff --git a/app.go b/app.go index 02d790e..0060e39 100644 --- a/app.go +++ b/app.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "gitea.libretechconsulting.com/rmcguire/go-app/pkg/app" grpcopts "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts" @@ -38,11 +39,13 @@ func prepareApp(ctx context.Context, aw *ambient.AmbientWeather) *app.App { CustomListener: ambienthttp.NewAWNMutatingListener(ctx, aw.Config.HTTP.Listen), // Necessary to fix certain bad AWN firmware - // Health check funcs HealthChecks: []httpopts.HealthCheckFunc{ - // TODO: Implement + // Check recorder health + // Only redis implements this func(ctx context.Context) error { - return nil + var errs error + errs = errors.Join(errs, aw.GetRecorder().Ping(ctx)) + return errs }, }, }, @@ -52,6 +55,7 @@ func prepareApp(ctx context.Context, aw *ambient.AmbientWeather) *app.App { GRPCDialOpts: []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), }, + GRPCGatewayOpts: aw.GetGatewayOpts(), Services: []*grpcopts.GRPCService{ { Name: "Weather Service", diff --git a/go.mod b/go.mod index ff1482c..95c6bcc 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module gitea.libretechconsulting.com/rmcguire/ambient-local-exporter go 1.24.1 require ( - gitea.libretechconsulting.com/rmcguire/go-app v0.8.1 + gitea.libretechconsulting.com/rmcguire/go-app v0.9.0 github.com/go-resty/resty/v2 v2.16.5 github.com/gorilla/schema v1.4.1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 @@ -13,7 +13,7 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 go.opentelemetry.io/otel/trace v1.35.0 golang.org/x/sys v0.31.0 - google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 + google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 google.golang.org/grpc v1.71.0 google.golang.org/protobuf v1.36.6 k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e @@ -57,7 +57,7 @@ require ( golang.org/x/net v0.37.0 // indirect golang.org/x/text v0.23.0 // indirect google.golang.org/genproto v0.0.0-20250313205543-e70fdf4c4cb4 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1a23a68..661ac71 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ gitea.libretechconsulting.com/rmcguire/go-app v0.8.0 h1:PC8vlpr8tLoRwIHsq5nvOYzz gitea.libretechconsulting.com/rmcguire/go-app v0.8.0/go.mod h1:W6YHFSGf4nJrgs9DqEaw+3J6ufIARsr1zpOs/V6gRTQ= gitea.libretechconsulting.com/rmcguire/go-app v0.8.1 h1:pnmFJnY77dsh84zvVcHqFuQgkxy3hjCQ8tFRGv/aJOw= gitea.libretechconsulting.com/rmcguire/go-app v0.8.1/go.mod h1:W6YHFSGf4nJrgs9DqEaw+3J6ufIARsr1zpOs/V6gRTQ= +gitea.libretechconsulting.com/rmcguire/go-app v0.9.0 h1:ZOBdVk2EehGCX6K3pdT5Dy07bMiUR7VTifE+n0ODfak= +gitea.libretechconsulting.com/rmcguire/go-app v0.9.0/go.mod h1:W6YHFSGf4nJrgs9DqEaw+3J6ufIARsr1zpOs/V6gRTQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -204,8 +206,12 @@ google.golang.org/genproto v0.0.0-20250313205543-e70fdf4c4cb4 h1:kCjWYliqPA8g5z8 google.golang.org/genproto v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:SqIx1NV9hcvqdLHo7uNZDS5lrUJybQ3evo3+z/WBfA0= google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 h1:IFnXJq3UPB3oBREOodn1v1aGQeZYQclEmvWRMN0PSsY= google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg= +google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= +google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= diff --git a/pkg/ambient/ambient.go b/pkg/ambient/ambient.go index 8855358..d1e1178 100644 --- a/pkg/ambient/ambient.go +++ b/pkg/ambient/ambient.go @@ -318,6 +318,18 @@ func (aw *AmbientWeather) enrichStation(update *weather.WeatherUpdate) { } } +func (aw *AmbientWeather) GetLogger() *zerolog.Logger { + aw.RLock() + defer aw.RUnlock() + return aw.l +} + +func (aw *AmbientWeather) GetCtx() context.Context { + aw.RLock() + defer aw.RUnlock() + return aw.appCtx +} + func (aw *AmbientWeather) GetRecorder() *recorder.WeatherRecorder { aw.RLock() defer aw.RUnlock() diff --git a/pkg/ambient/app.go b/pkg/ambient/app.go new file mode 100644 index 0000000..bbab148 --- /dev/null +++ b/pkg/ambient/app.go @@ -0,0 +1,33 @@ +package ambient + +import ( + "context" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" +) + +func (aw *AmbientWeather) GetGatewayOpts() []runtime.ServeMuxOption { + return []runtime.ServeMuxOption{ + runtime.WithMiddlewares(func(hf runtime.HandlerFunc) runtime.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) { + aw.GetLogger().Trace(). + Any("pathParams", pathParams). + Msg("inbound request called") + } + }), + runtime.WithRoutingErrorHandler( + func( + ctx context.Context, mux *runtime.ServeMux, mshl runtime.Marshaler, + w http.ResponseWriter, r *http.Request, code int, + ) { + aw.GetLogger().Trace(). + Int("code", code). + Str("pattern", r.Pattern). + Msg("requesting grpc-gateway route") + + // Pass back to default + runtime.DefaultRoutingErrorHandler(ctx, mux, mshl, w, r, code) + }), + } +} diff --git a/pkg/weather/recorder/recorder.go b/pkg/weather/recorder/recorder.go index f818b9e..d8464d4 100644 --- a/pkg/weather/recorder/recorder.go +++ b/pkg/weather/recorder/recorder.go @@ -29,6 +29,10 @@ type Opts struct { KeepLast int } +func (r *WeatherRecorder) Ping(ctx context.Context) error { + return r.recorder.Ping(ctx) +} + func MustNewWeatherRecorder(opts *Opts) *WeatherRecorder { if opts.KeepLast < 1 { opts.KeepLast = 1 diff --git a/pkg/weather/recorder/recorders/memory/memory.go b/pkg/weather/recorder/recorders/memory/memory.go index 9c733c8..6f21e94 100644 --- a/pkg/weather/recorder/recorders/memory/memory.go +++ b/pkg/weather/recorder/recorders/memory/memory.go @@ -25,6 +25,11 @@ type MemoryRecorder struct { *sync.RWMutex } +// No meaningful health check to do here +func (r *MemoryRecorder) Ping(ctx context.Context) error { + return nil +} + func (r *MemoryRecorder) Init(ctx context.Context, opts *recorders.RecorderOpts) { if opts.RetainLast < 1 { opts.RetainLast = DEF_RETAIN_LAST diff --git a/pkg/weather/recorder/recorders/noop/noop.go b/pkg/weather/recorder/recorders/noop/noop.go index a3758b3..07e00d8 100644 --- a/pkg/weather/recorder/recorders/noop/noop.go +++ b/pkg/weather/recorder/recorders/noop/noop.go @@ -20,4 +20,6 @@ func (n *NoopRecorder) Get(context.Context, *pb.GetWeatherRequest) ([]*weather.W func (n *NoopRecorder) Count(context.Context) int { return 0 } +func (n *NoopRecorder) Ping(context.Context) error { return nil } + func (r *NoopRecorder) Name() string { return "no-op recorder" } diff --git a/pkg/weather/recorder/recorders/recorders.go b/pkg/weather/recorder/recorders/recorders.go index 36bf50e..94b67ef 100644 --- a/pkg/weather/recorder/recorders/recorders.go +++ b/pkg/weather/recorder/recorders/recorders.go @@ -19,5 +19,6 @@ type Recorder interface { Set(context.Context, *weather.WeatherUpdate) error Get(context.Context, *pb.GetWeatherRequest) ([]*weather.WeatherUpdate, error) Count(context.Context) int // Best Effort + Ping(context.Context) error Name() string } diff --git a/pkg/weather/recorder/recorders/redis/redis.go b/pkg/weather/recorder/recorders/redis/redis.go index 13b4414..17397b3 100644 --- a/pkg/weather/recorder/recorders/redis/redis.go +++ b/pkg/weather/recorder/recorders/redis/redis.go @@ -36,6 +36,11 @@ type RedisRecorder struct { *sync.RWMutex } +func (r *RedisRecorder) Ping(ctx context.Context) error { + _, err := r.redis.Ping(ctx).Result() + return err +} + func (r *RedisRecorder) Init(ctx context.Context, opts *recorders.RecorderOpts) { if opts.AppConfig.RecorderConfig.RedisConfig == nil { panic("refusing to init redis recorder with no redisConfig")