mirror of
https://github.com/criyle/go-judge.git
synced 2025-09-26 22:39:12 +08:00
Refactor package structure & debug outputs
This commit is contained in:
parent
65630fbbcf
commit
f5dff9ca57
185
README.md
185
README.md
@ -1,38 +1,67 @@
|
||||
# go-judge
|
||||
|
||||
[](https://godoc.org/github.com/criyle/go-judge) [](https://goreportcard.com/report/github.com/criyle/go-judge) [](https://github.com/criyle/go-judge/releases/latest) 
|
||||
[](https://pkg.go.dev/github.com/criyle/go-judge) [](https://goreportcard.com/report/github.com/criyle/go-judge) [](https://github.com/criyle/go-judge/releases/latest) 
|
||||
|
||||
## Executor Service
|
||||
|
||||
### Architecture
|
||||
|
||||
#### Overall Architecture
|
||||
|
||||
```text
|
||||
+----------------------------------------------------------------------------------+
|
||||
| Transport Layer (HTTP / WebSocket / FFI / ...) |
|
||||
+----------------------------------------------------------------------------------+
|
||||
| Executor Worker |
|
||||
+-----------------------------------------------------------+----------------------+
|
||||
| EnvExec + Environment Pool + Environment Builder | File Store |
|
||||
+--------------------+----------------+---------------------+--------+-------+-----+
|
||||
| Linux (go-sandbox) | Windows (winc) | macOS (app sandbox) | Memory | Local | ... |
|
||||
+--------------------+----------------+---------------------+--------+-------+-----+
|
||||
+---------------------------------------------------------------------------+
|
||||
| Transport Layer (HTTP / WebSocket / FFI / ...) |
|
||||
+---------------------------------------------------------------------------+
|
||||
| Executor Worker |
|
||||
+-----------------------------------------------------------+---------------+
|
||||
| Environment Pool + EnvExec + Environment Builder | File Store |
|
||||
+--------------------+----------------+---------------------+--------+------+
|
||||
| Linux (go-sandbox) | Windows (winc) | macOS (app sandbox) | Memory | Disk |
|
||||
+--------------------+----------------+---------------------+--------+------+
|
||||
```
|
||||
|
||||
A rest service to run program in restricted environment and it is basically a wrapper for `pkg/envexec` to run single / multiple programs.
|
||||
### REST API
|
||||
|
||||
- /run POST execute program in the restricted environment
|
||||
A REST service to run program in restricted environment and it is basically a wrapper for `envexec` to run single / multiple programs.
|
||||
|
||||
- /run POST execute program in the restricted environment (examples below)
|
||||
- /file GET list all cached file
|
||||
- /file POST prepare a file in the executor service (in memory), returns fileId (can be referenced in /run parameter)
|
||||
- /file/:fileId GET downloads file from executor service (in memory), returns file content
|
||||
- /file/:fileId DELETE delete file specified by fileId
|
||||
- /ws WebSocket for /run
|
||||
- /metrics prometheus metrics (specifies `METRICS=1` environment variable to enable metrics)
|
||||
- /debug (specifies `DEBUG=1` environment variable to enable go runtime debug endpoint)
|
||||
- /version gets build git version (e.g. `v0.6.4-1-g20d2815`) together with runtime information (go version, os, platform)
|
||||
- /metrics prometheus metrics (specifies `ES_ENABLE_METRICS=1` environment variable to enable metrics)
|
||||
- /debug (specifies `ES_ENABLE_DEBUG=1` environment variable to enable go runtime debug endpoint)
|
||||
- /version gets build git version (e.g. `v0.9.4`) together with runtime information (go version, os, platform)
|
||||
|
||||
### Install & Run Developing Server
|
||||
### Command Line Arguments
|
||||
|
||||
- The default binding address for the executor server is `:5050`. Can be specified with `-http-addr` flag.
|
||||
- By default gRPC endpoint is disabled, to enable gRPC endpoint, add `-enable-grpc` flag.
|
||||
- The default binding address for the gRPC executor server is `:5051`. Can be specified with `-grpc-addr` flag.
|
||||
- The default concurrency is `4`, Can be specified with `-parallelism` flag.
|
||||
- The default file store is in memory, local cache can be specified with `-dir` flag.
|
||||
- The default log level is debug, use `-silent` to disable logs or use `-release` to enable release logger (auto turn on if in docker).
|
||||
- The default CGroup prefix is `executor_server`, Can be specified with `-cgroup-prefix` flag.
|
||||
- `-auth-token` to add token-based authentication to REST / gRPC
|
||||
- `-src-prefix` to restrict `src` copyIn path (need to be absolute path)
|
||||
- `-time-limit-checker-interval` specifies time limit checker interval (default 100ms) (valid value: \[1ms, 1s\])
|
||||
- `-output-limit` specifies size limit of POSIX rlimit of output
|
||||
- `-cpuset` specifies `cpuset.cpus` cgroup for each container
|
||||
- `-container-cred-start` specifies container `setuid` / `setgid` credential start point (default: 10000)
|
||||
- for example, by default container 0 will run with 10001 uid & gid and container 1 will run with 10002 uid & gid...
|
||||
- `-enable-cpu-rate` enabled `cpu` cgroup to control cpu rate using cfs_quota & cfs_period control
|
||||
- `-cpu-cfs-period` specifies cfs_period if cpu rate is enabled (default 100ms) (valid value: \[1ms, 1s\])
|
||||
- `-seccomp-conf` specifies `seecomp` filter setting to load when running program
|
||||
- for example, by `strace -c prog` to get all `syscall` needed and restrict to that sub set
|
||||
- however, the `syscall` count in one platform(e.g. x86_64) is not suitable for all platform, so this option is not recommended
|
||||
- the program killed by seccomp filter will have status `Dangerous Syscall`
|
||||
- By default, the GO debug endpoints are disabled, to enable, specifies `-enable-debug`
|
||||
- By default, the prometheus metrics endpoints are disabled, to enable, specifies `-enable-metrics`
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Environment variable will be override by command line arguments if they both present and all command line arguments have its correspond environment variable (e.g. `ES_HTTP_ADDR`). Run `executorserver --help` to see all the environment variable configurations.
|
||||
|
||||
### Install & Run
|
||||
|
||||
Install GO 1.13+ from [download](https://golang.org/dl/)
|
||||
|
||||
@ -51,41 +80,7 @@ Build by your own `docker build -t executorserver -f Dockerfile.exec .`
|
||||
|
||||
The `executorserver` need root privilege to create `cgroup`. Either creates sub-directory `/sys/fs/cgroup/cpuacct/executor_server`, `/sys/fs/cgroup/memory/executor_server`, `/sys/fs/cgroup/pids/executor_server` and make execution user readable or use `sudo` to run it.
|
||||
|
||||
#### Command Line Arguments
|
||||
|
||||
- The default binding address for the executor server is `:5050`. Can be specified with `-http-addr` flag.
|
||||
- The default binding address for the gRPC executor server is `:5051`. Can be specified with `-grpc-addr` flag. (Notice: need to set `ES_ENABLE_GRPC=1` environment variable to enable GRPC endpoint)
|
||||
- The default concurrency is `4`, Can be specified with `-parallelism` flag.
|
||||
- The default file store is in memory, local cache can be specified with `-dir` flag.
|
||||
- The default log level is debug, use `-silent` to disable logs or use `-release` to enable release logger (auto turn on if in docker).
|
||||
- The default CGroup prefix is `executor_server`, Can be specified with `-cgroup-prefix` flag.
|
||||
- `-auth-token` to add token-based authentication to REST / gRPC
|
||||
- `-src-prefix` to restrict `src` copyIn path (need to be absolute path)
|
||||
- `-time-limit-checker-interval` specifies time limit checker interval (default 100ms) (valid value: \[1ms, 1s\])
|
||||
- `-output-limit` specifies size limit of POSIX rlimit of output
|
||||
- `-cpuset` specifies `cpuset.cpus` cgroup for each container
|
||||
- `-container-cred-start` specifies container `setuid` / `setgid` credential start point (default: 10000)
|
||||
- for example, by default container 0 will run with 10001 uid & gid and container 1 will run with 10002 uid & gid...
|
||||
- `-enable-cpu-rate` enabled `cpu` cgroup to control cpu rate using cfs_quota & cfs_period control
|
||||
- `-cpu-cfs-period` specifies cfs_period if cpu rate is enabled (default 100ms) (valid value: \[1ms, 1s\])
|
||||
- `-seccomp-conf` specifies `seecomp` filter setting to load when running program
|
||||
- for example, by `strace -c prog` to get all `syscall` needed and restrict to that sub set
|
||||
- however, the `syscall` count in one platform(e.g. x86_64) is not suitable for all platform, so this option is not recommended
|
||||
- the program killed by seccomp filter will have status `Dangerous Syscall`
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
Environment variable will be override by command line arguments if they both present. All command line arguments have its correspond environment variable.
|
||||
|
||||
- The http binding address specifies as `ES_HTTP_ADDR=addr`
|
||||
- The grpc binding address specifies as `ES_GRPC_ADDR=addr`
|
||||
- The parallelism specifies as `ES_PARALLELISM=4`
|
||||
- The token specifies as `ES_AUTH_TOKEN=token`
|
||||
- `ES_ENABLE_GRPC=1` enables gRPC
|
||||
- `ES_ENABLE_METRICS=1` enables metrics
|
||||
- `ES_ENABLE_DEBUG=1` enables debug
|
||||
|
||||
### Build Shared object
|
||||
#### Build Shared object
|
||||
|
||||
Build container init `cinit`:
|
||||
|
||||
@ -101,7 +96,7 @@ For example, in JavaScript, run with `ffi-napi` (seems node 14 is not supported
|
||||
|
||||
Build `go build ./cmd/executorproxy`
|
||||
|
||||
Run `./executorproxy`, connect to gRPC endpoint and offers REST endpoint.
|
||||
Run `./executorproxy`, connect to gRPC endpoint expose as a REST endpoint.
|
||||
|
||||
### Build Executor Shell
|
||||
|
||||
@ -111,30 +106,20 @@ Run `./executorshell`, connect to gRPC endpoint with interactive shell.
|
||||
|
||||
### Container Root Filesystem
|
||||
|
||||
- [x] necessary lib / exec / compiler / header readonly bind mounted from current file system: /lib /lib64 /bin /usr
|
||||
- [x] work directory tmpfs mount: /w (work dir), /tmp (compiler temp files)
|
||||
For linux platform, the default mounts points are bind mounting host's `/lib`, `/lib64`, `/usr`, `/bin`, `/etc/alternatives`, `/etc/fpc.cfg`, `/dev/null`, `/dev/urandom` and mounts tmpfs at `/w`, `/tmp` and creates `/proc`.
|
||||
|
||||
The following mounts point are examples that can be configured through config file later
|
||||
To customize mount points, please look at example `mount.yaml` file.
|
||||
|
||||
- additional compiler scripts / exec readonly bind mounted: /c
|
||||
- additional header readonly bind mounted: /i
|
||||
### Packages
|
||||
|
||||
### Utilities
|
||||
|
||||
- pkg/envexec: run single / group of programs in parallel within restricted environment and resource constraints
|
||||
- pkg/pool: reference implementation for Cgroup & Environment Pool
|
||||
- envexec: run single / group of programs in parallel within restricted environment and resource constraints
|
||||
- env: reference implementation environments to inject into envexec
|
||||
|
||||
### Windows Support
|
||||
|
||||
Build `executorserver` by:
|
||||
|
||||
`go build ./cmd/executorserver/`
|
||||
|
||||
Build `executor_server.dll`: (need to install `gcc` as well)
|
||||
|
||||
`go build -buildmode=c-shared -o executor_server.so ./cmd/ffi/`
|
||||
|
||||
Run: `./executorserver`
|
||||
- Build `executorserver` by: `go build ./cmd/executorserver/`
|
||||
- Build `executor_server.dll`: (need to install `gcc` as well) `go build -buildmode=c-shared -o executor_server.so ./cmd/ffi/`
|
||||
- Run: `./executorserver`
|
||||
|
||||
#### Windows Security
|
||||
|
||||
@ -144,26 +129,19 @@ Run: `./executorserver`
|
||||
|
||||
### MacOS Support
|
||||
|
||||
Build `executorserver` by:
|
||||
|
||||
`go build ./cmd/executorserver/`
|
||||
|
||||
Build `executor_server.dylib`: (need to install `XCode`)
|
||||
|
||||
`go build -buildmode=c-shared -o executor_server.dylib ./cmd/ffi/`
|
||||
|
||||
Run: `./executorserver`
|
||||
- Build `executorserver` by: `go build ./cmd/executorserver/`
|
||||
- Build `executor_server.dylib`: (need to install `XCode`) `go build -buildmode=c-shared -o executor_server.dylib ./cmd/ffi/`
|
||||
- Run: `./executorserver`
|
||||
|
||||
#### MacOS Security
|
||||
|
||||
- `sandbox-init` profile deny network access and file read / write
|
||||
- `sandbox-init` profile deny network access and file read / write and read / write to `/Users` directory
|
||||
|
||||
### Benchmark
|
||||
|
||||
By `wrk` with `t.lua`:
|
||||
By `wrk` with `t.lua`: `wrk -s t.lua -c 1 -t 1 -d 30s --latency http://localhost:5050/run`.
|
||||
|
||||
- Tested single thread ~140-160 op/s macOS Docker Desktop & ~400-460 op/s Windows 10 WSL2.
|
||||
- Tested multi thread ~1100-1200 op/s Windows 10 WSL2
|
||||
However, these results are not the real use cases since the running time depends on the actual program specifies in the request. Normally, the executor server consumes ~1ms more compare to running without sandbox.
|
||||
|
||||
```lua
|
||||
wrk.method = "POST"
|
||||
@ -171,41 +149,32 @@ wrk.body = '{"cmd":[{"args":["/bin/cat","a.hs"],"env":["PATH=/usr/bin:/bin"],"
|
||||
wrk.headers["Content-Type"] = "application/json;charset=UTF-8"
|
||||
```
|
||||
|
||||
`wrk -s t.lua -c 1 -t 1 -d 30s --latency http://localhost:5050/run`
|
||||
- Single thread ~400-460 op/s Windows 10 WSL2
|
||||
- Multi thread ~1100-1200 op/s Windows 10 WSL2
|
||||
|
||||
e.g.:
|
||||
Single thread:
|
||||
|
||||
```text
|
||||
Running 30s test @ http://localhost:5050/run
|
||||
1 threads and 1 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 2.17ms 446.05us 22.37ms 93.88%
|
||||
Req/Sec 463.26 26.15 500.00 78.67%
|
||||
Latency 2.23ms 287.79us 14.29ms 86.51%
|
||||
Req/Sec 451.05 17.60 494.00 76.00%
|
||||
Latency Distribution
|
||||
50% 2.08ms
|
||||
75% 2.27ms
|
||||
50% 2.19ms
|
||||
75% 2.33ms
|
||||
90% 2.50ms
|
||||
99% 3.13ms
|
||||
13846 requests in 30.01s, 3.65MB read
|
||||
Requests/sec: 461.30
|
||||
Transfer/sec: 124.38KB
|
||||
99% 2.93ms
|
||||
13482 requests in 30.02s, 3.58MB read
|
||||
Requests/sec: 449.15
|
||||
Transfer/sec: 121.98KB
|
||||
```
|
||||
|
||||
## TODO
|
||||
### TODO
|
||||
|
||||
- [x] Github actions to auto build
|
||||
- [x] Configure mounts using YAML config file
|
||||
- [x] Investigate root-free running mechanism (no cgroup && not set uid / gid)
|
||||
- [x] Investigate RLimit settings (cpu, data, fsize, stack, noFile)
|
||||
- [x] Add WebSocket for job submission
|
||||
- [x] Windows support
|
||||
- [x] MacOS support
|
||||
- [x] GRPC + protobuf support
|
||||
- [x] Token-based authentication
|
||||
- [x] Prometheus metrics support
|
||||
- [x] Customize container workDir, hostName & domainName
|
||||
- [ ] Github actions to auto build docker image
|
||||
|
||||
## API interface
|
||||
### REST API Interface
|
||||
|
||||
```typescript
|
||||
interface LocalFile {
|
||||
|
@ -1,7 +1,12 @@
|
||||
package main
|
||||
|
||||
import "github.com/criyle/go-sandbox/container"
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/criyle/go-sandbox/container"
|
||||
)
|
||||
|
||||
func main() {
|
||||
container.Init()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/koding/multiconfig"
|
||||
)
|
||||
|
||||
|
@ -7,8 +7,10 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-judge/filestore"
|
||||
"github.com/criyle/go-judge/pb"
|
||||
"github.com/criyle/go-judge/worker"
|
||||
@ -100,9 +102,9 @@ func convertPBResult(r worker.Result) *pb.Response_Result {
|
||||
Status: pb.Response_Result_StatusType(r.Status),
|
||||
ExitStatus: int32(r.ExitStatus),
|
||||
Error: r.Error,
|
||||
Time: r.Time,
|
||||
RunTime: r.RunTime,
|
||||
Memory: r.Memory,
|
||||
Time: uint64(r.Time),
|
||||
RunTime: uint64(r.RunTime),
|
||||
Memory: uint64(r.Memory),
|
||||
Files: r.Files,
|
||||
FileIDs: r.FileIDs,
|
||||
}
|
||||
@ -172,10 +174,10 @@ func convertPBCmd(c *pb.Request_CmdType, srcPrefix string) (cm worker.Cmd, strea
|
||||
Args: c.GetArgs(),
|
||||
Env: c.GetEnv(),
|
||||
TTY: c.GetTty(),
|
||||
CPULimit: c.GetCPULimit(),
|
||||
RealCPULimit: c.GetRealCPULimit(),
|
||||
MemoryLimit: c.GetMemoryLimit(),
|
||||
StackLimit: c.GetStackLimit(),
|
||||
CPULimit: time.Duration(c.GetCPULimit()),
|
||||
RealCPULimit: time.Duration(c.GetRealCPULimit()),
|
||||
MemoryLimit: envexec.Size(c.GetMemoryLimit()),
|
||||
StackLimit: envexec.Size(c.GetStackLimit()),
|
||||
ProcLimit: c.GetProcLimit(),
|
||||
CPURateLimit: c.GetCPURateLimit(),
|
||||
CopyOut: c.GetCopyOut(),
|
||||
|
@ -15,10 +15,10 @@ import (
|
||||
|
||||
"github.com/criyle/go-judge/cmd/executorserver/config"
|
||||
"github.com/criyle/go-judge/env"
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-judge/filestore"
|
||||
"github.com/criyle/go-judge/pb"
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/pkg/pool"
|
||||
"github.com/criyle/go-judge/worker"
|
||||
ginpprof "github.com/gin-contrib/pprof"
|
||||
ginzap "github.com/gin-contrib/zap"
|
||||
|
@ -5,8 +5,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-judge/worker"
|
||||
)
|
||||
|
||||
@ -127,9 +128,9 @@ func convertResult(r worker.Result) Result {
|
||||
Status: Status(r.Status),
|
||||
ExitStatus: r.ExitStatus,
|
||||
Error: r.Error,
|
||||
Time: r.Time,
|
||||
RunTime: r.RunTime,
|
||||
Memory: r.Memory,
|
||||
Time: uint64(r.Time),
|
||||
RunTime: uint64(r.RunTime),
|
||||
Memory: uint64(r.Memory),
|
||||
FileIDs: r.FileIDs,
|
||||
}
|
||||
if r.Files != nil {
|
||||
@ -160,10 +161,10 @@ func convertCmd(c Cmd, srcPrefix string) (worker.Cmd, error) {
|
||||
Env: c.Env,
|
||||
Files: make([]worker.CmdFile, 0, len(c.Files)),
|
||||
TTY: c.TTY,
|
||||
CPULimit: c.CPULimit,
|
||||
RealCPULimit: c.RealCPULimit,
|
||||
MemoryLimit: c.MemoryLimit,
|
||||
StackLimit: c.StackLimit,
|
||||
CPULimit: time.Duration(c.CPULimit),
|
||||
RealCPULimit: time.Duration(c.RealCPULimit),
|
||||
MemoryLimit: envexec.Size(c.MemoryLimit),
|
||||
StackLimit: envexec.Size(c.StackLimit),
|
||||
ProcLimit: c.ProcLimit,
|
||||
CPURateLimit: c.CPURateLimit,
|
||||
CopyOut: c.CopyOut,
|
||||
|
@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/criyle/go-judge/cmd/executorserver/model"
|
||||
"github.com/criyle/go-judge/env"
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
"github.com/criyle/go-judge/filestore"
|
||||
"github.com/criyle/go-judge/pkg/pool"
|
||||
"github.com/criyle/go-judge/worker"
|
||||
)
|
||||
|
||||
|
8
env/doc.go
vendored
8
env/doc.go
vendored
@ -1,2 +1,8 @@
|
||||
// Package env provides a unified method to create environment for envexec
|
||||
// Package env provides a unified method to create environment for envexec.
|
||||
//
|
||||
// For linux, the env creates container & cgroup sandbox.
|
||||
//
|
||||
// For windows, the env creates low mandatory level sandbox.
|
||||
//
|
||||
// For macOS, the env creates sandbox_init sandbox.
|
||||
package env
|
||||
|
4
env/env_darwin.go
vendored
4
env/env_darwin.go
vendored
@ -1,8 +1,8 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/criyle/go-judge/pkg/macsandbox"
|
||||
"github.com/criyle/go-judge/pkg/pool"
|
||||
"github.com/criyle/go-judge/env/macsandbox"
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
)
|
||||
|
||||
var defaultRead = []string{
|
||||
|
9
env/env_linux.go
vendored
9
env/env_linux.go
vendored
@ -7,7 +7,8 @@ import (
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/pool"
|
||||
"github.com/criyle/go-judge/env/linuxcontainer"
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
"github.com/criyle/go-sandbox/container"
|
||||
"github.com/criyle/go-sandbox/pkg/cgroup"
|
||||
"github.com/criyle/go-sandbox/pkg/forkexec"
|
||||
@ -116,11 +117,11 @@ func NewBuilder(c Config) (pool.EnvBuilder, error) {
|
||||
cg.Destroy()
|
||||
}
|
||||
|
||||
var cgroupPool pool.CgroupPool
|
||||
var cgroupPool linuxcontainer.CgroupPool
|
||||
if cgb != nil {
|
||||
cgroupPool = pool.NewFakeCgroupPool(cgb, c.CPUCfsPeriod)
|
||||
cgroupPool = linuxcontainer.NewFakeCgroupPool(cgb, c.CPUCfsPeriod)
|
||||
}
|
||||
return pool.NewEnvBuilder(pool.Config{
|
||||
return linuxcontainer.NewEnvBuilder(linuxcontainer.Config{
|
||||
Builder: b,
|
||||
CgroupPool: cgroupPool,
|
||||
WorkDir: workDir,
|
||||
|
4
env/env_windows.go
vendored
4
env/env_windows.go
vendored
@ -1,8 +1,8 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/criyle/go-judge/pkg/pool"
|
||||
"github.com/criyle/go-judge/pkg/winc"
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
"github.com/criyle/go-judge/env/winc"
|
||||
)
|
||||
|
||||
// NewBuilder build a environment builder
|
||||
|
@ -1,4 +1,4 @@
|
||||
package pool
|
||||
package linuxcontainer
|
||||
|
||||
import "time"
|
||||
|
@ -1,9 +1,9 @@
|
||||
package pool
|
||||
package linuxcontainer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-sandbox/pkg/cgroup"
|
||||
)
|
||||
|
@ -1,10 +1,10 @@
|
||||
package pool
|
||||
package linuxcontainer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
)
|
||||
|
||||
// Cgroup defines interface to limit and monitor resources consumption of a process
|
@ -1,9 +1,10 @@
|
||||
package pool
|
||||
package linuxcontainer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
"github.com/criyle/go-sandbox/container"
|
||||
)
|
||||
|
||||
@ -27,7 +28,7 @@ type environmentBuilder struct {
|
||||
}
|
||||
|
||||
// NewEnvBuilder creates builder for linux container pools
|
||||
func NewEnvBuilder(c Config) EnvBuilder {
|
||||
func NewEnvBuilder(c Config) pool.EnvBuilder {
|
||||
return &environmentBuilder{
|
||||
builder: c.Builder,
|
||||
cgPool: c.CgroupPool,
|
||||
@ -39,7 +40,7 @@ func NewEnvBuilder(c Config) EnvBuilder {
|
||||
}
|
||||
|
||||
// Build creates linux container
|
||||
func (b *environmentBuilder) Build() (Environment, error) {
|
||||
func (b *environmentBuilder) Build() (pool.Environment, error) {
|
||||
m, err := b.builder.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
@ -1,4 +1,4 @@
|
||||
package pool
|
||||
package linuxcontainer
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-sandbox/container"
|
||||
"github.com/criyle/go-sandbox/pkg/rlimit"
|
||||
)
|
@ -1,9 +1,9 @@
|
||||
package pool
|
||||
package linuxcontainer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package pool
|
||||
package linuxcontainer
|
||||
|
||||
import (
|
||||
"github.com/criyle/go-sandbox/container"
|
@ -4,7 +4,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/pool"
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
)
|
||||
|
||||
var _ pool.EnvBuilder = &Builder{}
|
@ -7,8 +7,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/pkg/pool"
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-sandbox/pkg/forkexec"
|
||||
"github.com/criyle/go-sandbox/pkg/rlimit"
|
||||
"github.com/criyle/go-sandbox/runner"
|
@ -1,7 +1,7 @@
|
||||
package macsandbox
|
||||
|
||||
import (
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
)
|
||||
|
2
env/pool/doc.go
vendored
Normal file
2
env/pool/doc.go
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package pool provides reference implementation for envexec.EnvironmentPool from EnvBuilder
|
||||
package pool
|
@ -3,7 +3,7 @@ package pool
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
)
|
||||
|
||||
// Environment defines envexec.Environment with destroy
|
@ -4,7 +4,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/pool"
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
@ -12,8 +12,8 @@ import (
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/pkg/pool"
|
||||
"github.com/criyle/go-judge/env/pool"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/criyle/go-judge/file"
|
||||
"github.com/criyle/go-sandbox/runner"
|
||||
@ -12,8 +13,16 @@ import (
|
||||
|
||||
// copyOutAndCollect reads file and pipes in parallel from container
|
||||
func copyOutAndCollect(m Environment, c *Cmd, ptc []pipeCollector) (map[string]file.File, error) {
|
||||
var g errgroup.Group
|
||||
fc := make(chan file.File, len(ptc)+len(c.CopyOut))
|
||||
var (
|
||||
g errgroup.Group
|
||||
l sync.Mutex
|
||||
)
|
||||
rt := make(map[string]file.File)
|
||||
put := func(f file.File) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
rt[f.Name()] = f
|
||||
}
|
||||
|
||||
// copy out
|
||||
for _, n := range c.CopyOut {
|
||||
@ -40,7 +49,7 @@ func copyOutAndCollect(m Environment, c *Cmd, ptc []pipeCollector) (map[string]f
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fc <- file.NewMemFile(n, c)
|
||||
put(file.NewMemFile(n, c))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@ -53,7 +62,7 @@ func copyOutAndCollect(m Environment, c *Cmd, ptc []pipeCollector) (map[string]f
|
||||
if int64(p.buff.Buffer.Len()) > p.buff.Max {
|
||||
return runner.StatusOutputLimitExceeded
|
||||
}
|
||||
fc <- file.NewMemFile(p.name, p.buff.Buffer.Bytes())
|
||||
put(file.NewMemFile(p.name, p.buff.Buffer.Bytes()))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@ -65,15 +74,6 @@ func copyOutAndCollect(m Environment, c *Cmd, ptc []pipeCollector) (map[string]f
|
||||
})
|
||||
}
|
||||
|
||||
var err error
|
||||
go func() {
|
||||
err = g.Wait()
|
||||
close(fc)
|
||||
}()
|
||||
|
||||
rt := make(map[string]file.File)
|
||||
for f := range fc {
|
||||
rt[f.Name()] = f
|
||||
}
|
||||
err := g.Wait()
|
||||
return rt, err
|
||||
}
|
6
go.mod
6
go.mod
@ -5,7 +5,7 @@ go 1.14
|
||||
require (
|
||||
cloud.google.com/go v0.74.0 // indirect
|
||||
github.com/creack/pty v1.1.11
|
||||
github.com/criyle/go-sandbox v0.5.10
|
||||
github.com/criyle/go-sandbox v0.6.1
|
||||
github.com/elastic/go-seccomp-bpf v1.1.0
|
||||
github.com/elastic/go-ucfg v0.7.0
|
||||
github.com/fatih/camelcase v1.0.0 // indirect
|
||||
@ -28,9 +28,9 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.16.0
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||
golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 // indirect
|
||||
google.golang.org/genproto v0.0.0-20201211151036-40ec1c210f7a // indirect
|
||||
|
14
go.sum
14
go.sum
@ -85,8 +85,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/criyle/go-sandbox v0.5.10 h1:T+HUg5UVzIqIjKp+H3hBIkPpODUEChUsdwVch0LgQhs=
|
||||
github.com/criyle/go-sandbox v0.5.10/go.mod h1:6iDDyseyHVugTM2ixPleIBuRx3pVuMKh2qTm6IRmvd8=
|
||||
github.com/criyle/go-sandbox v0.6.1 h1:CQi/6hqWJt9ELtbt3rvqIfAKt3YjJ+h3BASlPWCA8ok=
|
||||
github.com/criyle/go-sandbox v0.6.1/go.mod h1:3IAbhJBgsSoyLixQELE82uibbZJ3dK0XNaqAy949hvk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -564,10 +564,10 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -643,10 +643,8 @@ golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f h1:QdHQnPce6K4XQewki9WNbG5KOROuDzqO3NaYjI1cXJ0=
|
||||
golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
|
@ -1,2 +0,0 @@
|
||||
// Package pool provides reference implements of environent pool and cgroup pool
|
||||
package pool
|
@ -3,9 +3,9 @@ package worker
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-judge/file"
|
||||
"github.com/criyle/go-judge/filestore"
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
)
|
||||
|
||||
// CmdFile defines file used in the cmd
|
||||
|
@ -1,6 +1,11 @@
|
||||
package worker
|
||||
|
||||
import "github.com/criyle/go-judge/pkg/envexec"
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
)
|
||||
|
||||
// Cmd defines command and limits to start a program using in envexec
|
||||
type Cmd struct {
|
||||
@ -9,10 +14,10 @@ type Cmd struct {
|
||||
Files []CmdFile
|
||||
TTY bool
|
||||
|
||||
CPULimit uint64
|
||||
RealCPULimit uint64
|
||||
MemoryLimit uint64
|
||||
StackLimit uint64
|
||||
CPULimit time.Duration
|
||||
RealCPULimit time.Duration
|
||||
MemoryLimit envexec.Size
|
||||
StackLimit envexec.Size
|
||||
ProcLimit uint64
|
||||
CPURateLimit float64
|
||||
|
||||
@ -48,9 +53,9 @@ type Result struct {
|
||||
Status envexec.Status
|
||||
ExitStatus int
|
||||
Error string
|
||||
Time uint64
|
||||
RunTime uint64
|
||||
Memory uint64
|
||||
Time time.Duration
|
||||
RunTime time.Duration
|
||||
Memory envexec.Size
|
||||
Files map[string][]byte
|
||||
FileIDs map[string]string
|
||||
}
|
||||
@ -61,3 +66,30 @@ type Response struct {
|
||||
Results []Result
|
||||
Error error
|
||||
}
|
||||
|
||||
func (r Result) String() string {
|
||||
type Result struct {
|
||||
Status envexec.Status
|
||||
ExitStatus int
|
||||
Error string
|
||||
Time time.Duration
|
||||
RunTime time.Duration
|
||||
Memory envexec.Size
|
||||
Files map[string]string
|
||||
FileIDs map[string]string
|
||||
}
|
||||
d := Result{
|
||||
Status: r.Status,
|
||||
ExitStatus: r.ExitStatus,
|
||||
Error: r.Error,
|
||||
Time: r.Time,
|
||||
RunTime: r.RunTime,
|
||||
Memory: r.Memory,
|
||||
Files: make(map[string]string),
|
||||
FileIDs: r.FileIDs,
|
||||
}
|
||||
for k, v := range r.Files {
|
||||
d.Files[k] = fmt.Sprintf("(len:%d)", len(v))
|
||||
}
|
||||
return fmt.Sprintf("%+v", d)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
)
|
||||
|
||||
// default tick interval 100 ms
|
||||
|
@ -7,9 +7,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/criyle/go-judge/file"
|
||||
"github.com/criyle/go-judge/filestore"
|
||||
"github.com/criyle/go-judge/pkg/envexec"
|
||||
)
|
||||
|
||||
const maxWaiting = 512
|
||||
@ -201,9 +201,9 @@ func (w *worker) convertResult(result envexec.Result, copyOutSet map[string]bool
|
||||
res.Status = result.Status
|
||||
res.ExitStatus = result.ExitStatus
|
||||
res.Error = result.Error
|
||||
res.Time = uint64(result.Time)
|
||||
res.RunTime = uint64(result.RunTime)
|
||||
res.Memory = uint64(result.Memory)
|
||||
res.Time = result.Time
|
||||
res.RunTime = result.RunTime
|
||||
res.Memory = result.Memory
|
||||
res.Files = make(map[string][]byte)
|
||||
res.FileIDs = make(map[string]string)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user