184 lines
5.1 KiB
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
|
|
}
|