From 5bcd86d4e7ec41ddc194130a236c07466be7f710 Mon Sep 17 00:00:00 2001 From: Ringo Hoffmann Date: Thu, 23 May 2024 16:43:33 +0200 Subject: [PATCH] refactor out dependency container --- cmd/ranna/main.go | 175 +++++++++++--------------- internal/api/interfaces.go | 28 +++++ internal/api/restapi.go | 12 +- internal/api/v1/interfaces.go | 28 +++++ internal/api/v1/routes.go | 29 +++-- internal/api/ws/handler.go | 7 +- internal/api/ws/interfaces.go | 19 +++ internal/api/ws/reatelimit.go | 12 +- internal/api/ws/session.go | 21 ++-- internal/config/confitaprovider.go | 10 +- internal/config/provider.go | 6 - internal/file/provider.go | 7 -- internal/namespace/provider.go | 5 - internal/sandbox/docker/interfaces.go | 7 ++ internal/sandbox/docker/provider.go | 26 ++-- internal/sandbox/docker/sandbox.go | 18 +-- internal/sandbox/interfaces.go | 24 ++++ internal/sandbox/manager.go | 66 +++++----- internal/sandbox/sandbox.go | 2 +- internal/spec/httpprovider.go | 2 +- internal/spec/provider.go | 12 -- internal/static/static.go | 12 -- pkg/models/errors.go | 1 - pkg/models/ws.go | 1 - pkg/random/random_test.go | 13 +- 25 files changed, 286 insertions(+), 257 deletions(-) delete mode 100644 internal/config/provider.go delete mode 100644 internal/file/provider.go delete mode 100644 internal/namespace/provider.go create mode 100644 internal/sandbox/docker/interfaces.go create mode 100644 internal/sandbox/interfaces.go delete mode 100644 internal/spec/provider.go delete mode 100644 internal/static/static.go diff --git a/cmd/ranna/main.go b/cmd/ranna/main.go index 018275d..86521fb 100644 --- a/cmd/ranna/main.go +++ b/cmd/ranna/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os" "os/signal" "strings" @@ -15,99 +16,40 @@ import ( "github.com/ranna-go/ranna/internal/sandbox/docker" "github.com/ranna-go/ranna/internal/scheduler" "github.com/ranna-go/ranna/internal/spec" - "github.com/ranna-go/ranna/internal/static" - "github.com/sarulabs/di/v2" "github.com/sirupsen/logrus" ) -func main() { - godotenv.Load() - - diBuilder, _ := di.NewBuilder() - - diBuilder.Add(di.Def{ - Name: static.DiConfigProvider, - Build: func(ctn di.Container) (interface{}, error) { - p := config.NewPaerser("") - return p, p.Load() - }, - }) - - diBuilder.Add(di.Def{ - Name: static.DiSpecProvider, - Build: func(ctn di.Container) (interface{}, error) { - cfg := ctn.Get(static.DiConfigProvider).(config.Provider) - specFile := cfg.Config().SpecFile - var p spec.Provider - if strings.HasPrefix(specFile, "https://") || strings.HasPrefix(specFile, "http://") { - p = spec.NewHttpProvider(specFile) - } else { - p = spec.NewFileProvider(specFile) - } - return p, p.Load() - }, - }) - - diBuilder.Add(di.Def{ - Name: static.DiSandboxProvider, - Build: func(ctn di.Container) (interface{}, error) { - return docker.NewDockerSandboxProvider(ctn) - }, - }) +type ConfigProvider interface { + Config() *config.Config +} - diBuilder.Add(di.Def{ - Name: static.DiSandboxManager, - Build: func(ctn di.Container) (interface{}, error) { - return sandbox.NewManager(ctn) - }, - Close: func(obj interface{}) error { - logrus.Info("cleaning up running sandboxes ...") - m := obj.(sandbox.Manager) - m.Cleanup() - return nil - }, - }) +type Scheduler interface { + Schedule(spec interface{}, job func()) (id interface{}, err error) +} - diBuilder.Add(di.Def{ - Name: static.DiFileProvider, - Build: func(ctn di.Container) (v interface{}, err error) { - v = file.NewLocalFileProvider() - return - }, - }) +type Manager interface { + PrepareEnvironments(force bool) []error +} - diBuilder.Add(di.Def{ - Name: static.DiAPI, - Build: func(ctn di.Container) (interface{}, error) { - return api.NewRestAPI(ctn) - }, - }) +type SpecProvider interface { + Spec() *spec.SafeSpecMap + Load() error +} - diBuilder.Add(di.Def{ - Name: static.DiNamespaceProvider, - Build: func(ctn di.Container) (v interface{}, err error) { - v = namespace.NewRandomProvider() - return - }, - }) +func checkErr(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "initialization failed: %v\n", err) + os.Exit(1) + } +} - diBuilder.Add(di.Def{ - Name: static.DiScheduler, - Build: func(ctn di.Container) (interface{}, error) { - sched := scheduler.NewCronScheduler() - sched.Start() - return sched, nil - }, - Close: func(obj interface{}) error { - sched := obj.(scheduler.Scheduler) - sched.Stop() - return nil - }, - }) +func main() { + godotenv.Load() - ctn := diBuilder.Build() + cfg := config.NewPaerser("") + err := cfg.Load() + checkErr(err) - cfg := ctn.Get(static.DiConfigProvider).(config.Provider) logrus.SetLevel(logrus.Level(cfg.Config().Log.Level)) logrus.SetFormatter(&logrus.TextFormatter{ ForceColors: cfg.Config().Debug, @@ -117,35 +59,66 @@ func main() { logrus.Warn("ATTENTION: Sandbox Networking is enabled by config! This is a high security risk!") } + specFile := cfg.Config().SpecFile + var specProvider SpecProvider + if strings.HasPrefix(specFile, "https://") || strings.HasPrefix(specFile, "http://") { + specProvider = spec.NewHttpProvider(specFile) + } else { + specProvider = spec.NewFileProvider(specFile) + } + err = specProvider.Load() + checkErr(err) + + sandboxProvider, err := docker.NewProvider(cfg) + checkErr(err) + + fileProvider := file.NewLocalFileProvider() + + namespaceProvider := namespace.NewRandomProvider() + + sandboxManager, err := sandbox.NewManager(sandboxProvider, specProvider, fileProvider, cfg, namespaceProvider) + checkErr(err) + defer func() { + logrus.Info("cleaning up running sandboxes ...") + // TODO: Handle errors + sandboxManager.Cleanup() + }() + + webApi, err := api.NewRestAPI(cfg, specProvider, sandboxManager) + checkErr(err) + + schedulerProvider := scheduler.NewCronScheduler() + schedulerProvider.Start() + defer schedulerProvider.Stop() + if !cfg.Config().SkipStartupPrep { - mgr := ctn.Get(static.DiSandboxManager).(sandbox.Manager) logrus.Info("Prepare spec environments ...") - mgr.PrepareEnvironments(true) + // TODO: Handle errors + sandboxManager.PrepareEnvironments(true) } else { logrus.Warn("Skipping spec preparation on startup") } - if err := scheduleTasks(ctn); err != nil { + if err := scheduleTasks(cfg, schedulerProvider, sandboxManager, specProvider); err != nil { logrus.WithError(err).Fatal("failed scheduling job") } - api := ctn.Get(static.DiAPI).(api.API) - go api.ListenAndServeBlocking() + go func() { + err = webApi.ListenAndServeBlocking() + checkErr(err) + }() sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) <-sc - - // Tear down dependency instances - ctn.DeleteWithSubContainers() } -func scheduleTasks(ctn di.Container) (err error) { - cfg := ctn.Get(static.DiConfigProvider).(config.Provider) - sched := ctn.Get(static.DiScheduler).(scheduler.Scheduler) - mgr := ctn.Get(static.DiSandboxManager).(sandbox.Manager) - specProvider := ctn.Get(static.DiSpecProvider).(spec.Provider) - +func scheduleTasks( + cfg ConfigProvider, + sched Scheduler, + mgr Manager, + specProvider SpecProvider, +) (err error) { schedule := func(name, spec string, job func()) (err error) { if spec != "" { logrus.WithField("name", name).WithField("spec", spec).Info("Scheduling job") @@ -154,8 +127,8 @@ func scheduleTasks(ctn di.Container) (err error) { return } - spec := cfg.Config().Scheduler.UpdateImages - if err = schedule("update spec environments", spec, func() { + scheduleSpec := cfg.Config().Scheduler.UpdateImages + if err = schedule("update spec environments", scheduleSpec, func() { logrus.Info("Updating spec environments ...") defer logrus.Info("Updating spec finished") mgr.PrepareEnvironments(true) @@ -163,8 +136,8 @@ func scheduleTasks(ctn di.Container) (err error) { return } - spec = cfg.Config().Scheduler.UpdateSpecs - if err = schedule("update specs", spec, func() { + scheduleSpec = cfg.Config().Scheduler.UpdateSpecs + if err = schedule("update specs", scheduleSpec, func() { if err = specProvider.Load(); err != nil { logrus.WithError(err).Error("Failed loading specs") } else { diff --git a/internal/api/interfaces.go b/internal/api/interfaces.go index 778f64e..a2450c8 100644 --- a/internal/api/interfaces.go +++ b/internal/api/interfaces.go @@ -1 +1,29 @@ package api + +import ( + "github.com/ranna-go/ranna/internal/config" + "github.com/ranna-go/ranna/internal/sandbox" + "github.com/ranna-go/ranna/internal/spec" + "github.com/ranna-go/ranna/pkg/models" +) + +type ConfigProvider interface { + Config() *config.Config +} + +type SpecProvider interface { + Spec() *spec.SafeSpecMap +} + +type SandboxManager interface { + RunInSandbox( + req *models.ExecutionRequest, + cSpn chan string, + cOut, cErr chan []byte, + cClose chan bool, + ) (err error) + PrepareEnvironments(force bool) []error + KillAndCleanUp(id string) (bool, error) + Cleanup() []error + GetProvider() sandbox.Provider +} diff --git a/internal/api/restapi.go b/internal/api/restapi.go index 1212482..c5fb74e 100644 --- a/internal/api/restapi.go +++ b/internal/api/restapi.go @@ -1,14 +1,12 @@ package api import ( + "errors" "strings" "github.com/gofiber/fiber/v2" v1 "github.com/ranna-go/ranna/internal/api/v1" - "github.com/ranna-go/ranna/internal/config" - "github.com/ranna-go/ranna/internal/static" "github.com/ranna-go/ranna/pkg/models" - "github.com/sarulabs/di/v2" "github.com/sirupsen/logrus" ) @@ -17,8 +15,7 @@ type RestAPI struct { app *fiber.App } -func NewRestAPI(ctn di.Container) (r *RestAPI, err error) { - cfg := ctn.Get(static.DiConfigProvider).(config.Provider) +func NewRestAPI(cfg ConfigProvider, spec SpecProvider, manager SandboxManager) (r *RestAPI, err error) { r = &RestAPI{ bindAddress: cfg.Config().API.BindAddress, @@ -37,7 +34,7 @@ func NewRestAPI(ctn di.Container) (r *RestAPI, err error) { ProxyHeader: "X-Forwarded-For", }) - new(v1.Router).Setup(r.app.Group("/v1"), ctn) + new(v1.Router).Setup(r.app.Group("/v1"), cfg, spec, manager) return } @@ -48,7 +45,8 @@ func (r *RestAPI) ListenAndServeBlocking() error { } func errorHandler(ctx *fiber.Ctx, err error) error { - if fErr, ok := err.(*fiber.Error); ok { + var fErr *fiber.Error + if errors.As(err, &fErr) { ctx.Status(fErr.Code) return ctx.JSON(&models.ErrorModel{ Error: fErr.Message, diff --git a/internal/api/v1/interfaces.go b/internal/api/v1/interfaces.go index b7b1f99..06f6c7c 100644 --- a/internal/api/v1/interfaces.go +++ b/internal/api/v1/interfaces.go @@ -1 +1,29 @@ package v1 + +import ( + "github.com/ranna-go/ranna/internal/config" + "github.com/ranna-go/ranna/internal/sandbox" + "github.com/ranna-go/ranna/internal/spec" + "github.com/ranna-go/ranna/pkg/models" +) + +type SpecProvider interface { + Spec() *spec.SafeSpecMap +} + +type ConfigProvider interface { + Config() *config.Config +} + +type SandboxManager interface { + RunInSandbox( + req *models.ExecutionRequest, + cSpn chan string, + cOut, cErr chan []byte, + cClose chan bool, + ) (err error) + PrepareEnvironments(force bool) []error + KillAndCleanUp(id string) (bool, error) + Cleanup() []error + GetProvider() sandbox.Provider +} diff --git a/internal/api/v1/routes.go b/internal/api/v1/routes.go index b715870..8851581 100644 --- a/internal/api/v1/routes.go +++ b/internal/api/v1/routes.go @@ -5,14 +5,11 @@ import ( "github.com/gofiber/fiber/v2" "github.com/ranna-go/ranna/internal/api/ws" - "github.com/ranna-go/ranna/internal/config" "github.com/ranna-go/ranna/internal/sandbox" - "github.com/ranna-go/ranna/internal/spec" "github.com/ranna-go/ranna/internal/static" "github.com/ranna-go/ranna/internal/util" "github.com/ranna-go/ranna/pkg/cappedbuffer" "github.com/ranna-go/ranna/pkg/models" - "github.com/sarulabs/di/v2" "github.com/sirupsen/logrus" ) @@ -21,21 +18,27 @@ var ( errEmptyCode = fiber.NewError(fiber.StatusBadRequest, "code is empty") ) +// Router +// // @title ranna main API // @version 1.0 // @description The ranna main REST API. // @basepath /v1 type Router struct { - spec spec.Provider - cfg config.Provider - manager sandbox.Manager + spec SpecProvider + cfg ConfigProvider + manager SandboxManager streamBufferCap int } -func (r *Router) Setup(route fiber.Router, ctn di.Container) { - r.cfg = ctn.Get(static.DiConfigProvider).(config.Provider) - r.spec = ctn.Get(static.DiSpecProvider).(spec.Provider) - r.manager = ctn.Get(static.DiSandboxManager).(sandbox.Manager) +func (r *Router) Setup(route fiber.Router, + cfg ConfigProvider, + spec SpecProvider, + manager SandboxManager, +) { + r.cfg = cfg + r.spec = spec + r.manager = manager sbc, err := util.ParseMemoryStr(r.cfg.Config().Sandbox.StreamBufferCap) if err != nil { @@ -50,7 +53,7 @@ func (r *Router) Setup(route fiber.Router, ctn di.Container) { route.Post("/exec", r.postExec) route.Get("/info", r.getInfo) route.Use("/ws", ws.Upgrade()) - route.Get("/ws", ws.Handler(ctn)) + route.Get("/ws", ws.Handler(cfg, manager)) } func (r *Router) optionsBypass(ctx *fiber.Ctx) error { @@ -156,11 +159,11 @@ func (r *Router) postExec(ctx *fiber.Ctx) (err error) { // --- UTIL --- func (r *Router) checkOutputLen(stdout, stderr string) (err error) { - max, err := util.ParseMemoryStr(r.cfg.Config().API.MaxOutputLen) + maxOutLen, err := util.ParseMemoryStr(r.cfg.Config().API.MaxOutputLen) if err != nil { return } - if int64(len(stdout))+int64(len(stderr)) > max { + if int64(len(stdout))+int64(len(stderr)) > maxOutLen { err = errOutputLenExceeded } return diff --git a/internal/api/ws/handler.go b/internal/api/ws/handler.go index f98d747..5962c0c 100644 --- a/internal/api/ws/handler.go +++ b/internal/api/ws/handler.go @@ -3,7 +3,6 @@ package ws import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/websocket/v2" - "github.com/sarulabs/di/v2" ) func Upgrade() fiber.Handler { @@ -18,7 +17,7 @@ func Upgrade() fiber.Handler { } } -func Handler(ctn di.Container) fiber.Handler { - rlm := NewRateLimitManager(ctn) - return newSession(rlm, ctn).Handler() +func Handler(cfg ConfigProvider, manager SandboxManager) fiber.Handler { + rlm := NewRateLimitManager(cfg) + return newSession(rlm, manager).Handler() } diff --git a/internal/api/ws/interfaces.go b/internal/api/ws/interfaces.go index 9859295..8e260f7 100644 --- a/internal/api/ws/interfaces.go +++ b/internal/api/ws/interfaces.go @@ -1 +1,20 @@ package ws + +import ( + "github.com/ranna-go/ranna/internal/config" + "github.com/ranna-go/ranna/pkg/models" +) + +type ConfigProvider interface { + Config() *config.Config +} + +type SandboxManager interface { + RunInSandbox( + req *models.ExecutionRequest, + cSpn chan string, + cOut, cErr chan []byte, + cClose chan bool, + ) (err error) + KillAndCleanUp(id string) (bool, error) +} diff --git a/internal/api/ws/reatelimit.go b/internal/api/ws/reatelimit.go index efe57a3..28d70fc 100644 --- a/internal/api/ws/reatelimit.go +++ b/internal/api/ws/reatelimit.go @@ -6,10 +6,7 @@ import ( "time" "github.com/gofiber/websocket/v2" - "github.com/ranna-go/ranna/internal/config" - "github.com/ranna-go/ranna/internal/static" "github.com/ranna-go/ranna/pkg/models" - "github.com/sarulabs/di/v2" "github.com/zekroTJA/ratelimit" "github.com/zekroTJA/timedmap" ) @@ -40,14 +37,13 @@ type RateLimitManager struct { limiters *timedmap.TimedMap } -func NewRateLimitManager(ctn di.Container) *RateLimitManager { - cfg := ctn.Get(static.DiConfigProvider).(config.Provider). - Config().API.WS.RateLimit +func NewRateLimitManager(cfg ConfigProvider) *RateLimitManager { + rlCfg := cfg.Config().API.WS.RateLimit limits := map[models.OpCode]limit{ models.OpExec: { - Burst: cfg.Burst, - Limit: time.Duration(cfg.LimitSeconds) * time.Second, + Burst: rlCfg.Burst, + Limit: time.Duration(rlCfg.LimitSeconds) * time.Second, }, } diff --git a/internal/api/ws/session.go b/internal/api/ws/session.go index e0aee34..fac133d 100644 --- a/internal/api/ws/session.go +++ b/internal/api/ws/session.go @@ -2,15 +2,13 @@ package ws import ( "encoding/json" + "errors" "sync" "github.com/gofiber/fiber/v2" "github.com/gofiber/websocket/v2" - "github.com/ranna-go/ranna/internal/sandbox" - "github.com/ranna-go/ranna/internal/static" "github.com/ranna-go/ranna/internal/util" "github.com/ranna-go/ranna/pkg/models" - "github.com/sarulabs/di/v2" "github.com/sirupsen/logrus" ) @@ -21,15 +19,16 @@ var sessionPool = sync.Pool{ } type session struct { - conn *websocket.Conn - manager sandbox.Manager - rlm *RateLimitManager + manager SandboxManager + + conn *websocket.Conn + rlm *RateLimitManager } -func newSession(rlm *RateLimitManager, ctn di.Container) (s *session) { +func newSession(rlm *RateLimitManager, manager SandboxManager) (s *session) { s = sessionPool.Get().(*session) s.conn = nil - s.manager = ctn.Get(static.DiSandboxManager).(sandbox.Manager) + s.manager = manager s.rlm = rlm return } @@ -78,10 +77,8 @@ func (s *session) Send(v models.Event) (err error) { func (s *session) SendError(err error, nonce int) error { var data models.WsError - if wsErr, ok := err.(models.WsError); ok { - data = wsErr - } else { - data = models.WsError{500, err.Error()} + if !errors.As(err, &data) { + data = models.WsError{Code: 500, Message: err.Error()} } return s.Send(models.Event{ Code: models.EventError, diff --git a/internal/config/confitaprovider.go b/internal/config/confitaprovider.go index 330af9d..16091c9 100644 --- a/internal/config/confitaprovider.go +++ b/internal/config/confitaprovider.go @@ -9,17 +9,17 @@ import ( "github.com/heetch/confita/backend/flags" ) -type ConfitaProvider struct { +type Provider struct { cfg *Config } -func NewConfitaProvider() *ConfitaProvider { - return &ConfitaProvider{ +func NewProvider() *Provider { + return &Provider{ cfg: &defaults, } } -func (p *ConfitaProvider) Load() error { +func (p *Provider) Load() error { loader := confita.NewLoader( env.NewBackend(), file.NewOptionalBackend("config.json"), @@ -29,6 +29,6 @@ func (p *ConfitaProvider) Load() error { return loader.Load(context.Background(), p.cfg) } -func (p *ConfitaProvider) Config() *Config { +func (p *Provider) Config() *Config { return p.cfg } diff --git a/internal/config/provider.go b/internal/config/provider.go deleted file mode 100644 index 6754029..0000000 --- a/internal/config/provider.go +++ /dev/null @@ -1,6 +0,0 @@ -package config - -type Provider interface { - Load() error - Config() *Config -} diff --git a/internal/file/provider.go b/internal/file/provider.go deleted file mode 100644 index a2c0748..0000000 --- a/internal/file/provider.go +++ /dev/null @@ -1,7 +0,0 @@ -package file - -type Provider interface { - CreateDirectory(path string) error - CreateFileWithContent(path, content string) error - DeleteDirectory(path string) error -} diff --git a/internal/namespace/provider.go b/internal/namespace/provider.go deleted file mode 100644 index 47d2784..0000000 --- a/internal/namespace/provider.go +++ /dev/null @@ -1,5 +0,0 @@ -package namespace - -type Provider interface { - Get() (string, error) -} diff --git a/internal/sandbox/docker/interfaces.go b/internal/sandbox/docker/interfaces.go new file mode 100644 index 0000000..011e2ea --- /dev/null +++ b/internal/sandbox/docker/interfaces.go @@ -0,0 +1,7 @@ +package docker + +import "github.com/ranna-go/ranna/internal/config" + +type ConfigProvider interface { + Config() *config.Config +} diff --git a/internal/sandbox/docker/provider.go b/internal/sandbox/docker/provider.go index 0a187fd..77330b7 100644 --- a/internal/sandbox/docker/provider.go +++ b/internal/sandbox/docker/provider.go @@ -1,19 +1,17 @@ package docker import ( + "errors" "fmt" "path" "path/filepath" "strings" dockerclient "github.com/fsouza/go-dockerclient" - "github.com/ranna-go/ranna/internal/config" "github.com/ranna-go/ranna/internal/sandbox" - "github.com/ranna-go/ranna/internal/static" "github.com/ranna-go/ranna/internal/util" "github.com/ranna-go/ranna/pkg/models" "github.com/rs/xid" - "github.com/sarulabs/di/v2" "github.com/sirupsen/logrus" ) @@ -21,17 +19,17 @@ const ( containerRootPath = "/var/tmp/exec" ) -type DockerSandboxProvider struct { - cfg config.Provider +type Provider struct { + cfg ConfigProvider client *dockerclient.Client } -var _ sandbox.Provider = (*DockerSandboxProvider)(nil) +var _ sandbox.Provider = (*Provider)(nil) -func NewDockerSandboxProvider(ctn di.Container) (dsp *DockerSandboxProvider, err error) { - dsp = &DockerSandboxProvider{} +func NewProvider(cfg ConfigProvider) (dsp *Provider, err error) { + dsp = &Provider{} - dsp.cfg = ctn.Get(static.DiConfigProvider).(config.Provider) + dsp.cfg = cfg dsp.client, err = dockerclient.NewClientFromEnv() if err != nil { @@ -41,7 +39,7 @@ func NewDockerSandboxProvider(ctn di.Container) (dsp *DockerSandboxProvider, err return } -func (dsp DockerSandboxProvider) Info() (v *models.SandboxInfo, err error) { +func (dsp *Provider) Info() (v *models.SandboxInfo, err error) { info, err := dsp.client.Info() if err != nil { return @@ -53,7 +51,7 @@ func (dsp DockerSandboxProvider) Info() (v *models.SandboxInfo, err error) { return } -func (dsp DockerSandboxProvider) Prepare(spec models.Spec, force bool) (err error) { +func (dsp *Provider) Prepare(spec models.Spec, force bool) (err error) { repo, tag := getImage(spec.Image) if force { @@ -61,7 +59,7 @@ func (dsp DockerSandboxProvider) Prepare(spec models.Spec, force bool) (err erro } else { _, err = dsp.client.InspectImage(repo + ":" + tag) } - if err == dockerclient.ErrNoSuchImage { + if errors.Is(err, dockerclient.ErrNoSuchImage) { logrus.WithFields(logrus.Fields{ "repo": repo, "tag": tag, @@ -76,7 +74,7 @@ func (dsp DockerSandboxProvider) Prepare(spec models.Spec, force bool) (err erro return } -func (dsp *DockerSandboxProvider) CreateSandbox(spec sandbox.RunSpec) (sbx sandbox.Sandbox, err error) { +func (dsp *Provider) CreateSandbox(spec sandbox.RunSpec) (sbx sandbox.Sandbox, err error) { repo, tag := getImage(spec.Image) err = dsp.Prepare(spec.Spec, false) @@ -116,7 +114,7 @@ func (dsp *DockerSandboxProvider) CreateSandbox(spec sandbox.RunSpec) (sbx sandb Name: fmt.Sprintf("ranna-%s-%s", spec.Language, xid.New().String()), }) - sbx = &DockerSandbox{ + sbx = &Sandbox{ client: dsp.client, container: container, } diff --git a/internal/sandbox/docker/sandbox.go b/internal/sandbox/docker/sandbox.go index 4cf79f9..ddf518d 100644 --- a/internal/sandbox/docker/sandbox.go +++ b/internal/sandbox/docker/sandbox.go @@ -6,20 +6,20 @@ import ( "github.com/ranna-go/ranna/pkg/chanwriter" ) -// DockerSandbox implements Sandbox for +// Sandbox implements Sandbox for // Docker containers. -type DockerSandbox struct { +type Sandbox struct { client *dockerclient.Client container *dockerclient.Container } -var _ sandbox.Sandbox = (*DockerSandbox)(nil) +var _ sandbox.Sandbox = (*Sandbox)(nil) -func (s *DockerSandbox) ID() string { +func (s *Sandbox) ID() string { return s.container.ID } -func (s *DockerSandbox) Run(cOut, cErr chan []byte, cClose chan bool) (err error) { +func (s *Sandbox) Run(cOut, cErr chan []byte, cClose chan bool) (err error) { buffStdout := chanwriter.New(cOut) buffStderr := chanwriter.New(cErr) waiter, err := s.client.AttachToContainerNonBlocking(dockerclient.AttachToContainerOptions{ @@ -44,8 +44,8 @@ func (s *DockerSandbox) Run(cOut, cErr chan []byte, cClose chan bool) (err error return } -func (s *DockerSandbox) IsRunning() (ok bool, err error) { - ctn, err := s.client.InspectContainer(s.container.ID) +func (s *Sandbox) IsRunning() (ok bool, err error) { + ctn, err := s.client.InspectContainerWithOptions(dockerclient.InspectContainerOptions{ID: s.container.ID}) if err != nil { return } @@ -54,13 +54,13 @@ func (s *DockerSandbox) IsRunning() (ok bool, err error) { return } -func (s *DockerSandbox) Kill() error { +func (s *Sandbox) Kill() error { return s.client.KillContainer(dockerclient.KillContainerOptions{ ID: s.container.ID, }) } -func (s *DockerSandbox) Delete() error { +func (s *Sandbox) Delete() error { return s.client.RemoveContainer(dockerclient.RemoveContainerOptions{ ID: s.container.ID, }) diff --git a/internal/sandbox/interfaces.go b/internal/sandbox/interfaces.go new file mode 100644 index 0000000..4f8b043 --- /dev/null +++ b/internal/sandbox/interfaces.go @@ -0,0 +1,24 @@ +package sandbox + +import ( + "github.com/ranna-go/ranna/internal/config" + "github.com/ranna-go/ranna/internal/spec" +) + +type SpecProvider interface { + Spec() *spec.SafeSpecMap +} + +type FileProvider interface { + CreateDirectory(path string) error + CreateFileWithContent(path, content string) error + DeleteDirectory(path string) error +} + +type ConfigProvider interface { + Config() *config.Config +} + +type NamespaceProvider interface { + Get() (string, error) +} diff --git a/internal/sandbox/manager.go b/internal/sandbox/manager.go index 8429e2f..2b9adb0 100644 --- a/internal/sandbox/manager.go +++ b/internal/sandbox/manager.go @@ -8,14 +8,8 @@ import ( "sync" "time" - "github.com/ranna-go/ranna/internal/config" - "github.com/ranna-go/ranna/internal/file" - "github.com/ranna-go/ranna/internal/namespace" - "github.com/ranna-go/ranna/internal/spec" - "github.com/ranna-go/ranna/internal/static" "github.com/ranna-go/ranna/pkg/models" "github.com/ranna-go/ranna/pkg/timeout" - "github.com/sarulabs/di/v2" "github.com/sirupsen/logrus" ) @@ -36,7 +30,7 @@ type Manager interface { // The sandbox is then started and the current go routine is // blocked until the execution is finished or timed out. // - // On success, an execution response is returned. + // On success returns an execution response. RunInSandbox( req *models.ExecutionRequest, cSpn chan string, @@ -65,20 +59,20 @@ type Manager interface { GetProvider() Provider } -// managerImpl is the standard implementation +// ManagerImpl is the standard implementation // of Manager. -type managerImpl struct { +type ManagerImpl struct { sandbox Provider - spec spec.Provider - file file.Provider - cfg config.Provider - ns namespace.Provider + spec SpecProvider + file FileProvider + cfg ConfigProvider + ns NamespaceProvider runningSandboxes *sync.Map isCleanup bool } -var _ Manager = (*managerImpl)(nil) +var _ Manager = (*ManagerImpl)(nil) // sandboxWrapper wraps a sandbox instance and // the used hostDir. @@ -98,26 +92,32 @@ type SystemError struct { // IsSystemError returns true when the passed // error is type of SystemError. func IsSystemError(err error) (ok bool) { - _, ok = err.(SystemError) - return + var systemError SystemError + return errors.As(err, &systemError) } -// NewManager returns a new instance of managerImpl. -func NewManager(ctn di.Container) (m *managerImpl, err error) { - m = &managerImpl{} - - m.sandbox = ctn.Get(static.DiSandboxProvider).(Provider) - m.spec = ctn.Get(static.DiSpecProvider).(spec.Provider) - m.file = ctn.Get(static.DiFileProvider).(file.Provider) - m.cfg = ctn.Get(static.DiConfigProvider).(config.Provider) - m.ns = ctn.Get(static.DiNamespaceProvider).(namespace.Provider) +// NewManager returns a new instance of ManagerImpl. +func NewManager( + sandbox Provider, + spec SpecProvider, + file FileProvider, + cfg ConfigProvider, + ns NamespaceProvider, +) (m *ManagerImpl, err error) { + m = &ManagerImpl{} + + m.sandbox = sandbox + m.spec = spec + m.file = file + m.cfg = cfg + m.ns = ns m.runningSandboxes = &sync.Map{} return } -func (m *managerImpl) PrepareEnvironments(force bool) (errs []error) { +func (m *ManagerImpl) PrepareEnvironments(force bool) (errs []error) { errs = []error{} for _, spec := range m.spec.Spec().GetSnapshot() { @@ -133,7 +133,7 @@ func (m *managerImpl) PrepareEnvironments(force bool) (errs []error) { return } -func (m *managerImpl) RunInSandbox( +func (m *ManagerImpl) RunInSandbox( req *models.ExecutionRequest, cSpn chan string, cOut, cErr chan []byte, @@ -201,7 +201,7 @@ func (m *managerImpl) RunInSandbox( runSpc.Cmd = spc.FileName } - // Create host directory + sub directory on the + // Create host directory + sub-directory on the // Docker host. hostDir := runSpc.GetAssambledHostDir() if err = m.file.CreateDirectory(hostDir); err != nil { @@ -209,7 +209,7 @@ func (m *managerImpl) RunInSandbox( return } - // Create code snippet file in the host + sub directory + // Create code snippet file in the host + sub-directory fileDir := path.Join(hostDir, spc.FileName) if err = m.file.CreateFileWithContent(fileDir, req.Code); err != nil { err = SystemError{err} @@ -266,7 +266,7 @@ func (m *managerImpl) RunInSandbox( return } -func (m *managerImpl) KillAndCleanUp(id string) (ok bool, err error) { +func (m *ManagerImpl) KillAndCleanUp(id string) (ok bool, err error) { v, ok := m.runningSandboxes.Load(id) if !ok { return @@ -282,7 +282,7 @@ func (m *managerImpl) KillAndCleanUp(id string) (ok bool, err error) { return } -func (m *managerImpl) Cleanup() (errs []error) { +func (m *ManagerImpl) Cleanup() (errs []error) { m.isCleanup = true errs = []error{} @@ -300,11 +300,11 @@ func (m *managerImpl) Cleanup() (errs []error) { return } -func (m *managerImpl) GetProvider() Provider { +func (m *ManagerImpl) GetProvider() Provider { return m.sandbox } -func (m *managerImpl) killAndCleanUp(w *sandboxWrapper) (err error) { +func (m *ManagerImpl) killAndCleanUp(w *sandboxWrapper) (err error) { defer func() { if err != nil { logrus. diff --git a/internal/sandbox/sandbox.go b/internal/sandbox/sandbox.go index 9ecd11a..f7893a3 100644 --- a/internal/sandbox/sandbox.go +++ b/internal/sandbox/sandbox.go @@ -26,7 +26,7 @@ type Sandbox interface { // taking care of the teardown of internal // processes. // - // Its like plugging the cable. ;) + // It's like plugging the cable. ;) Kill() error // Delete tears down the used resources diff --git a/internal/spec/httpprovider.go b/internal/spec/httpprovider.go index f839983..f5a534d 100644 --- a/internal/spec/httpprovider.go +++ b/internal/spec/httpprovider.go @@ -9,7 +9,7 @@ import ( "strings" ) -// HttpProviders implements Provider for fetching +// HttpProvider implements Provider for fetching // spec definitions over an HTTP endpoint. type HttpProvider struct { *baseProvider diff --git a/internal/spec/provider.go b/internal/spec/provider.go deleted file mode 100644 index efb3c6f..0000000 --- a/internal/spec/provider.go +++ /dev/null @@ -1,12 +0,0 @@ -package spec - -// Provider to load the spec map. -type Provider interface { - - // Load retrieves and parses a spec - // map from given source. - Load() error - - // Spec returns the loaded spec map. - Spec() *SafeSpecMap -} diff --git a/internal/static/static.go b/internal/static/static.go deleted file mode 100644 index fc8ed44..0000000 --- a/internal/static/static.go +++ /dev/null @@ -1,12 +0,0 @@ -package static - -const ( - DiConfigProvider = "configprovider" - DiSpecProvider = "specprovider" - DiSandboxProvider = "sandboxprovider" - DiSandboxManager = "sandboxmanager" - DiAPI = "apiservice" - DiFileProvider = "fileprovider" - DiNamespaceProvider = "namespaceprovider" - DiScheduler = "scheduler" -) diff --git a/pkg/models/errors.go b/pkg/models/errors.go index 9dd6f2b..fccded0 100644 --- a/pkg/models/errors.go +++ b/pkg/models/errors.go @@ -8,7 +8,6 @@ var ( ErrInvalidMessageType = WsError{400, "invalid message type"} ErrInvalidOpCode = WsError{400, "invalid operation code"} ErrEmptyCode = WsError{400, "code is empty"} - ErrUnimplemented = WsError{501, "this action is currently unimplemented"} ErrSandboxNotRunning = WsError{400, "sandbox is not running"} ErrRateLimited = WsError{429, "you have been rate limited"} ) diff --git a/pkg/models/ws.go b/pkg/models/ws.go index 6ae85e9..a19568c 100644 --- a/pkg/models/ws.go +++ b/pkg/models/ws.go @@ -8,7 +8,6 @@ const ( EventSpawn EventLog EventStop - EventKill // Unused ) type OpCode int diff --git a/pkg/random/random_test.go b/pkg/random/random_test.go index d9c57ea..1847a33 100644 --- a/pkg/random/random_test.go +++ b/pkg/random/random_test.go @@ -1,15 +1,18 @@ package random -import "testing" +import ( + "errors" + "testing" +) func TestGetRandBase64Str(t *testing.T) { v, err := GetRandBase64Str(0) - if v != "" || err != ErrInvalidLen { + if v != "" || !errors.Is(err, ErrInvalidLen) { t.Error("invalid length not detected") } v, err = GetRandBase64Str(-1) - if v != "" || err != ErrInvalidLen { + if v != "" || !errors.Is(err, ErrInvalidLen) { t.Error("invalid length not detected") } @@ -48,12 +51,12 @@ func TestGetRandBase64StrUniqueness(t *testing.T) { func TestGetRandByteArray(t *testing.T) { v, err := GetRandByteArray(0) - if v != nil || err != ErrInvalidLen { + if v != nil || !errors.Is(err, ErrInvalidLen) { t.Error("invalid length not detected") } v, err = GetRandByteArray(-1) - if v != nil || err != ErrInvalidLen { + if v != nil || !errors.Is(err, ErrInvalidLen) { t.Error("invalid length not detected") }