Skip to content

Commit

Permalink
chore(api,pkg): create generic server package for services
Browse files Browse the repository at this point in the history
  • Loading branch information
henrybarreto committed Dec 16, 2024
1 parent 542130b commit 79c02d4
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 46 deletions.
1 change: 0 additions & 1 deletion api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ require (
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
10 changes: 7 additions & 3 deletions api/routes/handler.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package routes

import (
svc "github.com/shellhub-io/shellhub/api/services"
"github.com/shellhub-io/shellhub/api/services"
)

type Handler struct {
service svc.Service
service services.Service
}

func NewHandler(s svc.Service) *Handler {
func (h *Handler) GetService() any {
return h.service
}

func NewHandler(s services.Service) *Handler {
return &Handler{service: s}
}
15 changes: 11 additions & 4 deletions api/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,14 @@ func NewRouter(service services.Service, opts ...Option) *echo.Echo {
}
}

// Internal routes only accessible by other services in the local container network
APIInternalRoutes(e, handler)
APIPublicRoutes(e, handler)

return e
}

// APIInternalRoutes sets to echo instance all internal routes for the service.
func APIInternalRoutes(e *echo.Echo, handler *Handler) {
internalAPI := e.Group("/internal")

internalAPI.GET(AuthRequestURL, gateway.Handler(handler.AuthRequest))
Expand All @@ -68,8 +75,10 @@ func NewRouter(service services.Service, opts ...Option) *echo.Echo {
internalAPI.POST(CreatePrivateKeyURL, gateway.Handler(handler.CreatePrivateKey))
internalAPI.POST(EvaluateKeyURL, gateway.Handler(handler.EvaluateKey))
internalAPI.POST(EventsSessionsURL, gateway.Handler(handler.EventSession))
}

// Public routes for external access through API gateway
// APIPublicRoutes sets to echo instance all public routes for the service.
func APIPublicRoutes(e *echo.Echo, handler *Handler) {
publicAPI := e.Group("/api")
publicAPI.GET(HealthCheckURL, gateway.Handler(handler.EvaluateHealth))

Expand Down Expand Up @@ -147,6 +156,4 @@ func NewRouter(service services.Service, opts ...Option) *echo.Echo {
"/api/containers?*": "/api/devices?$1&connector=true",
"/api/containers/*": "/api/devices/$1",
}))

return e
}
53 changes: 32 additions & 21 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"syscall"

