implement reference grpc server
This commit is contained in:
parent
4bd50b75b9
commit
a69cd3f44c
3
Makefile
3
Makefile
@ -3,8 +3,7 @@ CMD_NAME := demo-app # TODO: Update app name
|
|||||||
.PHONY: all test build docker install clean proto check_protoc
|
.PHONY: all test build docker install clean proto check_protoc
|
||||||
|
|
||||||
VERSION ?= development # Default to "development" if VERSION is not set
|
VERSION ?= development # Default to "development" if VERSION is not set
|
||||||
APIVERSION := v1alpha1
|
API_DIR := api/
|
||||||
API_DIR := api/$(APIVERSION)
|
|
||||||
PROTO_DIRS := $(wildcard proto/demo/app/*) # TODO: Update path (probably not demo)
|
PROTO_DIRS := $(wildcard proto/demo/app/*) # TODO: Update path (probably not demo)
|
||||||
PLATFORMS := linux/amd64 linux/arm64 darwin/amd64 darwin/arm64
|
PLATFORMS := linux/amd64 linux/arm64 darwin/amd64 darwin/arm64
|
||||||
OUTPUT_DIR := bin
|
OUTPUT_DIR := bin
|
||||||
|
24
config.yaml
24
config.yaml
@ -1,21 +1,29 @@
|
|||||||
# yaml-language-server: $schema=contrib/schema.json
|
# yaml-language-server: $schema=contrib/schema.json
|
||||||
|
|
||||||
# Custom demo-app config
|
# Custom demo-app config
|
||||||
demoType:
|
timezone: EST5EDT
|
||||||
optName: demo opt
|
opts:
|
||||||
optField: 99
|
factLang: en
|
||||||
demoField: 99
|
factType: random
|
||||||
|
|
||||||
# go-app config
|
# go-app config
|
||||||
name: Demo go-app HTTP App
|
name: Demo go-app
|
||||||
logging:
|
logging:
|
||||||
format: console
|
format: console
|
||||||
level: trace
|
level: trace
|
||||||
enabled: true
|
enabled: true
|
||||||
otel:
|
otel:
|
||||||
enabled: false
|
enabled: true
|
||||||
grpc:
|
stdoutEnabled: true
|
||||||
enabled: false
|
|
||||||
http:
|
http:
|
||||||
enabled: true
|
enabled: true
|
||||||
listen: :8080
|
listen: :8080
|
||||||
|
logRequests: true
|
||||||
|
grpc:
|
||||||
|
enabled: true
|
||||||
|
enableReflection: true
|
||||||
|
listen: :8081
|
||||||
|
grpcGatewayPath: /api
|
||||||
|
enableGRPCGateway: true
|
||||||
|
enableInstrumentation: true
|
||||||
|
logRequests: true
|
||||||
|
@ -2,10 +2,16 @@
|
|||||||
"definitions": {
|
"definitions": {
|
||||||
"ConfigDemoOpts": {
|
"ConfigDemoOpts": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"optField": {
|
"factLang": {
|
||||||
"type": "integer"
|
"default": "en",
|
||||||
|
"type": "string"
|
||||||
},
|
},
|
||||||
"optName": {
|
"factType": {
|
||||||
|
"default": "random",
|
||||||
|
"enum": [
|
||||||
|
"today",
|
||||||
|
"random"
|
||||||
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -104,12 +110,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"demoField": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"demoType": {
|
|
||||||
"$ref": "#/definitions/ConfigDemoOpts"
|
|
||||||
},
|
|
||||||
"environment": {
|
"environment": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -125,9 +125,16 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"opts": {
|
||||||
|
"$ref": "#/definitions/ConfigDemoOpts"
|
||||||
|
},
|
||||||
"otel": {
|
"otel": {
|
||||||
"$ref": "#/definitions/ConfigOTELConfig"
|
"$ref": "#/definitions/ConfigOTELConfig"
|
||||||
},
|
},
|
||||||
|
"timezone": {
|
||||||
|
"default": "UTC",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
10
go.mod
10
go.mod
@ -4,8 +4,14 @@ go 1.24.1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.libretechconsulting.com/rmcguire/go-app v0.9.2
|
gitea.libretechconsulting.com/rmcguire/go-app v0.9.2
|
||||||
|
github.com/go-resty/resty/v2 v2.16.5
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
golang.org/x/sys v0.31.0
|
golang.org/x/sys v0.31.0
|
||||||
|
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
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -35,7 +41,6 @@ require (
|
|||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
@ -50,8 +55,5 @@ require (
|
|||||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||||
golang.org/x/net v0.38.0 // indirect
|
golang.org/x/net v0.38.0 // indirect
|
||||||
golang.org/x/text v0.23.0 // indirect
|
golang.org/x/text v0.23.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
||||||
google.golang.org/grpc v1.71.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
|
||||||
)
|
)
|
||||||
|
4
go.sum
4
go.sum
@ -24,6 +24,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
|||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||||
|
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
@ -139,3 +141,5 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
|||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
|
||||||
|
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
|
7
main.go
7
main.go
@ -20,9 +20,11 @@ import (
|
|||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/app"
|
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/app"
|
||||||
|
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
|
||||||
optshttp "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
|
optshttp "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
|
||||||
|
|
||||||
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/config"
|
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/config"
|
||||||
|
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/demogrpc"
|
||||||
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/demohttp"
|
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/demohttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,10 +49,15 @@ func main() {
|
|||||||
|
|
||||||
// Prepare servers
|
// Prepare servers
|
||||||
demoHTTP := demohttp.NewDemoHTTPServer(ctx, demoApp)
|
demoHTTP := demohttp.NewDemoHTTPServer(ctx, demoApp)
|
||||||
|
demoGRPC := demogrpc.NewDemoGRPCServer(ctx, demoApp)
|
||||||
|
|
||||||
// Prepare app
|
// Prepare app
|
||||||
app := &app.App{
|
app := &app.App{
|
||||||
AppContext: ctx,
|
AppContext: ctx,
|
||||||
|
GRPC: &opts.AppGRPC{
|
||||||
|
Services: demoGRPC.GetServices(),
|
||||||
|
GRPCDialOpts: demoGRPC.GetDialOpts(),
|
||||||
|
},
|
||||||
HTTP: &optshttp.AppHTTP{
|
HTTP: &optshttp.AppHTTP{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
HealthChecks: demoHTTP.GetHealthCheckFuncs(),
|
HealthChecks: demoHTTP.GetHealthCheckFuncs(),
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DemoConfig struct {
|
type DemoConfig struct {
|
||||||
DemoField int `yaml:"demoField" json:"demoField,omitempty"`
|
Timezone string `yaml:"timezone" json:"timezone,omitempty" default:"UTC"`
|
||||||
DemoType *DemoOpts `yaml:"demoType" json:"demoType,omitempty"`
|
Opts *DemoOpts `yaml:"opts" json:"opts,omitempty"`
|
||||||
|
|
||||||
// Embeds go-app config, used by go-app to
|
// Embeds go-app config, used by go-app to
|
||||||
// merge custom config into go-app config, and to produce
|
// merge custom config into go-app config, and to produce
|
||||||
@ -18,6 +18,13 @@ type DemoConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DemoOpts struct {
|
type DemoOpts struct {
|
||||||
OptField int `yaml:"optField" json:"optField,omitempty"`
|
FactLang string `yaml:"factLang" json:"factLang,omitempty" default:"en"`
|
||||||
OptName string `yaml:"optName" json:"optName,omitempty"`
|
FactType FactType `yaml:"factType" json:"factType,omitempty" default:"random" enum:"today,random"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FactType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeToday FactType = "today"
|
||||||
|
TypeRandom FactType = "random"
|
||||||
|
)
|
||||||
|
86
pkg/demogrpc/demo.go
Normal file
86
pkg/demogrpc/demo.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package demogrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
|
grpccodes "google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
|
||||||
|
|
||||||
|
pb "gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/api/demo/app/v1alpha1"
|
||||||
|
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultFactType = config.TypeRandom
|
||||||
|
factsBaseURI = "https://uselessfacts.jsph.pl/api/v2/facts"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DemoGRPCServer struct {
|
||||||
|
tracer trace.Tracer
|
||||||
|
ctx context.Context
|
||||||
|
cfg *config.DemoConfig
|
||||||
|
client *resty.Client
|
||||||
|
pb.UnimplementedDemoAppServiceServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDemoGRPCServer(ctx context.Context, cfg *config.DemoConfig) *DemoGRPCServer {
|
||||||
|
if cfg.Opts == nil {
|
||||||
|
cfg.Opts = &config.DemoOpts{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Opts.FactType == "" {
|
||||||
|
cfg.Opts.FactType = config.TypeRandom
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DemoGRPCServer{
|
||||||
|
ctx: ctx,
|
||||||
|
cfg: cfg,
|
||||||
|
tracer: otel.GetTracer(ctx, "demoGRPCServer"),
|
||||||
|
client: resty.New().SetBaseURL(factsBaseURI),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DemoGRPCServer) GetDemo(ctx context.Context, req *pb.GetDemoRequest) (
|
||||||
|
*pb.GetDemoResponse, error,
|
||||||
|
) {
|
||||||
|
ctx, span := d.tracer.Start(ctx, "getDemo", trace.WithAttributes(
|
||||||
|
attribute.String("language", req.GetLanguage()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
r := d.client.R().SetContext(ctx)
|
||||||
|
|
||||||
|
if req.GetLanguage() != "" {
|
||||||
|
r = r.SetQueryParam("language", req.GetLanguage())
|
||||||
|
}
|
||||||
|
|
||||||
|
fact := new(RandomFact)
|
||||||
|
r.SetResult(fact)
|
||||||
|
|
||||||
|
resp, err := r.Get(fmt.Sprintf("/%s", d.cfg.Opts.FactType))
|
||||||
|
if err != nil {
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
span.RecordError(err)
|
||||||
|
|
||||||
|
zerolog.Ctx(d.ctx).Err(err).Send()
|
||||||
|
|
||||||
|
return nil, status.Errorf(grpccodes.Unknown, "%s: %s",
|
||||||
|
err.Error(), string(resp.Body()))
|
||||||
|
}
|
||||||
|
|
||||||
|
span.SetAttributes(attribute.String("fact", fact.Text))
|
||||||
|
span.SetStatus(codes.Ok, "")
|
||||||
|
|
||||||
|
zerolog.Ctx(d.ctx).Debug().Str("fact", fact.Text).Msg("retrieved fact")
|
||||||
|
|
||||||
|
return fact.FactToProto(), nil
|
||||||
|
}
|
25
pkg/demogrpc/fact.go
Normal file
25
pkg/demogrpc/fact.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package demogrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
|
pb "gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/api/demo/app/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RandomFact struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Text string `json:"text,omitempty"`
|
||||||
|
Source string `json:"source,omitempty"`
|
||||||
|
SourceURL string `json:"source_url,omitempty"`
|
||||||
|
Language string `json:"language,omitempty"`
|
||||||
|
Permalink string `json:"permalink,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RandomFact) FactToProto() *pb.GetDemoResponse {
|
||||||
|
return &pb.GetDemoResponse{
|
||||||
|
Timestamp: timestamppb.Now(),
|
||||||
|
Fact: f.Text,
|
||||||
|
Source: f.SourceURL,
|
||||||
|
Language: f.Language,
|
||||||
|
}
|
||||||
|
}
|
30
pkg/demogrpc/server.go
Normal file
30
pkg/demogrpc/server.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package demogrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
|
||||||
|
demoAppPb "gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/api/demo/app/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ds *DemoGRPCServer) GetDialOpts() []grpc.DialOption {
|
||||||
|
return []grpc.DialOption{
|
||||||
|
// NOTE: Necessary for grpc-gateway to connect to grpc server
|
||||||
|
// Update if grpc service has credentials, tls, etc..
|
||||||
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds *DemoGRPCServer) GetServices() []*opts.GRPCService {
|
||||||
|
return []*opts.GRPCService{
|
||||||
|
{
|
||||||
|
Name: "Demo App",
|
||||||
|
Type: &demoAppPb.DemoAppService_ServiceDesc,
|
||||||
|
Service: ds,
|
||||||
|
GwRegistrationFuncs: []opts.GwRegistrationFunc{
|
||||||
|
demoAppPb.RegisterDemoAppServiceHandler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user