Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

.PHONY: build prepare up all test

up:
up: build
docker compose up

prepare:
Expand Down
11 changes: 11 additions & 0 deletions evaluator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,14 @@

Can be run with `cargo build` and `cargo run`. Do note that it currently
requires root priviledges, since it edits cgroups.

## Language nuances

Some languages (their compilers or interpreters) have some nuances that need to be taken into account.

### Go

Go needs `GOCACHE` to be set to a folder.
The compiler then caches some files there.

For each Go version, we create a folder in `/opt/evaluator/compilers/go/<version>/.gocache` and run `go build std` for each version. The /opt/evaluator folder is mounted as RW volume, which is enough for the cache.
14 changes: 14 additions & 0 deletions evaluator/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,17 @@
commands:
- cp ${SOURCE_FILE} source.mjl
- ${EXECUTOR} interpret source.mjl

- name: go
compilers:
- name: 1.25.1
path: /opt/evaluator/compilers/go/1.25.1/bin/go
- name: 1.24.7
path: /opt/evaluator/compilers/go/1.24.7/bin/go
- name: 1.23.12
path: /opt/evaluator/compilers/go/1.23.12/bin/go
commands:
- cp ${SOURCE_FILE} source.go
- GOCACHE="/opt/evaluator/compilers/go/${COMPILER_NAME}/.gocache" ${COMPILER} build -o source ${COMPILER_ARGS} source.go
- rm source.go
- ./source ${SOURCE_ARGS}
2 changes: 1 addition & 1 deletion evaluator/config/userspace.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ gidmap {
detect_cgroupv2: true

cgroup_mem_max: 1073741824 # 1GB
cgroup_pids_max: 16 # Haskell needs at least this much.
cgroup_pids_max: 256 # Before Go, 16 was enough...
cgroup_cpu_ms_per_sec: 200

# create dummy user, some programs (racket) need it
Expand Down
3 changes: 3 additions & 0 deletions evaluator/infra/languages/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ image = "rust:1.79-bookworm"

[racket]
image = "gcc:13.2-bookworm"

[go]
image = "debian:bookworm-slim"
22 changes: 22 additions & 0 deletions evaluator/infra/languages/go/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash -e

apt-get update
apt-get install -y wget

COMPILERS_DIR="/opt/evaluator/compilers/go"

setup_go_version() {
GO_VERSION=$1
echo "Setting up go version $GO_VERSION"
GO_FTP="https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
echo "Downloading $GO_FTP"
wget "${GO_FTP}"
tar -xzf "go${GO_VERSION}.linux-amd64.tar.gz"
mkdir -p "${COMPILERS_DIR}"
mv go "${COMPILERS_DIR}/${GO_VERSION}"
GOCACHE="${COMPILERS_DIR}/${GO_VERSION}/.gocache" "${COMPILERS_DIR}/${GO_VERSION}/bin/go" build -v std
}

setup_go_version "1.25.1"
setup_go_version "1.24.7"
setup_go_version "1.23.12"
1 change: 1 addition & 0 deletions evaluator/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ async fn execute(spec: &RunSpec, payload: &RequestPayload, eval_id: &str) -> Res
.iter()
.find(|c| c.name == *compiler)
.with_context(|| format!("Compiler '{}' not found", compiler))?;
command = str::replace(&command, "${COMPILER_NAME}", &compiler_spec.name);
let compiler_path = match &compiler_spec.path {
Some(path) => path.to_owned(),
None => format!("{}/{}/{}", *COMPILERS_PATH, spec.name, compiler_spec.name),
Expand Down
22 changes: 22 additions & 0 deletions integration_tests/evaluator_tests/test_go.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import requests

EVALUATOR_ADDRESS = "http://evaluator:7800/api/v1/evaluate"

def generate_python(code: str):
return {"language": "go", "code": code, "compiler": "1.25.1"}

def test_simple_python():
payload = generate_python("""
package main

import "fmt"

func main() {
fmt.Println("Hello, World!")
}
""")
response = requests.post(EVALUATOR_ADDRESS, json=payload, headers={"X-Forwarded-For": "1.1.1.1"})
assert response.status_code == 200
values = response.json()
assert values["stdout"] == "Hello, World!\n"
assert values["stderr"] == ""
7 changes: 7 additions & 0 deletions website/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export const languages: { [key in LangKey]: ILanguage } = {
text: "Bash",
executors: ["bash-bookworm"],
},
go: {
name: "go",
server_name: "go",
editor_name: "go",
text: "Go",
compilers: ["1.25.1", "1.24.7", "1.23.12"],
},
c: {
name: "c",
server_name: "c",
Expand Down
19 changes: 19 additions & 0 deletions website/src/lib/defaultPrograms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,25 @@ export const defaultPrograms: IPrograms = {
"print(fact(5))",
].join("\n"),
shell: ["#!/bin/bash", "", "echo 'Hello, World!'"].join("\n"),
go: [
"package main",
"",
"import (",
' "fmt"',
")",
"",
"func fact(n int) int {",
" if n == 0 {",
" return 1",
" } else {",
" return n * fact(n - 1)",
" }",
"}",
"",
"func main() {",
" fmt.Println(fact(5))",
"}",
].join("\n"),
c: [
"#include <stdio.h>",
"",
Expand Down
1 change: 1 addition & 0 deletions website/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export type LangKey =
| "python3"
| "racket"
| "bash"
| "go"
| "c"
| "cpp"
| "rust"
Expand Down
Loading