diff --git a/.gitignore b/.gitignore index 46d179e..440d36f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ .viminfo .local/ .vscode/ +.bash_history +.viminfo diff --git a/grpcserver/grpcserver.go b/grpcserver/grpcserver.go index 7a5da06..54e82ca 100644 --- a/grpcserver/grpcserver.go +++ b/grpcserver/grpcserver.go @@ -13,7 +13,6 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/reflection" - env "github.com/datatrails/go-datatrails-common/environment" "github.com/datatrails/go-datatrails-common/grpchealth" grpcHealth "google.golang.org/grpc/health/grpc_health_v1" ) @@ -26,7 +25,7 @@ type RegisterServer func(*grpcServer) func defaultRegisterServer(g *grpcServer) {} -type GRPCServer struct { +type Server struct { name string log Logger listenStr string @@ -38,34 +37,34 @@ type GRPCServer struct { reflection bool } -type GRPCServerOption func(*GRPCServer) +type ServerOption func(*Server) -func WithAppendedInterceptor(i grpcUnaryServerInterceptor) GRPCServerOption { - return func(g *GRPCServer) { +func WithAppendedInterceptor(i grpcUnaryServerInterceptor) ServerOption { + return func(g *Server) { g.interceptors = append(g.interceptors, i) } } -func WithPrependedInterceptor(i grpcUnaryServerInterceptor) GRPCServerOption { - return func(g *GRPCServer) { +func WithPrependedInterceptor(i grpcUnaryServerInterceptor) ServerOption { + return func(g *Server) { g.interceptors = append([]grpcUnaryServerInterceptor{i}, g.interceptors...) } } -func WithRegisterServer(r RegisterServer) GRPCServerOption { - return func(g *GRPCServer) { +func WithRegisterServer(r RegisterServer) ServerOption { + return func(g *Server) { g.register = r } } -func WithoutHealth() GRPCServerOption { - return func(g *GRPCServer) { +func WithoutHealth() ServerOption { + return func(g *Server) { g.health = false } } -func WithReflection(r bool) GRPCServerOption { - return func(g *GRPCServer) { +func WithReflection(r bool) ServerOption { + return func(g *Server) { g.reflection = r } } @@ -77,21 +76,21 @@ func tracingFilter(ctx context.Context, fullMethodName string) bool { return true } -// New creates a new GRPCServer that is bound to a specific GRPC API. This object complies with +// New creates a new Server that is bound to a specific GRPC API. This object complies with // the standard Listener service and can be managed by the startup.Listeners object. -func New(log Logger, name string, opts ...GRPCServerOption) GRPCServer { - listenStr := fmt.Sprintf(":%s", env.GetOrFatal("PORT")) - - g := GRPCServer{ - name: strings.ToLower(name), - listenStr: listenStr, - register: defaultRegisterServer, - interceptors: []grpc.UnaryServerInterceptor{ - grpc_otrace.UnaryServerInterceptor(grpc_otrace.WithFilterFunc(tracingFilter)), - grpc_validator.UnaryServerInterceptor(), - }, - health: true, +func New(log Logger, name string, port string, opts ...ServerOption) *Server { + var g Server + + listenStr := fmt.Sprintf(":%s", port) + + g.name = strings.ToLower(name) + g.listenStr = listenStr + g.register = defaultRegisterServer + g.interceptors = []grpc.UnaryServerInterceptor{ + grpc_otrace.UnaryServerInterceptor(grpc_otrace.WithFilterFunc(tracingFilter)), + grpc_validator.UnaryServerInterceptor(), } + g.health = true for _, opt := range opts { opt(&g) } @@ -115,15 +114,15 @@ func New(log Logger, name string, opts ...GRPCServerOption) GRPCServer { g.server = server g.log = log.WithIndex("grpcserver", g.String()) - return g + return &g } -func (g *GRPCServer) String() string { +func (g *Server) String() string { // No logging in this method please. return fmt.Sprintf("%s%s", g.name, g.listenStr) } -func (g *GRPCServer) Listen() error { +func (g *Server) Listen() error { listen, err := net.Listen("tcp", g.listenStr) if err != nil { return fmt.Errorf("failed to listen %s: %w", g, err) @@ -141,7 +140,7 @@ func (g *GRPCServer) Listen() error { return nil } -func (g *GRPCServer) Shutdown(_ context.Context) error { +func (g *Server) Shutdown(_ context.Context) error { g.log.Infof("Shutdown") if g.healthService != nil { g.healthService.NotReady() // readiness diff --git a/httpserver/httpserver.go b/httpserver/httpserver.go index a5624e6..af8c525 100644 --- a/httpserver/httpserver.go +++ b/httpserver/httpserver.go @@ -54,19 +54,16 @@ func WithOptionalHandlers(handlers ...HandleChainFunc) ServerOption { // New creates a new httpserver. func New(log Logger, name string, port string, h http.Handler, opts ...ServerOption) *Server { - s := Server{ - server: http.Server{ - Addr: ":" + port, - }, - handler: h, - name: strings.ToLower(name), + var s Server + s.server = http.Server{ + Addr: ":" + port, } + s.handler = h + s.name = strings.ToLower(name) s.log = log.WithIndex("httpserver", s.String()) for _, opt := range opts { opt(&s) } - // It is preferable to return a copy rather than a reference. Unfortunately http.Server has an - // internal mutex and this cannot or should not be copied so we will return a reference instead. return &s } diff --git a/metrics/httpserver.go b/metrics/httpserver.go new file mode 100644 index 0000000..c88c786 --- /dev/null +++ b/metrics/httpserver.go @@ -0,0 +1,28 @@ +package metrics + +import ( + "fmt" + + "github.com/datatrails/go-datatrails-common/httpserver" +) + +type HTTPServer struct { + *httpserver.Server + metrics *Metrics +} + +func NewServer(log Logger, serviceName string, port string, opts ...MetricsOption) HTTPServer { + m := New( + log, + serviceName, + port, + ) + return HTTPServer{ + httpserver.New(log, fmt.Sprintf("metrics %s", serviceName), port, m.newPromHandler()), + m, + } +} + +func (h *HTTPServer) Metrics() *Metrics { + return h.metrics +} diff --git a/metrics/metrics.go b/metrics/metrics.go index 32691b8..2ac28b8 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -121,16 +121,9 @@ func (m *Metrics) Register(cs ...prometheus.Collector) { m.registry.MustRegister(cs...) } -func (m *Metrics) Port() string { - if m != nil { - return m.port - } - return "" -} - // NewPromHandler - this handler is used on the endpoint that serves metrics endpoint // which is provided on a different port to the service. // The default InstrumentMetricHandler is suppressed. -func (m *Metrics) NewPromHandler() http.Handler { +func (m *Metrics) newPromHandler() http.Handler { return promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{}) } diff --git a/restproxyserver/restproxyserver.go b/restproxyserver/restproxyserver.go index 046a2af..335ffa8 100644 --- a/restproxyserver/restproxyserver.go +++ b/restproxyserver/restproxyserver.go @@ -8,7 +8,6 @@ import ( "reflect" "strings" - env "github.com/datatrails/go-datatrails-common/environment" "github.com/datatrails/go-datatrails-common/httpserver" "github.com/datatrails/go-datatrails-common/tracing" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" @@ -33,7 +32,7 @@ type HeaderMatcherFunc = runtime.HeaderMatcherFunc type ErrorHandlerFunc = runtime.ErrorHandlerFunc type DialOption = grpc.DialOption -type RegisterRESTProxyServer func(context.Context, *ServeMux, string, []DialOption) error +type RegisterServer func(context.Context, *ServeMux, string, []DialOption) error type HandleChainFunc = httpserver.HandleChainFunc @@ -43,8 +42,8 @@ type filePath struct { fileHandler func(http.ResponseWriter, *http.Request, map[string]string) } -// RESTProxyServer represents the grpc-gateway rest serve endpoint. -type RESTProxyServer struct { +// Server represents the grpc-gateway rest serve endpoint. +type Server struct { name string port string log Logger @@ -54,30 +53,30 @@ type RESTProxyServer struct { options []runtime.ServeMuxOption filePaths []filePath handlers []HandleChainFunc - registers []RegisterRESTProxyServer + registers []RegisterServer mux *runtime.ServeMux server *httpserver.Server } -type RESTProxyServerOption func(*RESTProxyServer) +type ServerOption func(*Server) // WithMarshaler specifies an optional marshaler. -func WithMarshaler(mime string, m Marshaler) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithMarshaler(mime string, m Marshaler) ServerOption { + return func(g *Server) { g.options = append(g.options, runtime.WithMarshalerOption(mime, m)) } } // SetQueryParameterParser adds an intercepror that matches header values. -func SetQueryParameterParser(p QueryParameterParser) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func SetQueryParameterParser(p QueryParameterParser) ServerOption { + return func(g *Server) { g.options = append(g.options, runtime.SetQueryParameterParser(p)) } } // WithIncomingHeaderMatcher adds an intercepror that matches header values. -func WithIncomingHeaderMatcher(o HeaderMatcherFunc) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithIncomingHeaderMatcher(o HeaderMatcherFunc) ServerOption { + return func(g *Server) { if o != nil && !reflect.ValueOf(o).IsNil() { g.options = append(g.options, runtime.WithIncomingHeaderMatcher(o)) } @@ -85,8 +84,8 @@ func WithIncomingHeaderMatcher(o HeaderMatcherFunc) RESTProxyServerOption { } // WithOutgoingHeaderMatcher matches header values on output. Nil argument is ignored. -func WithOutgoingHeaderMatcher(o HeaderMatcherFunc) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithOutgoingHeaderMatcher(o HeaderMatcherFunc) ServerOption { + return func(g *Server) { if o != nil && !reflect.ValueOf(o).IsNil() { g.options = append(g.options, runtime.WithOutgoingHeaderMatcher(o)) } @@ -94,8 +93,8 @@ func WithOutgoingHeaderMatcher(o HeaderMatcherFunc) RESTProxyServerOption { } // WithErrorHandler adds error handling in special cases - e.g on 402 or 429. Nil argument is ignored. -func WithErrorHandler(o ErrorHandlerFunc) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithErrorHandler(o ErrorHandlerFunc) ServerOption { + return func(g *Server) { if o != nil && !reflect.ValueOf(o).IsNil() { g.options = append(g.options, runtime.WithErrorHandler(o)) } @@ -103,24 +102,25 @@ func WithErrorHandler(o ErrorHandlerFunc) RESTProxyServerOption { } // WithGRPCAddress - overides the defaultGRPCAddress ('localhost:') -func WithGRPCAddress(a string) RESTProxyServerOption { - return func(g *RESTProxyServer) { +// NB: will be removed +func WithGRPCAddress(a string) ServerOption { + return func(g *Server) { g.grpcAddress = a } } // WithRegisterHandlers adds grpc-gateway handlers. A nil value will emit an // error from the Listen() method. -func WithRegisterHandlers(registerers ...RegisterRESTProxyServer) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithRegisterHandlers(registerers ...RegisterServer) ServerOption { + return func(g *Server) { g.registers = append(g.registers, registerers...) } } // WithOptionalRegisterHandler adds grpc-gateway handlers. A nil value will be // ignored. -func WithOptionalRegisterHandlers(registerers ...RegisterRESTProxyServer) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithOptionalRegisterHandlers(registerers ...RegisterServer) ServerOption { + return func(g *Server) { for i := 0; i < len(registerers); i++ { registerer := registerers[i] if registerer != nil && !reflect.ValueOf(registerer).IsNil() { @@ -132,16 +132,16 @@ func WithOptionalRegisterHandlers(registerers ...RegisterRESTProxyServer) RESTPr // WithHTTPHandlers adds handlers on the http endpoint. A nil value will // return an error on executiong Listen() -func WithHTTPHandlers(handlers ...HandleChainFunc) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithHTTPHandlers(handlers ...HandleChainFunc) ServerOption { + return func(g *Server) { g.handlers = append(g.handlers, handlers...) } } // WithOptionalHTTPHandlers adds handlers on the http endpoint. A nil value will // be ignored. -func WithOptionalHTTPHandlers(handlers ...HandleChainFunc) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithOptionalHTTPHandlers(handlers ...HandleChainFunc) ServerOption { + return func(g *Server) { for i := 0; i < len(handlers); i++ { handler := handlers[i] if handler != nil && !reflect.ValueOf(handler).IsNil() { @@ -152,22 +152,22 @@ func WithOptionalHTTPHandlers(handlers ...HandleChainFunc) RESTProxyServerOption } // WithAppendedDialOption appends a grpc dial option. -func WithAppendedDialOption(d DialOption) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithAppendedDialOption(d DialOption) ServerOption { + return func(g *Server) { g.dialOptions = append(g.dialOptions, d) } } // WithPrependedDialOption prepends a grpc dial option. -func WithPrependedDialOption(d DialOption) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithPrependedDialOption(d DialOption) ServerOption { + return func(g *Server) { g.dialOptions = append([]DialOption{d}, g.dialOptions...) } } // WithHandlePath add REST file path handler. -func WithHandlePath(verb string, urlPath string, f func(http.ResponseWriter, *http.Request, map[string]string)) RESTProxyServerOption { - return func(g *RESTProxyServer) { +func WithHandlePath(verb string, urlPath string, f func(http.ResponseWriter, *http.Request, map[string]string)) ServerOption { + return func(g *Server) { g.filePaths = append( g.filePaths, filePath{ @@ -179,42 +179,48 @@ func WithHandlePath(verb string, urlPath string, f func(http.ResponseWriter, *ht } } -// New creates a new RESTProxyServer that is bound to a specific GRPC Gateway API. This object complies with +// New creates a new Server that is bound to a specific GRPC Gateway API. This object complies with // the standard Listener interface and can be managed by the startup.Listeners object. -func New(log Logger, name string, port string, opts ...RESTProxyServerOption) RESTProxyServer { - - g := RESTProxyServer{ - name: strings.ToLower(name), - port: port, - dialOptions: tracing.GRPCDialTracingOptions(), - options: []runtime.ServeMuxOption{}, - filePaths: []filePath{}, - handlers: []HandleChainFunc{}, - registers: []RegisterRESTProxyServer{}, - } - g.log = log.WithIndex("restproxyserver", g.String()) +func New(log Logger, name string, port string, opts ...ServerOption) *Server { + var g Server + return new_(&g, log, name, port, opts...) +} + +// function outlining +func new_(g *Server, log Logger, name string, port string, opts ...ServerOption) *Server { + + g.name = strings.ToLower(name) + g.port = port + g.dialOptions = tracing.GRPCDialTracingOptions() + g.options = []runtime.ServeMuxOption{} + g.filePaths = []filePath{} + g.handlers = []HandleChainFunc{} + g.registers = []RegisterServer{} + for _, opt := range opts { - opt(&g) + opt(g) } if g.grpcAddress == "" { - port := env.GetOrFatal("PORT") g.grpcAddress = fmt.Sprintf("localhost:%s", port) } g.mux = runtime.NewServeMux(g.options...) + g.log = log.WithIndex("restproxyserver", g.name) return g } -func (g *RESTProxyServer) String() string { +func (g *Server) String() string { // No logging in this method please. return fmt.Sprintf("%s:%s", g.name, g.port) } -func (g *RESTProxyServer) Listen() error { +func (g *Server) Listen() error { + + var err error for _, p := range g.filePaths { - err := g.mux.HandlePath(p.verb, p.urlPath, p.fileHandler) + err = g.mux.HandlePath(p.verb, p.urlPath, p.fileHandler) if err != nil { return fmt.Errorf("cannot handle path %s: %w", p.urlPath, err) } @@ -227,7 +233,7 @@ func (g *RESTProxyServer) Listen() error { if reflect.ValueOf(register).IsNil() { return ErrNilRegistererValue } - err := register(context.Background(), g.mux, g.grpcAddress, g.dialOptions) + err = register(context.Background(), g.mux, g.grpcAddress, g.dialOptions) if err != nil { return err } @@ -245,7 +251,7 @@ func (g *RESTProxyServer) Listen() error { return g.server.Listen() } -func (g *RESTProxyServer) Shutdown(ctx context.Context) error { +func (g *Server) Shutdown(ctx context.Context) error { g.log.Infof("Shutdown") return g.server.Shutdown(ctx) }