"github.com/getsentry/sentry-go"
"github.com/labstack/echo/v4"
"github.com/shellhub-io/shellhub/api/pkg/echo/handlers"
"github.com/shellhub-io/shellhub/api/routes"
"github.com/shellhub-io/shellhub/api/services"
"github.com/shellhub-io/shellhub/api/store"
Expand All @@ -16,6 +18,7 @@ import (
"github.com/shellhub-io/shellhub/pkg/api/internalclient"
storecache "github.com/shellhub-io/shellhub/pkg/cache"
"github.com/shellhub-io/shellhub/pkg/geoip/geolite2"
"github.com/shellhub-io/shellhub/pkg/server"
"github.com/shellhub-io/shellhub/pkg/worker/asynq"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -181,21 +184,6 @@ func startServer(ctx context.Context, cfg *config, store store.Store, cache stor

service := services.NewService(store, nil, nil, cache, apiClient, servicesOptions...)

routerOptions := []routes.Option{}

if cfg.SentryDSN != "" {
log.Info("Sentry report is enabled")

reporter, err := startSentry(cfg.SentryDSN)
if err != nil {
log.WithField("DSN", cfg.SentryDSN).WithError(err).Warn("Failed to start Sentry")
} else {
log.Info("Sentry client started")
}

routerOptions = append(routerOptions, routes.WithReporter(reporter))
}

worker := asynq.NewServer(
cfg.RedisURI,
asynq.BatchConfig(cfg.AsynqGroupMaxSize, cfg.AsynqGroupMaxDelay, int(cfg.AsynqGroupGracePeriod)),
Expand All @@ -209,19 +197,42 @@ func startServer(ctx context.Context, cfg *config, store store.Store, cache stor
Fatal("failed to start the worker")
}

router := routes.NewRouter(service, routerOptions...)

go func() {
<-ctx.Done()

log.Debug("Closing HTTP server due context cancellation")

worker.Shutdown()
router.Close()
}()

err = router.Start(":8080") //nolint:errcheck
log.WithError(err).Info("HTTP server closed")
handler := routes.NewHandler(service)

options := []server.Option[*echo.Echo]{
func(server *echo.Echo) {
// NOTE: Now, when something was customized on the HTTP server based on some configuration, a new closure
// should be created on this slice, simplifying and centralizing the HTTP options. In this case, when
// Sentry monitoring is enabled, we set a "reporter" for the global error handler.
if cfg.SentryDSN != "" {
log.Info("Sentry report is enabled")

reporter, err := startSentry(cfg.SentryDSN)
if err != nil {
log.WithField("DSN", cfg.SentryDSN).WithError(err).Warn("Failed to start Sentry")
} else {
log.Info("Sentry client started")
}

server.HTTPErrorHandler = handlers.NewErrors(reporter)
}
},
}

routes := []server.Route[*echo.Echo, *routes.Handler]{
routes.APIInternalRoutes,
routes.APIPublicRoutes,
}

return nil
return server.
NewDefaultServer(ctx, handler, nil, routes, options).
Listen()
}
46 changes: 29 additions & 17 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ require (
github.com/gorilla/websocket v1.5.0
github.com/hibiken/asynq v0.24.1
github.com/jarcoal/httpmock v1.3.1
github.com/labstack/echo/v4 v4.10.2
github.com/labstack/echo/v4 v4.12.0
github.com/mattn/go-shellwords v1.0.12
github.com/mholt/archiver/v4 v4.0.0-alpha.8
github.com/openwall/yescrypt-go v1.0.0
github.com/oschwald/geoip2-golang v1.8.0
github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.5
github.com/sethvargo/go-envconfig v0.9.0
github.com/shellhub-io/shellhub/api v0.16.3
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go/modules/redis v0.32.0
Expand All @@ -37,16 +38,17 @@ require (

require (
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.11.5 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/Microsoft/hcsshim v0.12.2 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/bodgit/plumbing v1.2.0 // indirect
github.com/bodgit/sevenzip v1.3.0 // indirect
github.com/bodgit/windows v1.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect
github.com/connesc/cipherio v0.2.1 // indirect
github.com/containerd/containerd v1.7.18 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
Expand All @@ -59,67 +61,77 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/getsentry/sentry-go v0.28.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.2.2 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/redis/go-redis/v9 v9.0.3 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
github.com/shirou/gopsutil/v3 v3.24.3 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/square/mongo-lock v0.0.0-20230808145049-cfcf499f6bf0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/testcontainers/testcontainers-go v0.32.0 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/tklauser/go-sysconf v0.3.13 // indirect
github.com/tklauser/numcpus v0.7.0 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/vmihailenco/go-tinylfu v0.2.2 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
github.com/xakep666/mongo-migrate v0.3.2 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver v1.16.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
go.opentelemetry.io/otel v1.26.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 // indirect
go.opentelemetry.io/otel/metric v1.26.0 // indirect
go.opentelemetry.io/otel/sdk v1.26.0 // indirect
go.opentelemetry.io/otel/trace v1.26.0 // indirect
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
32 changes: 32 additions & 0 deletions pkg/server/echo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package server

import (
"github.com/labstack/echo/v4"
)

// Echo is a wrapper around the Echo HTTP server with simplified lifecycle management.
type Echo struct {
echo *echo.Echo
}

var _ Server[*echo.Echo] = new(Echo)

// Underlying returns the underlying HTTP server.
func (s *Echo) Underlying() *echo.Echo {
return s.echo
}

// Close closes the server.
func (s *Echo) Close() error {
return s.echo.Close()
}

// Start starts the server at a given address.
func (s *Echo) Start(addr string) error {
return s.echo.Start(addr)
}

// Listen starts the HTTP server, listing for connections in [ServerListenDefaultAddress].
func (s *Echo) Listen() error {
return s.echo.Start(ServerListenDefaultAddress)
}
Loading

0 comments on commit 79c02d4

Please sign in to comment.