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
5 changes: 4 additions & 1 deletion common/config/db.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package config

type DBConfig struct {
Dsn string `yaml:"dsn"`
Dsn string `yaml:"Dsn"`

// InMemory should be used only for tests
InMemory bool `yaml:"InMemory"`
}
2 changes: 1 addition & 1 deletion common/connectors/connector_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func NewConnectorBase(connection *config.Connection) *ConnectorBase {
Connection: connection,
client: resty.New(),
}
// TODO: Add host initialization from connection
c.client.SetBaseURL(connection.Address)
// TODO: Add auth
// TODO: Add retry configuration
return c
Expand Down
25 changes: 25 additions & 0 deletions common/connectors/invokerconn/jobtype_string.go

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

10 changes: 9 additions & 1 deletion common/connectors/invokerconn/structs.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
//go:generate go run golang.org/x/tools/cmd/stringer@latest -type=JobType

package invokerconn

import "fmt"

type JobType int

const (
CompileJob JobType = iota
CompileJob JobType = iota + 1
TestJob
)

Expand All @@ -16,6 +20,10 @@ type Job struct {
// TODO: Add job dependency
}

func (j Job) String() string {
return fmt.Sprintf("ID: %s Submit: %d Type %v Test: %d", j.ID, j.SubmitID, j.Type, j.Test)
}

