Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(api,pkg): create generic server package for services #4441

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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