feat: unify argument passing and errors in runner
This commit is contained in:
parent
81b42f782e
commit
bb53ae280f
@ -19,7 +19,7 @@ func (h *handler) Build(_ context.Context, t *asynq.Task) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.log.Debug("build", zap.Any("payload", p))
|
h.log.Debug("build", zap.Any("payload", p))
|
||||||
meta := runner.JudgeMeta{Version: p.ProblemVersionID}
|
meta := runner.JudgeMeta{Run: runner.JudgeMetaRun{Version: p.ProblemVersionID}}
|
||||||
|
|
||||||
status, ctx := func() (e.Status, string) {
|
status, ctx := func() (e.Status, string) {
|
||||||
url, status := h.storageService.Get(p.StorageKey, time.Second*60*5)
|
url, status := h.storageService.Get(p.StorageKey, time.Second*60*5)
|
||||||
|
@ -15,6 +15,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
statusSystemError = runner.JudgeStatus{
|
||||||
|
Message: "System Error",
|
||||||
|
CompileMessage: "",
|
||||||
|
Tasks: []runner.TaskStatus{{Verdict: runner.VerdictSystemError, Message: "System Error"}},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func (h *handler) Judge(_ context.Context, t *asynq.Task) error {
|
func (h *handler) Judge(_ context.Context, t *asynq.Task) error {
|
||||||
var p model.SubmitJudgePayload
|
var p model.SubmitJudgePayload
|
||||||
if err := json.Unmarshal(t.Payload(), &p); err != nil {
|
if err := json.Unmarshal(t.Payload(), &p); err != nil {
|
||||||
@ -23,45 +31,59 @@ func (h *handler) Judge(_ context.Context, t *asynq.Task) error {
|
|||||||
|
|
||||||
user := utils.RandomString(16)
|
user := utils.RandomString(16)
|
||||||
h.log.Debug("judge", zap.Any("payload", p), zap.String("user", user))
|
h.log.Debug("judge", zap.Any("payload", p), zap.String("user", user))
|
||||||
meta := runner.JudgeMeta{Version: p.ProblemVersionID, User: user, Lang: p.Submission.Language}
|
meta := runner.JudgeMeta{
|
||||||
|
Run: runner.JudgeMetaRun{
|
||||||
SystemError := &runner.JudgeStatus{
|
Version: p.ProblemVersionID,
|
||||||
Message: "System Error",
|
User: user,
|
||||||
Tasks: []runner.TaskStatus{{Verdict: runner.VerdictSystemError, Message: "API Error"}},
|
Lang: p.Submission.Language,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
status, point, ctx := func() (e.Status, int32, *runner.JudgeStatus) {
|
status, point, ctx := func() (e.Status, int32, *runner.JudgeStatus) {
|
||||||
|
|
||||||
// 1. write user code
|
// 1. write user code
|
||||||
userCode := filepath.Join(runner.UserDir, user, fmt.Sprintf("%s.%s", user, p.Submission.Language))
|
userCode := filepath.Join(runner.UserDir, user, fmt.Sprintf("%s.%s", user, p.Submission.Language))
|
||||||
if !file.Touch(userCode) {
|
if !file.Touch(userCode) {
|
||||||
return e.InternalError, 0, SystemError
|
return e.InternalError, 0, &statusSystemError
|
||||||
}
|
}
|
||||||
err := file.Write(userCode, []byte(p.Submission.Code))
|
err := file.Write(userCode, []byte(p.Submission.Code))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return e.InternalError, 0, SystemError
|
return e.InternalError, 0, &statusSystemError
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. check problem
|
// 2. check problem
|
||||||
if !h.runnerService.ProblemExists(&meta) {
|
if !h.runnerService.ProblemExists(&meta) {
|
||||||
url, status := h.storageService.Get(p.StorageKey, time.Second*60*5)
|
url, status := h.storageService.Get(p.StorageKey, time.Second*60*5)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
return status, 0, SystemError
|
return status, 0, &statusSystemError
|
||||||
}
|
}
|
||||||
|
|
||||||
_, status = h.runnerService.NewProblem(&meta, url, false)
|
_, status = h.runnerService.NewProblem(&meta, url, false)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
return status, 0, SystemError
|
return status, 0, &statusSystemError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. compile
|
// 3. validate
|
||||||
|
if ret, status := h.runnerService.ValidatePath(&meta); status != e.Success {
|
||||||
|
return status, 0, ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. extract config
|
||||||
|
if status := h.runnerService.GetConfig(&meta, true); status != e.Success {
|
||||||
|
return e.RunnerProblemParseFailed, 0, &runner.JudgeStatus{
|
||||||
|
Message: fmt.Sprintf("System Error: Problem Malformed (%d:%s)", status, status.String()),
|
||||||
|
CompileMessage: "",
|
||||||
|
Tasks: []runner.TaskStatus{{Verdict: runner.VerdictSystemError, Message: "System Error"}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. compile
|
||||||
compileResult, status := h.runnerService.Compile(&meta)
|
compileResult, status := h.runnerService.Compile(&meta)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
return status, 0, compileResult
|
return status, 0, compileResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. run and judge
|
// 6. run and judge
|
||||||
result, point, status := h.runnerService.RunAndJudge(&meta)
|
result, point, status := h.runnerService.RunAndJudge(&meta)
|
||||||
return status, point, result
|
return status, point, result
|
||||||
}()
|
}()
|
||||||
|
@ -22,10 +22,20 @@ const (
|
|||||||
ContainerImageRun = "git.0x7f.app/woj/ubuntu-run:latest"
|
ContainerImageRun = "git.0x7f.app/woj/ubuntu-run:latest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type JudgeMetaRun struct {
|
||||||
|
Version uint // problem version id
|
||||||
|
User string // user id
|
||||||
|
Lang string // language
|
||||||
|
}
|
||||||
|
|
||||||
|
type JudgeMetaConfig struct {
|
||||||
|
All *Config
|
||||||
|
Lang *ConfigLanguage
|
||||||
|
}
|
||||||
|
|
||||||
type JudgeMeta struct {
|
type JudgeMeta struct {
|
||||||
Version uint
|
Run JudgeMetaRun
|
||||||
User string
|
Cfg JudgeMetaConfig
|
||||||
Lang string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -41,33 +51,47 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) ProblemExists(meta *JudgeMeta) bool {
|
func (s *service) ProblemExists(meta *JudgeMeta) bool {
|
||||||
problemPath := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version))
|
problemPath := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version))
|
||||||
return file.Exist(problemPath)
|
return file.Exist(problemPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) ValidatePath(meta *JudgeMeta) e.Status {
|
func (s *service) ValidatePath(meta *JudgeMeta) (*JudgeStatus, e.Status) {
|
||||||
|
gen := func(status e.Status) (*JudgeStatus, e.Status) {
|
||||||
|
return &JudgeStatus{
|
||||||
|
Message: fmt.Sprintf("System Error: Problem/User Files Not Found (%d:%s)", status, status.String()),
|
||||||
|
CompileMessage: "",
|
||||||
|
Tasks: []TaskStatus{{Verdict: VerdictSystemError, Message: "System Error"}},
|
||||||
|
}, status
|
||||||
|
}
|
||||||
|
|
||||||
if !s.ProblemExists(meta) {
|
if !s.ProblemExists(meta) {
|
||||||
s.log.Info("problem not exists", zap.Uint("version", meta.Version))
|
s.log.Error("problem not exists", zap.Uint("version", meta.Run.Version))
|
||||||
return e.RunnerProblemNotExist
|
return gen(e.RunnerProblemNotExist)
|
||||||
}
|
}
|
||||||
|
|
||||||
userPath := filepath.Join(UserDir, meta.User, fmt.Sprintf("%s.%s", meta.User, meta.Lang))
|
userPath := filepath.Join(UserDir, meta.Run.User, fmt.Sprintf("%s.%s", meta.Run.User, meta.Run.Lang))
|
||||||
if !file.Exist(userPath) {
|
if !file.Exist(userPath) {
|
||||||
s.log.Info("user program not exists", zap.String("user", meta.User), zap.String("lang", meta.Lang))
|
s.log.Error("user program not exists", zap.String("user", meta.Run.User), zap.String("lang", meta.Run.Lang))
|
||||||
return e.RunnerUserNotExist
|
return gen(e.RunnerUserNotExist)
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.Success
|
return nil, e.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) GetConfig(meta *JudgeMeta, skipCheck bool) (*Config, *ConfigLanguage, e.Status) {
|
func (s *service) GetConfig(meta *JudgeMeta, skipCheck bool) e.Status {
|
||||||
config, err := s.ParseConfig(meta, skipCheck)
|
config, err := s.ParseConfig(meta, skipCheck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, e.RunnerProblemParseFailed
|
return e.RunnerProblemParseFailed
|
||||||
}
|
}
|
||||||
cLang, ok := config.FilterLanguage(meta.Lang)
|
|
||||||
|
lang, ok := config.FilterLanguage(meta.Run.Lang)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, e.RunnerLanguageNotSupported
|
return e.RunnerLanguageNotSupported
|
||||||
}
|
}
|
||||||
return config, cLang, e.Success
|
|
||||||
|
meta.Cfg = JudgeMetaConfig{
|
||||||
|
All: config,
|
||||||
|
Lang: lang,
|
||||||
|
}
|
||||||
|
return e.Success
|
||||||
}
|
}
|
||||||
|
@ -14,62 +14,44 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *service) Compile(meta *JudgeMeta) (*JudgeStatus, e.Status) {
|
func (s *service) Compile(meta *JudgeMeta) (*JudgeStatus, e.Status) {
|
||||||
// 1. ensure problem/user exists
|
// 1. prepare judge environment
|
||||||
status := s.ValidatePath(meta)
|
workDir := filepath.Join(UserDir, meta.Run.User)
|
||||||
if status != e.Success {
|
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "judge")
|
||||||
return &JudgeStatus{Message: "check failed"}, status
|
|
||||||
}
|
|
||||||
|
|
||||||
config, cLang, status := s.GetConfig(meta, true)
|
sourceFile := filepath.Join(workDir, fmt.Sprintf("%s.%s", meta.Run.User, meta.Run.Lang))
|
||||||
if status != e.Success {
|
targetFile := filepath.Join(workDir, fmt.Sprintf("%s.out", meta.Run.User))
|
||||||
s.log.Error("[compile] parse config failed", zap.Any("meta", *meta))
|
|
||||||
return &JudgeStatus{
|
|
||||||
Message: "parse config failed",
|
|
||||||
Tasks: []TaskStatus{{Verdict: VerdictSystemError, Message: "parse config failed"}},
|
|
||||||
}, e.RunnerProblemParseFailed
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. prepare judge environment
|
logFile := filepath.Join(workDir, fmt.Sprintf("%s.compile.log", meta.Run.User))
|
||||||
workDir := filepath.Join(UserDir, meta.User)
|
|
||||||
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version), "judge")
|
|
||||||
|
|
||||||
sourceFile := filepath.Join(workDir, fmt.Sprintf("%s.%s", meta.User, meta.Lang))
|
|
||||||
targetFile := filepath.Join(workDir, fmt.Sprintf("%s.out", meta.User))
|
|
||||||
|
|
||||||
logFile := filepath.Join(workDir, fmt.Sprintf("%s.compile.log", meta.User))
|
|
||||||
log, err := os.Create(logFile)
|
log, err := os.Create(logFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Error("[compile] create log file failed", zap.Error(err))
|
s.log.Error("[compile] create log file failed", zap.Error(err))
|
||||||
return &JudgeStatus{
|
return &JudgeStatus{
|
||||||
Message: "create log file failed",
|
Message: "System Error: Unable to create log file",
|
||||||
Tasks: []TaskStatus{{Verdict: VerdictSystemError, Message: "create log file failed"}},
|
CompileMessage: "",
|
||||||
|
Tasks: []TaskStatus{{Verdict: VerdictSystemError, Message: "System Error"}},
|
||||||
}, e.RunnerUserCompileFailed
|
}, e.RunnerUserCompileFailed
|
||||||
}
|
}
|
||||||
defer func(log *os.File) {
|
defer func(log *os.File) {
|
||||||
_ = log.Close()
|
_ = log.Close()
|
||||||
}(log)
|
}(log)
|
||||||
|
|
||||||
// 3. compile
|
// 2. compile
|
||||||
err = utils.NewMust().
|
err = utils.NewMust().
|
||||||
DoAny(func() error { return os.Remove(targetFile) }).
|
DoAny(func() error { return os.Remove(targetFile) }).
|
||||||
Do(func() error { return file.TouchErr(targetFile) }).
|
Do(func() error { return file.TouchErr(targetFile) }).
|
||||||
Do(func() error {
|
Do(func() error {
|
||||||
l, ok := config.FilterLanguage(meta.Lang)
|
script := meta.Cfg.Lang.JudgeScript()
|
||||||
if !ok {
|
|
||||||
return e.RunnerProblemParseFailed.AsError()
|
|
||||||
}
|
|
||||||
script := l.JudgeScript()
|
|
||||||
|
|
||||||
args := &RunArgs{
|
args := &RunArgs{
|
||||||
Program: ProgramArgs{
|
Program: ProgramArgs{
|
||||||
Args: []string{"sh", "-c", fmt.Sprintf("cd /woj/user && make -f %s compile", script)},
|
Args: []string{"sh", "-c", fmt.Sprintf("cd /woj/user && make -f %s compile", script)},
|
||||||
Env: []string{fmt.Sprintf("USER_PROG=%s", meta.User), fmt.Sprintf("LANG=%s", meta.Lang)},
|
Env: []string{fmt.Sprintf("USER_PROG=%s", meta.Run.User), fmt.Sprintf("LANG=%s", meta.Run.Lang)},
|
||||||
},
|
},
|
||||||
Runtime: RuntimeArgs{
|
Runtime: RuntimeArgs{
|
||||||
Image: ContainerImageFull,
|
Image: ContainerImageFull,
|
||||||
Pid: int64(cLang.Runtime.Compile.NProcLimit + 2), // bash + make
|
Pid: int64(meta.Cfg.Lang.Runtime.Compile.NProcLimit + 2), // bash + make
|
||||||
Memory: uint64(cLang.Runtime.Compile.MemoryLimit * 1024 * 1024),
|
Memory: uint64(meta.Cfg.Lang.Runtime.Compile.MemoryLimit * 1024 * 1024),
|
||||||
Timeout: time.Duration((cLang.Runtime.Compile.TimeLimit+1000)/1000) * time.Second,
|
Timeout: time.Duration((meta.Cfg.Lang.Runtime.Compile.TimeLimit+1000)/1000) * time.Second,
|
||||||
},
|
},
|
||||||
IO: IOArgs{
|
IO: IOArgs{
|
||||||
Output: log,
|
Output: log,
|
||||||
@ -85,13 +67,13 @@ func (s *service) Compile(meta *JudgeMeta) (*JudgeStatus, e.Status) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Source: sourceFile,
|
Source: sourceFile,
|
||||||
Destination: fmt.Sprintf("/woj/user/%s.%s", meta.User, meta.Lang),
|
Destination: fmt.Sprintf("/woj/user/%s.%s", meta.Run.User, meta.Run.Lang),
|
||||||
Type: "bind",
|
Type: "bind",
|
||||||
Options: []string{"rbind", "ro"},
|
Options: []string{"rbind", "ro"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Source: targetFile,
|
Source: targetFile,
|
||||||
Destination: fmt.Sprintf("/woj/user/%s.out", meta.User),
|
Destination: fmt.Sprintf("/woj/user/%s.out", meta.Run.User),
|
||||||
Type: "bind",
|
Type: "bind",
|
||||||
Options: []string{"rbind"},
|
Options: []string{"rbind"},
|
||||||
},
|
},
|
||||||
@ -102,6 +84,7 @@ func (s *service) Compile(meta *JudgeMeta) (*JudgeStatus, e.Status) {
|
|||||||
}).
|
}).
|
||||||
Done()
|
Done()
|
||||||
|
|
||||||
|
status := e.Success
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Info("[compile] compile failed", zap.Error(err), zap.Any("meta", *meta))
|
s.log.Info("[compile] compile failed", zap.Error(err), zap.Any("meta", *meta))
|
||||||
status = e.RunnerUserCompileFailed
|
status = e.RunnerUserCompileFailed
|
||||||
@ -115,7 +98,8 @@ func (s *service) Compile(meta *JudgeMeta) (*JudgeStatus, e.Status) {
|
|||||||
|
|
||||||
if !file.Exist(targetFile) || file.Empty(targetFile) {
|
if !file.Exist(targetFile) || file.Empty(targetFile) {
|
||||||
return &JudgeStatus{
|
return &JudgeStatus{
|
||||||
Message: "compile failed",
|
Message: "Compile Failed",
|
||||||
|
CompileMessage: msgText,
|
||||||
Tasks: []TaskStatus{{Verdict: VerdictCompileError, Message: msgText}}},
|
Tasks: []TaskStatus{{Verdict: VerdictCompileError, Message: msgText}}},
|
||||||
utils.If(status == e.Success, e.RunnerUserCompileFailed, status)
|
utils.If(status == e.Success, e.RunnerUserCompileFailed, status)
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ func (c *Config) FilterLanguage(lang string) (*ConfigLanguage, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) ParseConfig(meta *JudgeMeta, skipCheck bool) (*Config, error) {
|
func (s *service) ParseConfig(meta *JudgeMeta, skipCheck bool) (*Config, error) {
|
||||||
base := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version))
|
base := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version))
|
||||||
file := filepath.Join(base, "config.json")
|
file := filepath.Join(base, "config.json")
|
||||||
|
|
||||||
data, err := os.ReadFile(file)
|
data, err := os.ReadFile(file)
|
||||||
|
@ -14,8 +14,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *service) DownloadProblem(meta *JudgeMeta, url string) e.Status {
|
func (s *service) DownloadProblem(meta *JudgeMeta, url string) e.Status {
|
||||||
zipPath := filepath.Join(TmpDir, fmt.Sprintf("%d.zip", meta.Version))
|
zipPath := filepath.Join(TmpDir, fmt.Sprintf("%d.zip", meta.Run.Version))
|
||||||
problemPath := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version))
|
problemPath := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version))
|
||||||
|
|
||||||
err := down.Down(zipPath, url)
|
err := down.Down(zipPath, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -37,21 +37,21 @@ func (s *service) PrebuildProblem(meta *JudgeMeta, config *Config, force bool) e
|
|||||||
return e.RunnerProblemNotExist
|
return e.RunnerProblemNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
mark := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version), ".mark.prebuild")
|
mark := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), ".mark.prebuild")
|
||||||
if force {
|
if force {
|
||||||
_ = os.Remove(mark)
|
_ = os.Remove(mark)
|
||||||
} else if file.Exist(mark) {
|
} else if file.Exist(mark) {
|
||||||
return e.Success
|
return e.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
prebuildScript := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version), "judge", "prebuild.Makefile")
|
prebuildScript := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "judge", "prebuild.Makefile")
|
||||||
if !file.Exist(prebuildScript) {
|
if !file.Exist(prebuildScript) {
|
||||||
s.log.Info("[new] prebuild script not found", zap.String("path", prebuildScript), zap.Uint("version", meta.Version))
|
s.log.Info("[new] prebuild script not found", zap.String("path", prebuildScript), zap.Uint("version", meta.Run.Version))
|
||||||
return e.Success
|
return e.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version), "data")
|
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "data")
|
||||||
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version), "judge")
|
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "judge")
|
||||||
|
|
||||||
args := &RunArgs{
|
args := &RunArgs{
|
||||||
Program: ProgramArgs{
|
Program: ProgramArgs{
|
||||||
@ -83,7 +83,7 @@ func (s *service) PrebuildProblem(meta *JudgeMeta, config *Config, force bool) e
|
|||||||
err := s.pool.WaitForTask(id)
|
err := s.pool.WaitForTask(id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warn("[new] prebuild problem failed", zap.Error(err), zap.Uint("version", meta.Version))
|
s.log.Warn("[new] prebuild problem failed", zap.Error(err), zap.Uint("version", meta.Run.Version))
|
||||||
return e.RunnerProblemPrebuildFailed
|
return e.RunnerProblemPrebuildFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ func (s *service) PrebuildProblem(meta *JudgeMeta, config *Config, force bool) e
|
|||||||
|
|
||||||
func (s *service) NewProblem(meta *JudgeMeta, url string, force bool) (*Config, e.Status) {
|
func (s *service) NewProblem(meta *JudgeMeta, url string, force bool) (*Config, e.Status) {
|
||||||
if force {
|
if force {
|
||||||
problemPath := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version))
|
problemPath := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version))
|
||||||
_ = os.RemoveAll(problemPath)
|
_ = os.RemoveAll(problemPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ func (s *service) NewProblem(meta *JudgeMeta, url string, force bool) (*Config,
|
|||||||
|
|
||||||
cfg, err := s.ParseConfig(meta, false)
|
cfg, err := s.ParseConfig(meta, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Info("[new] parse problem failed", zap.Error(err), zap.Uint("version", meta.Version))
|
s.log.Info("[new] parse problem failed", zap.Error(err), zap.Uint("version", meta.Run.Version))
|
||||||
return &Config{}, e.RunnerProblemParseFailed
|
return &Config{}, e.RunnerProblemParseFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,43 +13,43 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *service) SandboxArgsBuilder(meta *JudgeMeta, cLang *ConfigLanguage, id int) string {
|
func (s *service) SandboxArgsBuilder(meta *JudgeMeta, id int) string {
|
||||||
var args []string
|
var args []string
|
||||||
|
|
||||||
args = append(args, fmt.Sprintf("--memory_limit=%d", cLang.Runtime.Run.MemoryLimit))
|
args = append(args, fmt.Sprintf("--memory_limit=%d", meta.Cfg.Lang.Runtime.Run.MemoryLimit))
|
||||||
args = append(args, fmt.Sprintf("--nproc_limit=%d", cLang.Runtime.Run.NProcLimit))
|
args = append(args, fmt.Sprintf("--nproc_limit=%d", meta.Cfg.Lang.Runtime.Run.NProcLimit))
|
||||||
args = append(args, fmt.Sprintf("--time_limit=%d", cLang.Runtime.Run.TimeLimit))
|
args = append(args, fmt.Sprintf("--time_limit=%d", meta.Cfg.Lang.Runtime.Run.TimeLimit))
|
||||||
args = append(args, fmt.Sprintf("--sandbox_template=%s", cLang.Lang))
|
args = append(args, fmt.Sprintf("--sandbox_template=%s", meta.Cfg.Lang.Lang))
|
||||||
args = append(args, fmt.Sprintf("--sandbox_action=ret"))
|
args = append(args, fmt.Sprintf("--sandbox_action=ret"))
|
||||||
args = append(args, fmt.Sprintf("--uid=1000"))
|
args = append(args, fmt.Sprintf("--uid=1000"))
|
||||||
args = append(args, fmt.Sprintf("--gid=1000"))
|
args = append(args, fmt.Sprintf("--gid=1000"))
|
||||||
args = append(args, fmt.Sprintf("--file_input=/woj/problem/data/input/%d.input", id))
|
args = append(args, fmt.Sprintf("--file_input=/woj/problem/data/input/%d.input", id))
|
||||||
args = append(args, fmt.Sprintf("--file_output=/woj/user/%d.out.usr", id))
|
args = append(args, fmt.Sprintf("--file_output=/woj/user/%d.out.usr", id))
|
||||||
args = append(args, fmt.Sprintf("--file_info=/woj/user/%d.info", id))
|
args = append(args, fmt.Sprintf("--file_info=/woj/user/%d.info", id))
|
||||||
args = append(args, fmt.Sprintf("--program=/woj/user/%s.out", meta.User))
|
args = append(args, fmt.Sprintf("--program=/woj/user/%s.out", meta.Run.User))
|
||||||
|
|
||||||
return strings.Join(args, " ")
|
return strings.Join(args, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) ProblemRun(meta *JudgeMeta, config *Config, cLang *ConfigLanguage) {
|
func (s *service) ProblemRun(meta *JudgeMeta) {
|
||||||
workDir := filepath.Join(UserDir, meta.User)
|
workDir := filepath.Join(UserDir, meta.Run.User)
|
||||||
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version), "data", "input")
|
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "data", "input")
|
||||||
|
|
||||||
runtimeArgs := RuntimeArgs{
|
runtimeArgs := RuntimeArgs{
|
||||||
Image: ContainerImageRun,
|
Image: ContainerImageRun,
|
||||||
// sh, woj_launcher:program, woj_launcher:killer, woj_launcher:stat
|
// sh, woj_launcher:program, woj_launcher:killer, woj_launcher:stat
|
||||||
Pid: int64(cLang.Runtime.Run.NProcLimit + 4),
|
Pid: int64(meta.Cfg.Lang.Runtime.Run.NProcLimit + 4),
|
||||||
Memory: uint64(cLang.Runtime.Run.MemoryLimit * 1024 * 1024),
|
Memory: uint64(meta.Cfg.Lang.Runtime.Run.MemoryLimit * 1024 * 1024),
|
||||||
// woj-sandbox killer will add 1 more second, here we add 1 also
|
// woj-sandbox killer will add 1 more second, here we add 1 also
|
||||||
Timeout: time.Duration((cLang.Runtime.Run.TimeLimit+1000)/1000+1+1) * time.Second,
|
Timeout: time.Duration((meta.Cfg.Lang.Runtime.Run.TimeLimit+1000)/1000+1+1) * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
ids := make([]int, 0)
|
ids := make([]int, 0)
|
||||||
for _, task := range config.Tasks {
|
for _, task := range meta.Cfg.All.Tasks {
|
||||||
f := func(id int) func() error {
|
f := func(id int) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
testCase := filepath.Join(dataDir, fmt.Sprintf("%d.input", id))
|
testCase := filepath.Join(dataDir, fmt.Sprintf("%d.input", id))
|
||||||
targetFile := filepath.Join(workDir, fmt.Sprintf("%s.out", meta.User))
|
targetFile := filepath.Join(workDir, fmt.Sprintf("%s.out", meta.Run.User))
|
||||||
ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id))
|
ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id))
|
||||||
ifoFile := filepath.Join(workDir, fmt.Sprintf("%d.info", id))
|
ifoFile := filepath.Join(workDir, fmt.Sprintf("%d.info", id))
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ func (s *service) ProblemRun(meta *JudgeMeta, config *Config, cLang *ConfigLangu
|
|||||||
Args: []string{
|
Args: []string{
|
||||||
"sh", "-c",
|
"sh", "-c",
|
||||||
"cd /woj/user && /woj/framework/scripts/woj_launcher " +
|
"cd /woj/user && /woj/framework/scripts/woj_launcher " +
|
||||||
s.SandboxArgsBuilder(meta, cLang, id),
|
s.SandboxArgsBuilder(meta, id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Runtime: runtimeArgs,
|
Runtime: runtimeArgs,
|
||||||
@ -72,7 +72,7 @@ func (s *service) ProblemRun(meta *JudgeMeta, config *Config, cLang *ConfigLangu
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Source: targetFile,
|
Source: targetFile,
|
||||||
Destination: fmt.Sprintf("/woj/user/%s.out", meta.User),
|
Destination: fmt.Sprintf("/woj/user/%s.out", meta.Run.User),
|
||||||
Type: "bind",
|
Type: "bind",
|
||||||
Options: []string{"rbind", "ro"},
|
Options: []string{"rbind", "ro"},
|
||||||
},
|
},
|
||||||
@ -114,21 +114,21 @@ func (s *service) ProblemRun(meta *JudgeMeta, config *Config, cLang *ConfigLangu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) ProblemJudge(meta *JudgeMeta, config *Config, cLang *ConfigLanguage) {
|
func (s *service) ProblemJudge(meta *JudgeMeta) {
|
||||||
workDir := filepath.Join(UserDir, meta.User)
|
workDir := filepath.Join(UserDir, meta.Run.User)
|
||||||
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version), "data")
|
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "data")
|
||||||
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Version), "judge")
|
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "judge")
|
||||||
script := cLang.JudgeScript()
|
script := meta.Cfg.Lang.JudgeScript()
|
||||||
|
|
||||||
runtimeArgs := RuntimeArgs{
|
runtimeArgs := RuntimeArgs{
|
||||||
Image: ContainerImageFull,
|
Image: ContainerImageFull,
|
||||||
Pid: int64(cLang.Runtime.Check.NProcLimit + 2), // bash + make
|
Pid: int64(meta.Cfg.Lang.Runtime.Check.NProcLimit + 2), // bash + make
|
||||||
Memory: uint64(cLang.Runtime.Check.MemoryLimit * 1024 * 1024),
|
Memory: uint64(meta.Cfg.Lang.Runtime.Check.MemoryLimit * 1024 * 1024),
|
||||||
Timeout: time.Duration((cLang.Runtime.Check.TimeLimit+1000)/1000) * time.Second,
|
Timeout: time.Duration((meta.Cfg.Lang.Runtime.Check.TimeLimit+1000)/1000) * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
ids := make([]int, 0)
|
ids := make([]int, 0)
|
||||||
for _, task := range config.Tasks {
|
for _, task := range meta.Cfg.All.Tasks {
|
||||||
f := func(id int) func() error {
|
f := func(id int) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id))
|
ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id))
|
||||||
@ -142,7 +142,7 @@ func (s *service) ProblemJudge(meta *JudgeMeta, config *Config, cLang *ConfigLan
|
|||||||
},
|
},
|
||||||
Env: []string{
|
Env: []string{
|
||||||
fmt.Sprintf("TEST_NUM=%d", id),
|
fmt.Sprintf("TEST_NUM=%d", id),
|
||||||
fmt.Sprintf("CMP=%s", cLang.Judge.Cmp),
|
fmt.Sprintf("CMP=%s", meta.Cfg.Lang.Judge.Cmp),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Runtime: runtimeArgs,
|
Runtime: runtimeArgs,
|
||||||
@ -197,26 +197,14 @@ func (s *service) ProblemJudge(meta *JudgeMeta, config *Config, cLang *ConfigLan
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) RunAndJudge(meta *JudgeMeta) (*JudgeStatus, int32, e.Status) {
|
func (s *service) RunAndJudge(meta *JudgeMeta) (*JudgeStatus, int32, e.Status) {
|
||||||
// 1. ensure problem/user exists
|
// 1. run user program
|
||||||
status := s.ValidatePath(meta)
|
s.ProblemRun(meta)
|
||||||
if status != e.Success {
|
|
||||||
return &JudgeStatus{Message: "check failed"}, 0, status
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. config
|
// 2. run judge
|
||||||
config, cLang, status := s.GetConfig(meta, true)
|
s.ProblemJudge(meta)
|
||||||
if status != e.Success {
|
|
||||||
return &JudgeStatus{Message: status.String()}, 0, status
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. run user program
|
// 3. check result
|
||||||
s.ProblemRun(meta, config, cLang)
|
result, pts := s.CheckResults(meta)
|
||||||
|
|
||||||
// 4. run judge
|
|
||||||
s.ProblemJudge(meta, config, cLang)
|
|
||||||
|
|
||||||
// 5. check result
|
|
||||||
result, pts := s.CheckResults(meta, config, cLang)
|
|
||||||
|
|
||||||
return result, pts, e.Success
|
return result, pts, e.Success
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,10 @@ type Service interface {
|
|||||||
ParseConfig(meta *JudgeMeta, skipCheck bool) (*Config, error)
|
ParseConfig(meta *JudgeMeta, skipCheck bool) (*Config, error)
|
||||||
// ProblemExists check if problem exists
|
// ProblemExists check if problem exists
|
||||||
ProblemExists(meta *JudgeMeta) bool
|
ProblemExists(meta *JudgeMeta) bool
|
||||||
|
// ValidatePath check if problem/user exists
|
||||||
|
ValidatePath(meta *JudgeMeta) (*JudgeStatus, e.Status)
|
||||||
|
// GetConfig return config and filter language config, will fill result to meta.Config
|
||||||
|
GetConfig(meta *JudgeMeta, skipCheck bool) e.Status
|
||||||
|
|
||||||
HealthCheck() error
|
HealthCheck() error
|
||||||
Shutdown() error
|
Shutdown() error
|
||||||
|
@ -48,6 +48,7 @@ type TaskStatus struct {
|
|||||||
|
|
||||||
type JudgeStatus struct {
|
type JudgeStatus struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
|
CompileMessage string `json:"compile_message"`
|
||||||
Tasks []TaskStatus `json:"tasks"`
|
Tasks []TaskStatus `json:"tasks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,19 +194,19 @@ func (t *TaskStatus) checkJudge(pts *map[int]int32) *TaskStatus {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) CheckResults(meta *JudgeMeta, config *Config, cLang *ConfigLanguage) (*JudgeStatus, int32) {
|
func (s *service) CheckResults(meta *JudgeMeta) (*JudgeStatus, int32) {
|
||||||
// CE will be processed in phase compile
|
// CE will be processed in phase compile
|
||||||
|
|
||||||
pts := map[int]int32{}
|
pts := map[int]int32{}
|
||||||
for _, task := range config.Tasks {
|
for _, task := range meta.Cfg.All.Tasks {
|
||||||
pts[task.Id] = task.Points
|
pts[task.Id] = task.Points
|
||||||
}
|
}
|
||||||
|
|
||||||
var results []TaskStatus
|
var results []TaskStatus
|
||||||
dir := filepath.Join(UserDir, meta.User)
|
dir := filepath.Join(UserDir, meta.Run.User)
|
||||||
var sum int32 = 0
|
var sum int32 = 0
|
||||||
|
|
||||||
for i := 1; i <= len(config.Tasks); i++ {
|
for i := 1; i <= len(meta.Cfg.All.Tasks); i++ {
|
||||||
result := TaskStatus{Id: i, Verdict: VerdictAccepted, Points: 0}
|
result := TaskStatus{Id: i, Verdict: VerdictAccepted, Points: 0}
|
||||||
|
|
||||||
info := filepath.Join(dir, fmt.Sprintf("%d.info", i))
|
info := filepath.Join(dir, fmt.Sprintf("%d.info", i))
|
||||||
@ -213,8 +214,8 @@ func (s *service) CheckResults(meta *JudgeMeta, config *Config, cLang *ConfigLan
|
|||||||
|
|
||||||
result.getInfoText(info).
|
result.getInfoText(info).
|
||||||
getInfo().
|
getInfo().
|
||||||
checkTime(cLang).
|
checkTime(meta.Cfg.Lang).
|
||||||
checkMemory(cLang).
|
checkMemory(meta.Cfg.Lang).
|
||||||
checkExit().
|
checkExit().
|
||||||
getJudgeText(judge).
|
getJudgeText(judge).
|
||||||
getJudge().
|
getJudge().
|
||||||
|
Loading…
Reference in New Issue
Block a user