Refactor package structure & debug outputs

This commit is contained in:
criyle 2020-12-25 17:25:09 -08:00
parent 65630fbbcf
commit f5dff9ca57
53 changed files with 216 additions and 201 deletions

181
README.md
View File

@ -1,38 +1,67 @@
# go-judge # go-judge
[![GoDoc](https://godoc.org/github.com/criyle/go-judge?status.svg)](https://godoc.org/github.com/criyle/go-judge) [![Go Report Card](https://goreportcard.com/badge/github.com/criyle/go-judge)](https://goreportcard.com/report/github.com/criyle/go-judge) [![Release](https://img.shields.io/github/v/tag/criyle/go-judge)](https://github.com/criyle/go-judge/releases/latest) ![Build](https://github.com/criyle/go-judge/workflows/Build/badge.svg) [![Go Reference](https://pkg.go.dev/badge/github.com/criyle/go-judge.svg)](https://pkg.go.dev/github.com/criyle/go-judge) [![Go Report Card](https://goreportcard.com/badge/github.com/criyle/go-judge)](https://goreportcard.com/report/github.com/criyle/go-judge) [![Release](https://img.shields.io/github/v/tag/criyle/go-judge)](https://github.com/criyle/go-judge/releases/latest) ![Build](https://github.com/criyle/go-judge/workflows/Build/badge.svg)
## Executor Service ## Executor Service
### Architecture ### Architecture
#### Overall Architecture
```text ```text
+----------------------------------------------------------------------------------+ +---------------------------------------------------------------------------+
| Transport Layer (HTTP / WebSocket / FFI / ...) | | Transport Layer (HTTP / WebSocket / FFI / ...) |
+----------------------------------------------------------------------------------+ +---------------------------------------------------------------------------+
| Executor Worker | | Executor Worker |
+-----------------------------------------------------------+----------------------+ +-----------------------------------------------------------+---------------+
| EnvExec + Environment Pool + Environment Builder | File Store | | Environment Pool + EnvExec + Environment Builder | File Store |
+--------------------+----------------+---------------------+--------+-------+-----+ +--------------------+----------------+---------------------+--------+------+
| Linux (go-sandbox) | Windows (winc) | macOS (app sandbox) | Memory | Local | ... | | 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 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 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 GET downloads file from executor service (in memory), returns file content
- /file/:fileId DELETE delete file specified by fileId - /file/:fileId DELETE delete file specified by fileId
- /ws WebSocket for /run - /ws WebSocket for /run
- /metrics prometheus metrics (specifies `METRICS=1` environment variable to enable metrics) - /metrics prometheus metrics (specifies `ES_ENABLE_METRICS=1` environment variable to enable metrics)
- /debug (specifies `DEBUG=1` environment variable to enable go runtime debug endpoint) - /debug (specifies `ES_ENABLE_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) - /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/) 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. 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 #### Build Shared object
- 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 container init `cinit`: 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` 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 ### Build Executor Shell
@ -111,30 +106,20 @@ Run `./executorshell`, connect to gRPC endpoint with interactive shell.
### Container Root Filesystem ### Container Root Filesystem
- [x] necessary lib / exec / compiler / header readonly bind mounted from current file system: /lib /lib64 /bin /usr 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`.
- [x] work directory tmpfs mount: /w (work dir), /tmp (compiler temp files)
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 ### Packages
- additional header readonly bind mounted: /i
### Utilities - envexec: run single / group of programs in parallel within restricted environment and resource constraints
- env: reference implementation environments to inject into envexec
- pkg/envexec: run single / group of programs in parallel within restricted environment and resource constraints
- pkg/pool: reference implementation for Cgroup & Environment Pool
### Windows Support ### Windows Support
Build `executorserver` by: - 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/`
`go build ./cmd/executorserver/` - Run: `./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 #### Windows Security
@ -144,26 +129,19 @@ Run: `./executorserver`
### MacOS Support ### MacOS Support
Build `executorserver` by: - 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/`
`go build ./cmd/executorserver/` - Run: `./executorserver`
Build `executor_server.dylib`: (need to install `XCode`)
`go build -buildmode=c-shared -o executor_server.dylib ./cmd/ffi/`
Run: `./executorserver`
#### MacOS Security #### 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 ### 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. 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.
- Tested multi thread ~1100-1200 op/s Windows 10 WSL2
```lua ```lua
wrk.method = "POST" 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.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 ```text
Running 30s test @ http://localhost:5050/run Running 30s test @ http://localhost:5050/run
1 threads and 1 connections 1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev Thread Stats Avg Stdev Max +/- Stdev
Latency 2.17ms 446.05us 22.37ms 93.88% Latency 2.23ms 287.79us 14.29ms 86.51%
Req/Sec 463.26 26.15 500.00 78.67% Req/Sec 451.05 17.60 494.00 76.00%
Latency Distribution Latency Distribution
50% 2.08ms 50% 2.19ms
75% 2.27ms 75% 2.33ms
90% 2.50ms 90% 2.50ms
99% 3.13ms 99% 2.93ms
13846 requests in 30.01s, 3.65MB read 13482 requests in 30.02s, 3.58MB read
Requests/sec: 461.30 Requests/sec: 449.15
Transfer/sec: 124.38KB Transfer/sec: 121.98KB
``` ```
## TODO ### TODO
- [x] Github actions to auto build - [ ] Github actions to auto build docker image
- [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
## API interface ### REST API Interface
```typescript ```typescript
interface LocalFile { interface LocalFile {

View File

@ -1,7 +1,12 @@
package main package main
import "github.com/criyle/go-sandbox/container" import (
"os"
"github.com/criyle/go-sandbox/container"
)
func main() { func main() {
container.Init() container.Init()
os.Exit(2)
} }

View File

@ -4,7 +4,7 @@ import (
"os" "os"
"time" "time"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/envexec"
"github.com/koding/multiconfig" "github.com/koding/multiconfig"
) )

View File

@ -7,8 +7,10 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"time"
"github.com/creack/pty" "github.com/creack/pty"
"github.com/criyle/go-judge/envexec"
"github.com/criyle/go-judge/filestore" "github.com/criyle/go-judge/filestore"
"github.com/criyle/go-judge/pb" "github.com/criyle/go-judge/pb"
"github.com/criyle/go-judge/worker" "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), Status: pb.Response_Result_StatusType(r.Status),
ExitStatus: int32(r.ExitStatus), ExitStatus: int32(r.ExitStatus),
Error: r.Error, Error: r.Error,
Time: r.Time, Time: uint64(r.Time),
RunTime: r.RunTime, RunTime: uint64(r.RunTime),
Memory: r.Memory, Memory: uint64(r.Memory),
Files: r.Files, Files: r.Files,
FileIDs: r.FileIDs, FileIDs: r.FileIDs,
} }
@ -172,10 +174,10 @@ func convertPBCmd(c *pb.Request_CmdType, srcPrefix string) (cm worker.Cmd, strea
Args: c.GetArgs(), Args: c.GetArgs(),
Env: c.GetEnv(), Env: c.GetEnv(),
TTY: c.GetTty(), TTY: c.GetTty(),
CPULimit: c.GetCPULimit(), CPULimit: time.Duration(c.GetCPULimit()),
RealCPULimit: c.GetRealCPULimit(), RealCPULimit: time.Duration(c.GetRealCPULimit()),
MemoryLimit: c.GetMemoryLimit(), MemoryLimit: envexec.Size(c.GetMemoryLimit()),
StackLimit: c.GetStackLimit(), StackLimit: envexec.Size(c.GetStackLimit()),
ProcLimit: c.GetProcLimit(), ProcLimit: c.GetProcLimit(),
CPURateLimit: c.GetCPURateLimit(), CPURateLimit: c.GetCPURateLimit(),
CopyOut: c.GetCopyOut(), CopyOut: c.GetCopyOut(),

View File

@ -15,10 +15,10 @@ import (
"github.com/criyle/go-judge/cmd/executorserver/config" "github.com/criyle/go-judge/cmd/executorserver/config"
"github.com/criyle/go-judge/env" "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/filestore"
"github.com/criyle/go-judge/pb" "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" "github.com/criyle/go-judge/worker"
ginpprof "github.com/gin-contrib/pprof" ginpprof "github.com/gin-contrib/pprof"
ginzap "github.com/gin-contrib/zap" ginzap "github.com/gin-contrib/zap"

View File

@ -5,8 +5,9 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/envexec"
"github.com/criyle/go-judge/worker" "github.com/criyle/go-judge/worker"
) )
@ -127,9 +128,9 @@ func convertResult(r worker.Result) Result {
Status: Status(r.Status), Status: Status(r.Status),
ExitStatus: r.ExitStatus, ExitStatus: r.ExitStatus,
Error: r.Error, Error: r.Error,
Time: r.Time, Time: uint64(r.Time),
RunTime: r.RunTime, RunTime: uint64(r.RunTime),
Memory: r.Memory, Memory: uint64(r.Memory),
FileIDs: r.FileIDs, FileIDs: r.FileIDs,
} }
if r.Files != nil { if r.Files != nil {
@ -160,10 +161,10 @@ func convertCmd(c Cmd, srcPrefix string) (worker.Cmd, error) {
Env: c.Env, Env: c.Env,
Files: make([]worker.CmdFile, 0, len(c.Files)), Files: make([]worker.CmdFile, 0, len(c.Files)),
TTY: c.TTY, TTY: c.TTY,
CPULimit: c.CPULimit, CPULimit: time.Duration(c.CPULimit),
RealCPULimit: c.RealCPULimit, RealCPULimit: time.Duration(c.RealCPULimit),
MemoryLimit: c.MemoryLimit, MemoryLimit: envexec.Size(c.MemoryLimit),
StackLimit: c.StackLimit, StackLimit: envexec.Size(c.StackLimit),
ProcLimit: c.ProcLimit, ProcLimit: c.ProcLimit,
CPURateLimit: c.CPURateLimit, CPURateLimit: c.CPURateLimit,
CopyOut: c.CopyOut, CopyOut: c.CopyOut,

View File

@ -12,8 +12,8 @@ import (
"github.com/criyle/go-judge/cmd/executorserver/model" "github.com/criyle/go-judge/cmd/executorserver/model"
"github.com/criyle/go-judge/env" "github.com/criyle/go-judge/env"
"github.com/criyle/go-judge/env/pool"
"github.com/criyle/go-judge/filestore" "github.com/criyle/go-judge/filestore"
"github.com/criyle/go-judge/pkg/pool"
"github.com/criyle/go-judge/worker" "github.com/criyle/go-judge/worker"
) )

8
env/doc.go vendored
View File

@ -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 package env

4
env/env_darwin.go vendored
View File

@ -1,8 +1,8 @@
package env package env
import ( import (
"github.com/criyle/go-judge/pkg/macsandbox" "github.com/criyle/go-judge/env/macsandbox"
"github.com/criyle/go-judge/pkg/pool" "github.com/criyle/go-judge/env/pool"
) )
var defaultRead = []string{ var defaultRead = []string{

9
env/env_linux.go vendored
View File

@ -7,7 +7,8 @@ import (
"sync/atomic" "sync/atomic"
"syscall" "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/container"
"github.com/criyle/go-sandbox/pkg/cgroup" "github.com/criyle/go-sandbox/pkg/cgroup"
"github.com/criyle/go-sandbox/pkg/forkexec" "github.com/criyle/go-sandbox/pkg/forkexec"
@ -116,11 +117,11 @@ func NewBuilder(c Config) (pool.EnvBuilder, error) {
cg.Destroy() cg.Destroy()
} }
var cgroupPool pool.CgroupPool var cgroupPool linuxcontainer.CgroupPool
if cgb != nil { 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, Builder: b,
CgroupPool: cgroupPool, CgroupPool: cgroupPool,
WorkDir: workDir, WorkDir: workDir,

4
env/env_windows.go vendored
View File

@ -1,8 +1,8 @@
package env package env
import ( import (
"github.com/criyle/go-judge/pkg/pool" "github.com/criyle/go-judge/env/pool"
"github.com/criyle/go-judge/pkg/winc" "github.com/criyle/go-judge/env/winc"
) )
// NewBuilder build a environment builder // NewBuilder build a environment builder

View File

@ -1,4 +1,4 @@
package pool package linuxcontainer
import "time" import "time"

View File

@ -1,9 +1,9 @@
package pool package linuxcontainer
import ( import (
"time" "time"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/envexec"
"github.com/criyle/go-sandbox/pkg/cgroup" "github.com/criyle/go-sandbox/pkg/cgroup"
) )

View File

@ -1,10 +1,10 @@
package pool package linuxcontainer
import ( import (
"sync" "sync"
"time" "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 // Cgroup defines interface to limit and monitor resources consumption of a process

View File

@ -1,9 +1,10 @@
package pool package linuxcontainer
import ( import (
"fmt" "fmt"
"syscall" "syscall"
"github.com/criyle/go-judge/env/pool"
"github.com/criyle/go-sandbox/container" "github.com/criyle/go-sandbox/container"
) )
@ -27,7 +28,7 @@ type environmentBuilder struct {
} }
// NewEnvBuilder creates builder for linux container pools // NewEnvBuilder creates builder for linux container pools
func NewEnvBuilder(c Config) EnvBuilder { func NewEnvBuilder(c Config) pool.EnvBuilder {
return &environmentBuilder{ return &environmentBuilder{
builder: c.Builder, builder: c.Builder,
cgPool: c.CgroupPool, cgPool: c.CgroupPool,
@ -39,7 +40,7 @@ func NewEnvBuilder(c Config) EnvBuilder {
} }
// Build creates linux container // Build creates linux container
func (b *environmentBuilder) Build() (Environment, error) { func (b *environmentBuilder) Build() (pool.Environment, error) {
m, err := b.builder.Build() m, err := b.builder.Build()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -1,4 +1,4 @@
package pool package linuxcontainer
import ( import (
"context" "context"
@ -7,7 +7,7 @@ import (
"syscall" "syscall"
"time" "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/container"
"github.com/criyle/go-sandbox/pkg/rlimit" "github.com/criyle/go-sandbox/pkg/rlimit"
) )

View File

@ -1,9 +1,9 @@
package pool package linuxcontainer
import ( import (
"time" "time"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/envexec"
"github.com/criyle/go-sandbox/runner" "github.com/criyle/go-sandbox/runner"
) )

View File

@ -1,4 +1,4 @@
package pool package linuxcontainer
import ( import (
"github.com/criyle/go-sandbox/container" "github.com/criyle/go-sandbox/container"

View File

@ -4,7 +4,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/criyle/go-judge/pkg/pool" "github.com/criyle/go-judge/env/pool"
) )
var _ pool.EnvBuilder = &Builder{} var _ pool.EnvBuilder = &Builder{}

View File

@ -7,8 +7,8 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/env/pool"
"github.com/criyle/go-judge/pkg/pool" "github.com/criyle/go-judge/envexec"
"github.com/criyle/go-sandbox/pkg/forkexec" "github.com/criyle/go-sandbox/pkg/forkexec"
"github.com/criyle/go-sandbox/pkg/rlimit" "github.com/criyle/go-sandbox/pkg/rlimit"
"github.com/criyle/go-sandbox/runner" "github.com/criyle/go-sandbox/runner"

View File

@ -1,7 +1,7 @@
package macsandbox package macsandbox
import ( import (
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/envexec"
"github.com/criyle/go-sandbox/runner" "github.com/criyle/go-sandbox/runner"
) )

2
env/pool/doc.go vendored Normal file
View File

@ -0,0 +1,2 @@
// Package pool provides reference implementation for envexec.EnvironmentPool from EnvBuilder
package pool

View File

@ -3,7 +3,7 @@ package pool
import ( import (
"sync" "sync"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/envexec"
) )
// Environment defines envexec.Environment with destroy // Environment defines envexec.Environment with destroy

View File

@ -4,7 +4,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/criyle/go-judge/pkg/pool" "github.com/criyle/go-judge/env/pool"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )

View File

@ -12,8 +12,8 @@ import (
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/env/pool"
"github.com/criyle/go-judge/pkg/pool" "github.com/criyle/go-judge/envexec"
"github.com/criyle/go-sandbox/runner" "github.com/criyle/go-sandbox/runner"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )

View File

@ -4,7 +4,7 @@ import (
"time" "time"
"unsafe" "unsafe"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/envexec"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )

View File

@ -4,7 +4,7 @@ import (
"time" "time"
"unsafe" "unsafe"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/envexec"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"sync"
"github.com/criyle/go-judge/file" "github.com/criyle/go-judge/file"
"github.com/criyle/go-sandbox/runner" "github.com/criyle/go-sandbox/runner"
@ -12,8 +13,16 @@ import (
// copyOutAndCollect reads file and pipes in parallel from container // copyOutAndCollect reads file and pipes in parallel from container
func copyOutAndCollect(m Environment, c *Cmd, ptc []pipeCollector) (map[string]file.File, error) { func copyOutAndCollect(m Environment, c *Cmd, ptc []pipeCollector) (map[string]file.File, error) {
var g errgroup.Group var (
fc := make(chan file.File, len(ptc)+len(c.CopyOut)) 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 // copy out
for _, n := range c.CopyOut { for _, n := range c.CopyOut {
@ -40,7 +49,7 @@ func copyOutAndCollect(m Environment, c *Cmd, ptc []pipeCollector) (map[string]f
if err != nil { if err != nil {
return err return err
} }
fc <- file.NewMemFile(n, c) put(file.NewMemFile(n, c))
return nil 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 { if int64(p.buff.Buffer.Len()) > p.buff.Max {
return runner.StatusOutputLimitExceeded return runner.StatusOutputLimitExceeded
} }
fc <- file.NewMemFile(p.name, p.buff.Buffer.Bytes()) put(file.NewMemFile(p.name, p.buff.Buffer.Bytes()))
return nil return nil
}) })
} }
@ -65,15 +74,6 @@ func copyOutAndCollect(m Environment, c *Cmd, ptc []pipeCollector) (map[string]f
}) })
} }
var err error err := g.Wait()
go func() {
err = g.Wait()
close(fc)
}()
rt := make(map[string]file.File)
for f := range fc {
rt[f.Name()] = f
}
return rt, err return rt, err
} }

6
go.mod
View File

@ -5,7 +5,7 @@ go 1.14
require ( require (
cloud.google.com/go v0.74.0 // indirect cloud.google.com/go v0.74.0 // indirect
github.com/creack/pty v1.1.11 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-seccomp-bpf v1.1.0
github.com/elastic/go-ucfg v0.7.0 github.com/elastic/go-ucfg v0.7.0
github.com/fatih/camelcase v1.0.0 // indirect github.com/fatih/camelcase v1.0.0 // indirect
@ -28,9 +28,9 @@ require (
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0 go.uber.org/zap v1.16.0
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 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/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/term v0.0.0-20201210144234-2321bbc49cbf // indirect
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 // indirect golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 // indirect
google.golang.org/genproto v0.0.0-20201211151036-40ec1c210f7a // indirect google.golang.org/genproto v0.0.0-20201211151036-40ec1c210f7a // indirect

14
go.sum
View File

@ -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.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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.6.1 h1:CQi/6hqWJt9ELtbt3rvqIfAKt3YjJ+h3BASlPWCA8ok=
github.com/criyle/go-sandbox v0.5.10/go.mod h1:6iDDyseyHVugTM2ixPleIBuRx3pVuMKh2qTm6IRmvd8= 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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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-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-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-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 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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 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-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 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 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso= golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/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/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= 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-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=

View File

@ -1,2 +0,0 @@
// Package pool provides reference implements of environent pool and cgroup pool
package pool

View File

@ -3,9 +3,9 @@ package worker
import ( import (
"fmt" "fmt"
"github.com/criyle/go-judge/envexec"
"github.com/criyle/go-judge/file" "github.com/criyle/go-judge/file"
"github.com/criyle/go-judge/filestore" "github.com/criyle/go-judge/filestore"
"github.com/criyle/go-judge/pkg/envexec"
) )
// CmdFile defines file used in the cmd // CmdFile defines file used in the cmd

View File

@ -1,6 +1,11 @@
package worker 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 // Cmd defines command and limits to start a program using in envexec
type Cmd struct { type Cmd struct {
@ -9,10 +14,10 @@ type Cmd struct {
Files []CmdFile Files []CmdFile
TTY bool TTY bool
CPULimit uint64 CPULimit time.Duration
RealCPULimit uint64 RealCPULimit time.Duration
MemoryLimit uint64 MemoryLimit envexec.Size
StackLimit uint64 StackLimit envexec.Size
ProcLimit uint64 ProcLimit uint64
CPURateLimit float64 CPURateLimit float64
@ -48,9 +53,9 @@ type Result struct {
Status envexec.Status Status envexec.Status
ExitStatus int ExitStatus int
Error string Error string
Time uint64 Time time.Duration
RunTime uint64 RunTime time.Duration
Memory uint64 Memory envexec.Size
Files map[string][]byte Files map[string][]byte
FileIDs map[string]string FileIDs map[string]string
} }
@ -61,3 +66,30 @@ type Response struct {
Results []Result Results []Result
Error error 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)
}

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"time" "time"
"github.com/criyle/go-judge/pkg/envexec" "github.com/criyle/go-judge/envexec"
) )
// default tick interval 100 ms // default tick interval 100 ms

View File

@ -7,9 +7,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/criyle/go-judge/envexec"
"github.com/criyle/go-judge/file" "github.com/criyle/go-judge/file"
"github.com/criyle/go-judge/filestore" "github.com/criyle/go-judge/filestore"
"github.com/criyle/go-judge/pkg/envexec"
) )
const maxWaiting = 512 const maxWaiting = 512
@ -201,9 +201,9 @@ func (w *worker) convertResult(result envexec.Result, copyOutSet map[string]bool
res.Status = result.Status res.Status = result.Status
res.ExitStatus = result.ExitStatus res.ExitStatus = result.ExitStatus
res.Error = result.Error res.Error = result.Error
res.Time = uint64(result.Time) res.Time = result.Time
res.RunTime = uint64(result.RunTime) res.RunTime = result.RunTime
res.Memory = uint64(result.Memory) res.Memory = result.Memory
res.Files = make(map[string][]byte) res.Files = make(map[string][]byte)
res.FileIDs = make(map[string]string) res.FileIDs = make(map[string]string)