Skip to content
Merged
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
49 changes: 49 additions & 0 deletions common/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package config

import (
"os"

"github.com/xorcare/pointer"
"gopkg.in/yaml.v3"
)

type Config struct {
Port int `yaml:"Port"`
Host *string `yaml:"Host,omitempty"` // leave empty for localhost

LogPath *string `yaml:"LogPath,omitempty"`
LogLevel *int `yaml:"LogLevel,omitempty"`

Invoker *InvokerConfig `yaml:"Invoker,omitempty"`
// TODO: Add instances here

DB DBConfig `yaml:"DB"`
// if instance is set up on server, leave connection empty
MasterConnection *Connection `yaml:"MasterConnection,omitempty"`
StorageConnection *Connection `yaml:"StorageConnection,omitempty"`
}

func ReadConfig(configPath string) *Config {
content, err := os.ReadFile(configPath)
if err != nil {
panic(err)
}

config := new(Config)
err = yaml.Unmarshal(content, config)
if err != nil {
panic(err)
}

fillInConfig(config)

return config
}

func fillInConfig(config *Config) {
if config.Host == nil {
config.Host = pointer.String("localhost")
}

fillInConnections(config)
}
10 changes: 10 additions & 0 deletions common/config/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package config

type Connection struct {
Address string `yaml:"Address"`
// TODO: Add authentification
}

func fillInConnections(config *Config) {
// TODO: Add auto connection creation if master or storageconn are set up
}
5 changes: 5 additions & 0 deletions common/config/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package config

type DBConfig struct {
Dsn string `yaml:"dsn"`
}
23 changes: 23 additions & 0 deletions common/config/invoker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package config

import "github.com/xorcare/pointer"

type InvokerConfig struct {
Instances int `yaml:"Instances"`
QueueSize *int `yaml:"QueueSize,omitempty"` // default is 10

CacheSize uint64 `yaml:"CacheSize"`
CachePath string `yaml:"CachePath"`
}

func fillInInvokerConfig(config *InvokerConfig) {
if config.QueueSize == nil {
config.QueueSize = pointer.Int(10)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

в константу бы

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

хз на самом деле, тут по сути все эти fillIN это одна большая константная функция, там тогда константы на каждое поле надо делать и читать это будет невозможно. Тут как раз удобно что ты быстренько на код посмотрел и понял какие дефолт значения

}
if len(config.CachePath) == 0 {
panic("No invoker cache path specified")
}
if config.CacheSize == 0 {
panic("No invoker cache size specified")
}
}
21 changes: 21 additions & 0 deletions common/connectors/invokerconn/structs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package invokerconn

import (
"testing_system/common/db/models"
)

// This will be changed in later commits

const (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вообще мне кажется, что можно для "енумов" свои типы делать отдельные, чтобы не передать что-то не то (вот как в storageconn/structs.go)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ага, так и будет в следующем коммите

JobTypeCompile = iota
JobTypeTest
)

type Job struct {
SubmitID uint `json:"submitID" binding:"required"`
JobType int `json:"jobType" binding:"required"`
Test int `json:"test"`

Submit *models.Submission `json:"submit,omitempty"`
Problem *models.Problem `json:"problem,omitempty"`
}
27 changes: 27 additions & 0 deletions common/connectors/storageconn/resourcetype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions common/connectors/storageconn/storage_connector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package storageconn

import (
"testing_system/common"
)

type Connector struct {
// TODO: Add storage data loader
}

func NewStorageConnector(ts *common.TestingSystem) *Connector {
return nil
}

func (s *Connector) Download(request Request) *ResponseFiles {
return nil
}

func (s *Connector) Upload(request Request) *Response {
return nil
}

func (s *Connector) Delete(request Request) *Response {
return nil
}
96 changes: 96 additions & 0 deletions common/connectors/storageconn/structs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package storageconn

//go:generate stringer -type=ResourceType

import (
"fmt"
"os"
"path/filepath"
"strconv"
"testing_system/lib/logger"
)

type ResourceType int

const (
SourceCode ResourceType = iota
CompiledBinary
Test
Checker
Interactor
// Will be increased
)

