woj-server/internal/service/runner/run_judge.go

184 lines
5.1 KiB
Go

package runner
import (
"fmt"
"git.0x7f.app/WOJ/woj-server/internal/e"
"git.0x7f.app/WOJ/woj-server/pkg/file"
"git.0x7f.app/WOJ/woj-server/pkg/utils"
"go.uber.org/zap"
"os"
"path/filepath"
"time"
)
func (s *service) problemRun(version uint, user string, lang string, config *Config) {
workDir := filepath.Join(UserDir, user)
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "data", "input")
// woj-sandbox killer will add 2 more seconds, here we add 2 more seconds
timeout := time.Duration((config.Runtime.TimeLimit+1000)/1000+2+2) * time.Second
ids := make([]int, 0)
for _, task := range config.Tasks {
f := func(id int) func() {
return func() {
testCase := filepath.Join(dataDir, fmt.Sprintf("%d.input", id))
targetFile := filepath.Join(workDir, fmt.Sprintf("%s.out", user))
ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id))
ifoFile := filepath.Join(workDir, fmt.Sprintf("%d.info", id))
err := utils.NewMust().
DoAny(func() error { return os.Remove(ansFile) }).
DoAny(func() error { return os.Remove(ifoFile) }).
Do(func() error { return file.TouchErr(ansFile) }).
Do(func() error { return file.TouchErr(ifoFile) }).
Do(func() error {
args := []string{
"--cpus", "1",
"--network", "none",
"-v", fmt.Sprintf("%s:/woj/problem/data/input/%d.input:ro", testCase, id),
"-v", fmt.Sprintf("%s:/woj/user/%s.out:ro", targetFile, user),
"-v", fmt.Sprintf("%s:/woj/user/%d.out.usr", ansFile, id),
"-v", fmt.Sprintf("%s:/woj/user/%d.info", ifoFile, id),
"git.0x7f.app/woj/ubuntu-run:latest",
"sh", "-c",
fmt.Sprintf("cd /woj/user && /woj/framework/scripts/woj_launcher "+
"--memory_limit=%d "+
"--nproc_limit=%d "+
"--time_limit=%d "+
"--sandbox_template=%s "+
"--sandbox_action=ret "+
"--uid=1000 "+
"--gid=1000 "+
"--file_input=/woj/problem/data/input/%d.input "+
"--file_output=/woj/user/%d.out.usr "+
"--file_info=/woj/user/%d.info "+
"-program=/woj/user/%s.out",
config.Runtime.MemoryLimit,
config.Runtime.NProcLimit,
config.Runtime.TimeLimit,
lang,
id, id, id,
user,
),
}
runArgs := &podmanArgs{
executeArgs: executeArgs{
args: args,
timeout: timeout,
},
}
return s.podmanRun(runArgs)
}).
Done()
if err != nil {
s.log.Info("run failed",
zap.Error(err),
zap.Uint("version", version),
zap.String("user", user),
zap.String("lang", lang),
)
}
}
}(task.Id)
id := s.pool.AddTask(f)
ids = append(ids, id)
}
for _, id := range ids {
s.pool.WaitForTask(id)
}
}
func (s *service) problemJudge(version uint, user string, lang string, config *Config) {
workDir := filepath.Join(UserDir, user)
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "data")
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "judge")
ids := make([]int, 0)
for _, task := range config.Tasks {
f := func(id int) func() {
return func() {
ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id))
jdgFile := filepath.Join(workDir, fmt.Sprintf("%d.judge", id))
c, ok := s.getLangInfo(config, lang)
if !ok {
return
}
err := utils.NewMust().
DoAny(func() error { return os.Remove(jdgFile) }).
Do(func() error { return file.TouchErr(jdgFile) }).
Do(func() error {
args := []string{
"-v", fmt.Sprintf("%s:/woj/problem/judge:ro", judgeDir),
"-v", fmt.Sprintf("%s:/woj/problem/data:ro", dataDir),
"-v", fmt.Sprintf("%s:/woj/user/%d.out.usr", ansFile, id),
"-v", fmt.Sprintf("%s:/woj/user/%d.judge", jdgFile, id),
"-e", fmt.Sprintf("TEST_NUM=%d", id),
"-e", fmt.Sprintf("CMP=%s", c.Cmp),
"git.0x7f.app/woj/ubuntu-full:latest",
"sh", "-c",
fmt.Sprintf("cd /woj/user && make -f %s judge", s.getLangScript(&c, lang)),
}
runArgs := &podmanArgs{
executeArgs: executeArgs{
args: args,
},
}
return s.podmanRun(runArgs)
}).
Done()
if err != nil {
s.log.Info("judge failed",
zap.Error(err),
zap.Uint("version", version),
zap.String("user", user),
zap.String("lang", lang),
)
}
}
}(task.Id)
id := s.pool.AddTask(f)
ids = append(ids, id)
}
for _, id := range ids {
s.pool.WaitForTask(id)
}
}
func (s *service) RunAndJudge(version uint, user string, lang string) (JudgeStatus, int32, e.Status) {
// 1. ensure problem/user exists
status := s.check(version, user, lang)
if status != e.Success {
return JudgeStatus{Message: "check failed"}, 0, status
}
// 2. config
config, err := s.ParseConfig(version, false)
if err != nil {
return JudgeStatus{Message: "parse config failed"}, 0, e.RunnerProblemParseFailed
}
// 3. run user program
s.problemRun(version, user, lang, &config)
// 4. run judger
s.problemJudge(version, user, lang, &config)
// 5. check result
result, pts := s.checkResults(user, &config)
return result, pts, e.Success
}