type Status struct {
MaxNewJobs uint64 `json:"MaxNewJobs"`
ActiveJobIDs []string `json:"ActiveJobIDs"`
Expand Down
57 changes: 30 additions & 27 deletions common/connectors/storageconn/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package storageconn
import (
"encoding/json"
"fmt"
"github.com/go-resty/resty/v2"
"io"
"mime"
"net/http"
"os"
"path/filepath"
"testing_system/common/config"
"testing_system/common/connectors"
"testing_system/lib/connector"
)

type Connector struct {
Expand All @@ -31,9 +33,12 @@ func (s *Connector) Download(request *Request) *FileResponse {
return response
}

path := "/storage/get"
r := s.connection.R()

if request.Ctx != nil {
r.SetContext(request.Ctx)
}

requestJSON, err := json.Marshal(request)
if err != nil {
response.Error = fmt.Errorf("failed to form request to storage: %v", err)
Expand All @@ -43,8 +48,9 @@ func (s *Connector) Download(request *Request) *FileResponse {
r.SetQueryParams(map[string]string{
"request": string(requestJSON),
})
r.SetDoNotParseResponse(true)

resp, err := r.SetDoNotParseResponse(true).Execute("GET", path)
resp, err := r.Get("/storage/get")
if err != nil {
response.Error = fmt.Errorf("failed to send request: %v", err)
return response
Expand All @@ -55,7 +61,15 @@ func (s *Connector) Download(request *Request) *FileResponse {
if resp.StatusCode() == http.StatusNotFound {
response.Error = ErrStorageFileNotFound
} else {
response.Error = fmt.Errorf("get request failed with status: %v", resp.Status())
body, err := io.ReadAll(resp.RawBody())
if err != nil {
response.Error = &connector.Error{
Code: resp.StatusCode(),
Message: err.Error(),
Path: resp.Request.URL,
}
}
response.Error = connector.ParseRespError(body, resp)
}
return response
}
Expand Down Expand Up @@ -107,8 +121,10 @@ func (s *Connector) Upload(request *Request) *Response {
return response
}

path := "/storage/upload"
r := s.connection.R()
if request.Ctx != nil {
r.SetContext(request.Ctx)
}

requestJSON, err := json.Marshal(request)
if err != nil {
Expand All @@ -120,28 +136,24 @@ func (s *Connector) Upload(request *Request) *Response {
"request": string(requestJSON),
})

// request.StorageFilename can be empty
r.SetFileReader("file", request.StorageFilename, request.File)

resp, err := r.Post(path)
if err != nil {
response.Error = fmt.Errorf("failed to send request: %v", err)
return response
}

if resp.IsError() {
response.Error = fmt.Errorf("upload failed with status: %v", resp.Status())
return response
requestFileName := request.StorageFilename
// request.StorageFilename can be empty but http requires filename to be specified
if requestFileName == "" {
requestFileName = "noname"
}
r.SetFileReader("file", requestFileName, request.File)

response.Error = connector.ReceiveEmpty(r, "/storage/upload", resty.MethodPost)
return response
}

func (s *Connector) Delete(request *Request) *Response {
response := &Response{R: *request}

path := "/storage/remove"
r := s.connection.R()
if request.Ctx != nil {
r.SetContext(request.Ctx)
}

requestJSON, err := json.Marshal(request)
if err != nil {
Expand All @@ -153,16 +165,7 @@ func (s *Connector) Delete(request *Request) *Response {
"request": string(requestJSON),
})

resp, err := r.Delete(path)
if err != nil {
response.Error = fmt.Errorf("failed to send request: %v", err)
return response
}

if resp.IsError() {
response.Error = fmt.Errorf("delete failed with status: %v", resp.Status())
return response
}
response.Error = connector.ReceiveEmpty(r, "/storage/remove", resty.MethodDelete)

return response
}
6 changes: 5 additions & 1 deletion common/connectors/storageconn/structs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package storageconn

import (
"context"
"io"
"os"
"path/filepath"
Expand All @@ -13,7 +14,7 @@ type Request struct {
Resource resource.Type `json:"resource"`

/*
Resource must always has exactly one of ProblemId and SubmitID
Resource must always have exactly one of ProblemId and SubmitID
Including, only TestID cannot be specified
ID=0 is considered absent
*/
Expand All @@ -36,6 +37,9 @@ type Request struct {

// If StorageFilename is not specified, Storage tries to get the filename automatically
StorageFilename string `json:"storageFilename"`

// Context may be specified for requests
Ctx context.Context `json:"-"`
}

type Response struct {
Expand Down
2 changes: 0 additions & 2 deletions common/constants/verdict/verdict.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,5 @@ const (
CF Verdict = "CF" // Check failed
SK Verdict = "SK" // Skipped

QD Verdict = "QD" // Queued
CL Verdict = "CL" // Compiling
RU Verdict = "RU" // Running
)
29 changes: 27 additions & 2 deletions common/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@ package db

import (
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"testing_system/common/config"
"testing_system/common/db/models"
"testing_system/lib/logger"
)

func NewDB(config config.DBConfig) (*gorm.DB, error) {
db, err := gorm.Open(postgres.Open(config.Dsn), &gorm.Config{})
var db *gorm.DB
var err error
if config.InMemory {
db, err = newInMemoryDB()
} else {
db, err = newPostgresDB(config)
}
if err != nil {
return nil, logger.Error("Can't open database with dsn=\"%v\" because of %v:", config.Dsn, err)
return nil, err
}

if err = db.AutoMigrate(&models.Problem{}); err != nil {
return nil, logger.Error("Can't migrate Problem: %v", err)
}
Expand All @@ -21,3 +29,20 @@ func NewDB(config config.DBConfig) (*gorm.DB, error) {
}
return db, err
}

func newPostgresDB(config config.DBConfig) (*gorm.DB, error) {
db, err := gorm.Open(postgres.Open(config.Dsn), &gorm.Config{})
if err != nil {
return nil, logger.Error("Can't open database with dsn=\"%v\" because of %v:", config.Dsn, err)
}
return db, nil
}

func newInMemoryDB() (*gorm.DB, error) {
db, err := gorm.Open(sqlite.Open("file:ts?mode=memory&cache=shared"), &gorm.Config{})
if err != nil {
return nil, err
}
logger.Warn("InMemory DB should not be used in production, consider using postgres db instead")
return db, nil
}
10 changes: 5 additions & 5 deletions common/db/models/models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestTestResultSerialization(t *testing.T) {
t.Run("json", func(t *testing.T) {
b, err := json.Marshal(testResult)
require.Nil(t, err)
require.Equal(t, `{"testNumber":1,"verdict":"OK","time":"5s","memory":"5m"}`, string(b))
require.Equal(t, `{"TestNumber":1,"Verdict":"OK","Time":"5s","Memory":"5m"}`, string(b))

var newTestResult TestResult
err = json.Unmarshal(b, &newTestResult)
Expand All @@ -48,10 +48,10 @@ func TestTestResultSerialization(t *testing.T) {
t.Run("yaml", func(t *testing.T) {
b, err := yaml.Marshal(testResult)
require.Nil(t, err)
require.Equal(t, `testNumber: 1
verdict: OK
time: 5s
memory: 5m
require.Equal(t, `TestNumber: 1
Verdict: OK
Time: 5s
Memory: 5m
`, string(b))
var newTestResult TestResult
err = yaml.Unmarshal(b, &newTestResult)
Expand Down
16 changes: 8 additions & 8 deletions common/db/models/problem.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,27 @@ const (
type Problem struct {
gorm.Model

ProblemType ProblemType
ProblemType ProblemType `yaml:"ProblemType"`

TimeLimit customfields.Time
MemoryLimit customfields.Memory
TimeLimit customfields.Time `yaml:"TimeLimit"`
MemoryLimit customfields.Memory `yaml:"MemoryLimit"`

TestsNumber uint64
TestsNumber uint64 `yaml:"TestsNumber"`

// WallTimeLimit specifies maximum execution and wait time.
// By default, it is max(5s, TimeLimit * 2)
WallTimeLimit *customfields.Time
WallTimeLimit *customfields.Time `yaml:"WallTimeLimit,omitempty"`

// MaxOpenFiles specifies maximum number of files, opened by testing system.
// By default, it is 64
MaxOpenFiles *uint64
MaxOpenFiles *uint64 `yaml:"MaxOpenFiles,omitempty"`

// MaxThreads specifies maximum number of threads and/or processes
// By default, it is single thread
// If MaxThreads equals to -1, any number of threads allowed
MaxThreads *int64
MaxThreads *int64 `yaml:"MaxThreads,omitempty"`

// MaxOutputSize specifies maximum output in EACH file.
// By default, it is 1g
MaxOutputSize *customfields.Memory
MaxOutputSize *customfields.Memory `yaml:"MaxOutputSize,omitempty"`
}
20 changes: 10 additions & 10 deletions common/db/models/submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (
)

type TestResult struct {
TestNumber uint64 `json:"testNumber" yaml:"testNumber"`
Points *float64 `json:"points,omitempty" yaml:"points,omitempty"`
Verdict verdict.Verdict `json:"verdict" yaml:"verdict"`
Time customfields.Time `json:"time" yaml:"time"`
Memory customfields.Memory `json:"memory" yaml:"memory"`
TestNumber uint64 `json:"TestNumber" yaml:"TestNumber"`
Points *float64 `json:"Points,omitempty" yaml:"Points,omitempty"`
Verdict verdict.Verdict `json:"Verdict" yaml:"Verdict"`
Time customfields.Time `json:"Time" yaml:"Time"`
Memory customfields.Memory `json:"Memory" yaml:"Memory"`
}

type TestResults []TestResult
Expand Down Expand Up @@ -44,10 +44,10 @@ func (t TestResults) GormDBDataType(db *gorm.DB, field *schema.Field) string {

type Submission struct {
gorm.Model
ProblemID uint64
Language string
ProblemID uint64 `json:"ProblemID" yaml:"ProblemID"`
Language string `json:"Language" yaml:"Language"`

Score float64
Verdict verdict.Verdict
TestResults TestResults `gorm:"type:jsonb"`
Score float64 `json:"Score" yaml:"Score"`
Verdict verdict.Verdict `json:"Verdict" yaml:"Verdict"`
TestResults TestResults `gorm:"type:jsonb" json:"TestResults" yaml:"TestResults"`
}
Loading