type Request struct {
// Should be always specified
Resource ResourceType `json:"resource"`

// If resource is part of problem, ProblemID is used
ProblemID uint64 `json:"problemID"`
// If resource is part of submit, SubmitID is used
SubmitID uint64 `json:"submitID"`
// If resource is a test, TestID should be specified
TestID uint64 `json:"testID"`

// For any download, BaseFolder should be specified. The files with original filenames will be placed there
BaseFolder string `json:"-"`

// For uploads, FilePath or FilePaths should be specified (depending on whether the resource is single-file or not).
// Filename will be taken from filename inside the path.
FilePath string `json:"-"`
FilePaths []string `json:"-"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

почему не хранить всегда []string?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это вообще сильно поменяю в следующем коммите, пока можно забить

}

func (s *Request) FillBaseFolder(parent string) {
s.BaseFolder = filepath.Join(parent, s.Resource.String())
switch s.Resource {
case SourceCode, CompiledBinary:
s.BaseFolder = filepath.Join(s.BaseFolder, strconv.FormatUint(s.SubmitID, 10))
case Checker, Interactor:
s.BaseFolder = filepath.Join(s.BaseFolder, strconv.FormatUint(s.ProblemID, 10))
case Test:
s.BaseFolder = filepath.Join(s.BaseFolder, fmt.Sprintf("%d-%d", s.SubmitID, s.TestID))
default:
logger.Panic("Can not fill base folder for storageconn request of type %s", s.Resource)
}
}

type Response struct {
R Request
Error error
}

type ResponseFiles struct {
Response
fileNames []string
Size uint64
}

func (r *ResponseFiles) File() (string, bool) {
if len(r.fileNames) == 0 {
return "", false
}
return filepath.Join(r.R.BaseFolder, r.fileNames[0]), true
}

func (r *ResponseFiles) Get(fileName string) (string, bool) {
for _, f := range r.fileNames {
if fileName == f {
return filepath.Join(r.R.BaseFolder, f), true
}
}
return "", false
}

func (r *ResponseFiles) CleanUp() {
if r.Error != nil {
return
}
if len(r.R.BaseFolder) == 0 {
return
}
err := os.RemoveAll(r.R.BaseFolder)
if err != nil {
logger.Panic("Can not remove resource folder, error: %s", err.Error())
}
}
5 changes: 3 additions & 2 deletions db/db.go → common/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
"log/slog"
"testing_system/db/models"
"testing_system/common/config"
"testing_system/common/db/models"
)

func NewDb(config Config) (*gorm.DB, error) {
func NewDB(config config.DBConfig) (*gorm.DB, error) {
db, err := gorm.Open(postgres.Open(config.Dsn), &gorm.Config{})
if err != nil {
slog.Error("Can't open database", "dsn", config.Dsn, "err", err)
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
104 changes: 104 additions & 0 deletions common/testing_system.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package common

import (
"context"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"net/http"
"os/signal"
"strconv"
"sync"
"syscall"
"testing_system/common/config"
"testing_system/common/db"
"testing_system/lib/logger"
)

type TestingSystem struct {
Config *config.Config
Router *gin.Engine
DB *gorm.DB

processes []func()
defers []func()

StopCtx context.Context
stopFunc context.CancelFunc
stopWG sync.WaitGroup
}

func InitTestingSystem(configPath string) *TestingSystem {
ts := &TestingSystem{
Config: config.ReadConfig(configPath),
}
logger.InitLogger(ts.Config)
ts.Router = gin.Default() // TODO: Add router options (e.g debug)

var err error
ts.DB, err = db.NewDB(ts.Config.DB)
if err != nil {
logger.Panic("Can not set up db connection, error: %s", err.Error())
}

return ts
}

func (ts *TestingSystem) AddProcess(f func()) {
ts.processes = append(ts.processes, f)
}

func (ts *TestingSystem) AddDefer(f func()) {
ts.defers = append(ts.defers, f)
}

func (ts *TestingSystem) Run() {
var ctx context.Context
var cancel context.CancelFunc
ctx, ts.stopFunc = context.WithCancel(context.Background())
ts.StopCtx, cancel = signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
defer cancel()

for _, process := range ts.processes {
ts.runProcess(process)
}

ts.runServer()

ts.stopWG.Wait()

for _, d := range ts.defers {
d()
}
}

func (ts *TestingSystem) runServer() {
addr := ":" + strconv.Itoa(ts.Config.Port)
if ts.Config.Host != nil {
addr = *ts.Config.Host + addr
}
logger.Info("Starting server at " + addr)
server := http.Server{
Addr: addr,
Handler: ts.Router,
}
go func() {
<-ts.StopCtx.Done()
logger.Info("Shutting down server")
server.Shutdown(context.Background())
}()
server.ListenAndServe()
}

func (ts *TestingSystem) runProcess(f func()) {
ts.stopWG.Add(1)
defer func() {
v := recover()
if v != nil {
logger.Error("One process got panic, shutting down all processes gracefully")
ts.stopFunc()
}
ts.stopWG.Done()
}()

f()
}
5 changes: 0 additions & 5 deletions db/config.go

This file was deleted.

Loading