Fix routes having the same method, update docs, update build
continuous-integration/drone/tag Build is passing Details

master v0.2.3
René Jochum 2 years ago
parent d7f4170aab
commit fd1fdb8b4f
Signed by: jochum
GPG Key ID: F7D906F5E51E8E5E

@ -24,5 +24,6 @@ steps:
dockerfile: ./docker/router/Dockerfile
repo: registry.fk.jochum.dev/jo-micro/router
build_args:
- DOCKER_IO=registry.fk.jochum.dev/docker_hub_cache
- VERSION=${DRONE_TAG:1}
auto_tag: true

@ -1,3 +1,5 @@
[![Build Status](https://drone.fk.jochum.dev/api/badges/jo-micro/router/status.svg)](https://drone.fk.jochum.dev/jo-micro/router)
# router
A dynamic router (API Gatway) for go-micro.
@ -8,6 +10,31 @@ It looks for services that host "proto/routerclientpb/routerclientpb.RouterClien
- gin doesn't allow to delete routes, so if you want to delete a route you have to restart go-micro/router.
## Usage
docker-compose:
```yaml
services:
router:
restart: unless-stopped
image: docker.io/jomicro/router:0.2.3
environment:
- MICRO_TRANSPORT=grpc
- MICRO_REGISTRY=nats
- MICRO_REGISTRY_ADDRESS=nats:4222
- MICRO_BROKER=nats
- MICRO_BROKER_ADDRESS=nats:4222
- SERVER_ADDRESS=:8080
- LOG_LEVEL=info
ports:
- 8080:8080
depends_on:
- nats
```
See `cmd/microrouterd/plugins.go` for a list of availabel transports, registries and brokers.
## Todo
- Add (more) examples.
@ -15,7 +42,7 @@ It looks for services that host "proto/routerclientpb/routerclientpb.RouterClien
- Add support for [debug](https://github.com/asim/go-micro/tree/master/debug).
- Maybe add optional support for [auth](https://github.com/asim/go-micro/blob/master/auth/auth.go).
## Examples
## Integration examples
Have a look at [internalService](https://jochum.dev/jo-micro/router/blob/master/cmd/microrouterd/main.go#L35) or the author's FOSS project [microlobby](https://github.com/pcdummy/microlobby).

@ -36,6 +36,8 @@ tasks:
vars:
VOLUME_PATH:
sh: podman volume inspect jo_micro-router_go --format "{{"{{"}}.Mountpoint{{"}}"}}"
preconditions:
- test -n "{{.CLI_ARGS}}"
protoc:
run: "once"

@ -30,14 +30,14 @@ type JSONRoute struct {
type Handler struct {
service micro.Service
engine *gin.Engine
routes map[string]bool
routes map[string]*routerclientpb.RoutesReply_Route
}
func NewHandler(service micro.Service, engine *gin.Engine) (*Handler, error) {
return &Handler{
service: service,
engine: engine,
routes: make(map[string]bool),
routes: make(map[string]*routerclientpb.RoutesReply_Route),
}, nil
}
@ -51,16 +51,16 @@ func (h *Handler) Start() error {
for {
services, err := util.FindByEndpoint(h.service, "RouterClientService.Routes")
if err != nil {
iLogger.WithCaller().Error(err)
iLogger.Logrus().Error(err)
continue
}
for _, s := range services {
iLogger.WithCaller().Debug("Found service ", s.Name)
iLogger.Logrus().WithField("service", s.Name).Tracef("Found service")
client := routerclientpb.NewRouterClientService(s.Name, h.service.Client())
resp, err := client.Routes(ctx, &emptypb.Empty{})
if err != nil {
iLogger.WithCaller().Error(err)
iLogger.Logrus().Error(err)
// failure in getting routes, silently ignore
continue
}
@ -68,7 +68,6 @@ func (h *Handler) Start() error {
serviceGroup := globalGroup.Group(fmt.Sprintf("/%s", resp.GetRouterURI()))
for _, route := range resp.Routes {
iLogger.WithCaller().Debug("Found endpoint ", route.Endpoint)
var g *gin.RouterGroup = nil
if route.IsGlobal {
@ -77,11 +76,20 @@ func (h *Handler) Start() error {
g = serviceGroup
}
// Calculate the path of the route and register it if it's not registered yet
path := fmt.Sprintf("%s: %s/%s", route.Method, g.BasePath(), route.Path)
if _, ok := h.routes[path]; !ok {
// Calculate the pathMethod of the route and register it if it's not registered yet
pathMethod := fmt.Sprintf("%s:%s%s", route.GetMethod(), g.BasePath(), route.GetPath())
path := fmt.Sprintf("%s%s", g.BasePath(), route.GetPath())
if _, ok := h.routes[pathMethod]; !ok {
iLogger.Logrus().
WithField("service", s.Name).
WithField("endpoint", route.GetEndpoint()).
WithField("method", route.GetMethod()).
WithField("path", path).
Debugf("Found route")
g.Handle(route.GetMethod(), route.GetPath(), h.proxy(s.Name, route))
h.routes[path] = true
h.routes[pathMethod] = route
h.routes[pathMethod].Path = path
}
}
}
@ -173,7 +181,7 @@ func (h *Handler) proxy(serviceName string, route *routerclientpb.RoutesReply_Ro
var response json.RawMessage
err := h.service.Client().Call(ctx, req, &response)
if err != nil {
iLogger.WithCaller().Error(err)
iLogger.Logrus().Error(err)
pErr := errors.FromError(err)
code := int(http.StatusInternalServerError)
@ -192,9 +200,7 @@ func (h *Handler) proxy(serviceName string, route *routerclientpb.RoutesReply_Ro
}
func (h *Handler) Routes(ctx context.Context, in *emptypb.Empty, out *routerserverpb.RoutesReply) error {
ginRoutes := h.engine.Routes()
for _, route := range ginRoutes {
for _, route := range h.routes {
out.Routes = append(out.Routes, &routerserverpb.RoutesReply_Route{
Method: route.Method,
Path: route.Path,

@ -1,3 +1,5 @@
ARG DOCKER_IO=docker.io
# STEP 1 build executable binary
FROM registry.fk.jochum.dev/jo-micro/builder:latest AS builder
@ -11,7 +13,7 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -installsuffix cgo -ldflags="
# STEP 2 build a small image
# start from busybox
FROM busybox
FROM ${DOCKER_IO}/library/busybox:latest
LABEL maintainer="René Jochum <rene@jochum.dev>"

@ -30,7 +30,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.0
github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f
github.com/urfave/cli/v2 v2.15.0
github.com/urfave/cli/v2 v2.16.0
go-micro.dev/v4 v4.8.1
google.golang.org/protobuf v1.28.1
)

@ -682,7 +682,6 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@ -723,6 +722,8 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/urfave/cli/v2 v2.15.0 h1:/U7qTMlBYcmo/Z34PaaVY0Gw04xoGJqEdRAiWNHNyy8=
github.com/urfave/cli/v2 v2.15.0/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
github.com/urfave/cli/v2 v2.16.0 h1:p0XJ2TBh4AyH7twPdMBPL5OMrd9nxkAOOlTHxT5SNBA=
github.com/urfave/cli/v2 v2.16.0/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=

@ -16,9 +16,14 @@ type Handler struct {
}
// NewHandler returns a new dynrouterpb Handler
func NewHandler(routerURI string, routes ...Route) *Handler {
func NewHandler(routerURI string, routes ...*Route) *Handler {
pbRoutes := []*routerclientpb.RoutesReply_Route{}
for _, r := range routes {
// NewRoute returns nil if no Endpoint has been specified, ignore these here
if r == nil {
continue
}
pbRoutes = append(pbRoutes, &routerclientpb.RoutesReply_Route{
IsGlobal: r.IsGlobal,
Method: r.Method,

@ -30,6 +30,14 @@ func Intialized() bool {
return initialized
}
// caller returns string presentation of log caller which is formatted as
// `/path/to/file.go:line_number`. e.g. `/internal/app/api.go:25`
func caller() func(*runtime.Frame) (function string, file string) {
return func(f *runtime.Frame) (function string, file string) {
return "", fmt.Sprintf("%s:%d", f.File, f.Line)
}
}
func Start(cli *cli.Context) error {
if initialized {
return nil
@ -44,6 +52,15 @@ func Start(cli *cli.Context) error {
myLogger.Out = os.Stdout
myLogger.Level = lvl
myLogger.SetReportCaller(true)
myLogger.SetFormatter(&logrus.JSONFormatter{
CallerPrettyfier: caller(),
FieldMap: logrus.FieldMap{
logrus.FieldKeyFile: "caller",
},
})
microLogger.DefaultLogger = microLogrus.NewLogger(microLogrus.WithLogger(myLogger))
initialized = true
@ -60,13 +77,3 @@ func Stop() error {
func Logrus() *logrus.Logger {
return myLogger
}
func WithCaller() *logrus.Entry {
e := logrus.NewEntry(myLogger)
_, file, no, ok := runtime.Caller(1)
if ok {
e.WithField("caller", fmt.Sprintf("%s:%d", file, no))
}
return e
}

@ -1,17 +1,15 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package router
import "net/http"
const (
MethodGet = "GET"
MethodHead = "HEAD"
MethodPost = "POST"
MethodPut = "PUT"
MethodPatch = "PATCH" // RFC 5789
MethodDelete = "DELETE"
MethodConnect = "CONNECT"
MethodOptions = "OPTIONS"
MethodTrace = "TRACE"
MethodGet = http.MethodGet
MethodHead = http.MethodHead
MethodPost = http.MethodPost
MethodPut = http.MethodPut
MethodPatch = http.MethodPatch
MethodDelete = http.MethodDelete
MethodConnect = http.MethodConnect
MethodOptions = http.MethodOptions
MethodTrace = http.MethodTrace
)

@ -1,6 +1,8 @@
package router
import "net/http"
import (
"log"
)
type Route struct {
// isGlobal=True == no prefix route
@ -13,17 +15,22 @@ type Route struct {
type Option func(*Route)
func NewRoute(endpoint interface{}, opts ...Option) Route {
route := Route{
func NewRoute(opts ...Option) *Route {
route := &Route{
IsGlobal: false,
Method: http.MethodGet,
Method: MethodGet,
Path: "/",
Endpoint: endpoint,
Endpoint: nil,
Params: []string{},
}
for _, o := range opts {
o(&route)
o(route)
}
if route.Endpoint == nil {
log.Println("router.Endpoint() is a required argument")
return nil
}
return route

Loading…
Cancel
Save