From df62496537c42e98ef7a6802c34626dead8ee27a Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sun, 3 Mar 2024 01:29:34 -0500 Subject: [PATCH] feat: basic framework --- Makefile | 3 +- README.md | 18 ++---- cmd/joj3/main.go | 20 ++++++ cmd/tiger/root.go | 26 -------- go.mod | 14 ++-- go.sum | 53 +++------------ internal/app/healthcheck/main.go | 1 + internal/executors/all.go | 8 +++ internal/executors/dummy/executor.go | 21 ++++++ internal/executors/dummy/meta.go | 9 +++ internal/parsers/all.go | 8 +++ internal/parsers/dummy/meta.go | 9 +++ internal/parsers/dummy/parser.go | 15 +++++ internal/stage/executor.go | 15 +++++ internal/stage/parser.go | 18 ++++++ internal/stage/run.go | 49 ++++++++++++++ pkg/runner/root.go | 96 ---------------------------- 17 files changed, 194 insertions(+), 189 deletions(-) create mode 100644 cmd/joj3/main.go delete mode 100644 cmd/tiger/root.go create mode 100644 internal/app/healthcheck/main.go create mode 100644 internal/executors/all.go create mode 100644 internal/executors/dummy/executor.go create mode 100644 internal/executors/dummy/meta.go create mode 100644 internal/parsers/all.go create mode 100644 internal/parsers/dummy/meta.go create mode 100644 internal/parsers/dummy/parser.go create mode 100644 internal/stage/executor.go create mode 100644 internal/stage/parser.go create mode 100644 internal/stage/run.go delete mode 100644 pkg/runner/root.go diff --git a/Makefile b/Makefile index 79c0ab6..45f79e1 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ .PHONY: all clean BUILD_DIR = ./build +APP_NAME = joj3 all: - go build -o $(BUILD_DIR)/tiger ./cmd/tiger + go build -o $(BUILD_DIR)/$(APP_NAME) ./cmd/$(APP_NAME) clean: rm -rf $(BUILD_DIR)/* diff --git a/README.md b/README.md index b6e67cc..edbaba6 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,10 @@ # JOJ3 -## Try `tiger` - ```bash -$ make clean && make && sudo ./build/tiger python3 -c 'bytearray(1024 * 1024); 10 ** 10 ** 3; print("out"); import sys; print("err", file=sys.stderr)' +$ make clean && make && ./build/joj3 rm -rf ./build/* rm -rf *.out -go build -o ./build/tiger ./cmd/tiger -2024/03/01 01:25:34 INFO process created pid=3148763 -2024/03/01 01:25:34 INFO done success time=16.80708ms -ReturnCode: 0 -Stdout: out - -Stderr: err - -TimedOut: false -TimeNs: 0 -MemoryByte: 0 +go build -o ./build/joj3 ./cmd/joj3 +dummy stage 0: score: 0, comment: I'm a dummy +dummy stage 1: score: 0, comment: I'm a dummy ``` diff --git a/cmd/joj3/main.go b/cmd/joj3/main.go new file mode 100644 index 0000000..a48f167 --- /dev/null +++ b/cmd/joj3/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + + _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors" + _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers" + "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" +) + +func main() { + stages := stage.ParseStages() + results := stage.Run(stages) + for _, result := range results { + fmt.Printf( + "%s: score: %d, comment: %s\n", + result.Name, result.Score, result.Comment, + ) + } +} diff --git a/cmd/tiger/root.go b/cmd/tiger/root.go deleted file mode 100644 index 35a4cdf..0000000 --- a/cmd/tiger/root.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/runner" -) - -func main() { - if len(os.Args) < 2 { - fmt.Println("usage: " + os.Args[0] + " ") - os.Exit(1) - } - // hard limit of timeout 1000ms - runResult, err := runner.RunInCgroupsV1(os.Args[1:], "nobody", "/joj3.tiger", 1000) - if err != nil { - fmt.Printf("Error: %v\n", err) - } - fmt.Printf("ReturnCode: %d\n", runResult.ReturnCode) - fmt.Printf("Stdout: %s\n", runResult.Stdout) - fmt.Printf("Stderr: %s\n", runResult.Stderr) - fmt.Printf("TimedOut: %v\n", runResult.TimedOut) - fmt.Printf("TimeNs: %v\n", runResult.TimeNs) - fmt.Printf("MemoryByte: %v\n", runResult.MemoryByte) -} diff --git a/go.mod b/go.mod index d0fd59b..18c2a21 100644 --- a/go.mod +++ b/go.mod @@ -2,15 +2,11 @@ module focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3 go 1.22.0 -require ( - github.com/containerd/cgroups v1.1.0 - github.com/opencontainers/runtime-spec v1.2.0 -) +require github.com/criyle/go-judge v1.8.1 require ( - github.com/coreos/go-systemd/v22 v22.3.2 // indirect - github.com/docker/go-units v0.4.0 // indirect - github.com/godbus/dbus/v5 v5.0.4 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 // indirect + github.com/creack/pty v1.1.21 // indirect + github.com/criyle/go-sandbox v0.10.1 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.16.0 // indirect ) diff --git a/go.sum b/go.sum index bb37a6f..ad35e91 100644 --- a/go.sum +++ b/go.sum @@ -1,43 +1,10 @@ -github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= -github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= -github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= +github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/criyle/go-judge v1.8.1 h1:VI9OGz1MnLp9cv//gMVL8uruNwxSod5UmNwew8ZnfCA= +github.com/criyle/go-judge v1.8.1/go.mod h1:yZepeuMTmQXEJnBgHZQIGVuytRYWlLhQBQHLaL84N5w= +github.com/criyle/go-sandbox v0.10.1 h1:z9Il/UXQwKEvIwdr1wVheWWWAqGWtdTItBmEsWqFqT4= +github.com/criyle/go-sandbox v0.10.1/go.mod h1:ivPw/HEh5unxVRlXJxCgkgTCuy+cxTkQDX7D2XQf/kg= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/internal/app/healthcheck/main.go b/internal/app/healthcheck/main.go new file mode 100644 index 0000000..9b4e3b7 --- /dev/null +++ b/internal/app/healthcheck/main.go @@ -0,0 +1 @@ +package healthcheck diff --git a/internal/executors/all.go b/internal/executors/all.go new file mode 100644 index 0000000..706ba7b --- /dev/null +++ b/internal/executors/all.go @@ -0,0 +1,8 @@ +package executors + +import ( + _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/executors/dummy" +) + +// this file does nothing but imports to ensure all the init() functions +// in the subpackages are called diff --git a/internal/executors/dummy/executor.go b/internal/executors/dummy/executor.go new file mode 100644 index 0000000..5c8b296 --- /dev/null +++ b/internal/executors/dummy/executor.go @@ -0,0 +1,21 @@ +package dummy + +import ( + "github.com/criyle/go-judge/cmd/go-judge/model" + "github.com/criyle/go-judge/envexec" +) + +type Dummy struct{} + +func (e *Dummy) Run(model.Cmd) model.Result { + return model.Result{ + Status: model.Status(envexec.StatusInvalid), + ExitStatus: 0, + Error: "I'm a dummy", + Time: 0, + Memory: 0, + RunTime: 0, + Files: map[string]string{}, + FileIDs: map[string]string{}, + } +} diff --git a/internal/executors/dummy/meta.go b/internal/executors/dummy/meta.go new file mode 100644 index 0000000..e687e52 --- /dev/null +++ b/internal/executors/dummy/meta.go @@ -0,0 +1,9 @@ +package dummy + +import "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" + +var name = "dummy" + +func init() { + stage.RegisterExecutor(name, &Dummy{}) +} diff --git a/internal/parsers/all.go b/internal/parsers/all.go new file mode 100644 index 0000000..5ee6a62 --- /dev/null +++ b/internal/parsers/all.go @@ -0,0 +1,8 @@ +package parsers + +import ( + _ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy" +) + +// this file does nothing but imports to ensure all the init() functions +// in the subpackages are called diff --git a/internal/parsers/dummy/meta.go b/internal/parsers/dummy/meta.go new file mode 100644 index 0000000..fd9ba75 --- /dev/null +++ b/internal/parsers/dummy/meta.go @@ -0,0 +1,9 @@ +package dummy + +import "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" + +var name = "dummy" + +func init() { + stage.RegisterParser(name, &Dummy{}) +} diff --git a/internal/parsers/dummy/parser.go b/internal/parsers/dummy/parser.go new file mode 100644 index 0000000..89e6bb1 --- /dev/null +++ b/internal/parsers/dummy/parser.go @@ -0,0 +1,15 @@ +package dummy + +import ( + "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" + "github.com/criyle/go-judge/cmd/go-judge/model" +) + +type Dummy struct{} + +func (e *Dummy) Run(result model.Result, config string) stage.ParserResult { + return stage.ParserResult{ + Score: 0, + Comment: "I'm a dummy", + } +} diff --git a/internal/stage/executor.go b/internal/stage/executor.go new file mode 100644 index 0000000..ecf09d7 --- /dev/null +++ b/internal/stage/executor.go @@ -0,0 +1,15 @@ +package stage + +import ( + "github.com/criyle/go-judge/cmd/go-judge/model" +) + +var executorMap = map[string]Executor{} + +type Executor interface { + Run(model.Cmd) model.Result +} + +func RegisterExecutor(name string, executor Executor) { + executorMap[name] = executor +} diff --git a/internal/stage/parser.go b/internal/stage/parser.go new file mode 100644 index 0000000..43f3994 --- /dev/null +++ b/internal/stage/parser.go @@ -0,0 +1,18 @@ +package stage + +import "github.com/criyle/go-judge/cmd/go-judge/model" + +var parserMap = map[string]Parser{} + +type Parser interface { + Run(model.Result, string) ParserResult +} + +type ParserResult struct { + Score int + Comment string +} + +func RegisterParser(name string, parser Parser) { + parserMap[name] = parser +} diff --git a/internal/stage/run.go b/internal/stage/run.go new file mode 100644 index 0000000..fcb92a2 --- /dev/null +++ b/internal/stage/run.go @@ -0,0 +1,49 @@ +package stage + +import ( + "github.com/criyle/go-judge/cmd/go-judge/model" +) + +type Stage struct { + name string + executor Executor + executorCmd model.Cmd + parser Parser + parserConfig string +} + +type StageResult struct { + Name string + ParserResult +} + +func ParseStages() []Stage { + stages := []Stage{} + config := [][]string{ + {"dummy stage 0", "dummy", "dummy"}, + {"dummy stage 1", "dummy", "dummy"}, + } + for _, v := range config { + stages = append(stages, Stage{ + name: v[0], + executor: executorMap[v[1]], + executorCmd: model.Cmd{}, + parser: parserMap[v[2]], + parserConfig: "", + }) + } + return stages +} + +func Run(stages []Stage) []StageResult { + var parserResults []StageResult + for _, stage := range stages { + executorResult := stage.executor.Run(stage.executorCmd) + parserResult := stage.parser.Run(executorResult, stage.parserConfig) + parserResults = append(parserResults, StageResult{ + Name: stage.name, + ParserResult: parserResult, + }) + } + return parserResults +} diff --git a/pkg/runner/root.go b/pkg/runner/root.go deleted file mode 100644 index 95f026d..0000000 --- a/pkg/runner/root.go +++ /dev/null @@ -1,96 +0,0 @@ -package runner - -import ( - "bytes" - "log/slog" - "os/exec" - "os/user" - "strconv" - "syscall" - "time" - - "github.com/containerd/cgroups" - "github.com/opencontainers/runtime-spec/specs-go" -) - -type RunResult struct { - ReturnCode int - Stdout []byte - Stderr []byte - TimedOut bool - TimeNs uint64 - MemoryByte uint64 -} - -func RunInCgroupsV1( - args []string, username string, cgroupsPath string, timeoutMs uint, -) (result *RunResult, err error) { - u, err := user.Lookup(username) - if err != nil { - return - } - control, err := cgroups.New( - cgroups.V1, - cgroups.StaticPath(cgroupsPath), - &specs.LinuxResources{}, - ) - if err != nil { - return - } - defer func() { - if err := control.Delete(); err != nil { - slog.Error("control.Delete", "error", err) - } - }() - cmd := exec.Command(args[0], args[1:]...) - cmd.SysProcAttr = &syscall.SysProcAttr{} - uid, _ := strconv.ParseUint(u.Uid, 10, 32) - gid, _ := strconv.ParseUint(u.Gid, 10, 32) - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - start := time.Now() - err = cmd.Start() - if err != nil { - return - } - pid := cmd.Process.Pid - slog.Info("process created", "pid", strconv.Itoa(pid)) - if err = control.Add(cgroups.Process{Pid: pid}); err != nil { - return - } - var returnCode int - exitCode := make(chan int, 1) - go func(exit_code chan int) { - if err = cmd.Wait(); err != nil { - exit_code <- err.(*exec.ExitError).ExitCode() - } else { - exit_code <- 0 - } - }(exitCode) - timeoutLimit := time.Duration(timeoutMs) * time.Millisecond - timedOut := false - select { - case returnCode = <-exitCode: - slog.Info("done success", "time", time.Since(start)) - case <-time.After(timeoutLimit): - slog.Info("done timeout", "time", time.Since(start)) - _ = cmd.Process.Kill() - returnCode = <-exitCode - timedOut = true - } - stats, err := control.Stat(cgroups.IgnoreNotExist) - if err != nil { - return - } - result = &RunResult{ - ReturnCode: returnCode, - Stdout: stdout.Bytes(), - Stderr: stderr.Bytes(), - TimedOut: timedOut, - TimeNs: stats.CPU.Usage.Kernel + stats.CPU.Usage.User, - MemoryByte: stats.Memory.Usage.Max, // Memory.Usage.Max = 0 when killed - } - return -}