Your first task
This walks through a realistic request: compile a C program once, then run it
on two inputs in parallel. It assumes Faber is running as in the quick
start with API_KEY set.
The program
Section titled “The program”A program that reads two integers and prints their sum:
#include <stdio.h>
int main(void) { int a, b; if (scanf("%d %d", &a, &b) != 2) return 1; printf("%d\n", a + b); return 0;}The request
Section titled “The request”Save this as request.json:
{ "stop_on_failure": true, "steps": [ { "cmd": "gcc", "args": ["-O2", "-o", "main", "main.c"], "files": { "main.c": "#include <stdio.h>\n\nint main(void) {\n int a, b;\n if (scanf(\"%d %d\", &a, &b) != 2) return 1;\n printf(\"%d\\n\", a + b);\n return 0;\n}\n" } }, [ { "cmd": "./main", "stdin": "1 2\n", "sandbox_profile": "native_v2" }, { "cmd": "./main", "stdin": "40 2\n", "sandbox_profile": "native_v2" } ] ]}and send it:
curl -s http://localhost:3000/api/v1/execute \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ --data @request.json | jqWhat each part does:
steps: two steps that run in order. The first is a single task (an object); the second is a parallel step (an array) whose tasks run at the same time.cmdandargs: the program and its arguments.gcchas no/, so it is looked up onPATHinside the sandbox../mainis relative to the working directory.files: files to create before the task starts, keyed by path relative to the workspace/faber. Values here are plain strings (UTF-8). You can also send base64 content, mark a file executable, or reference an uploaded file; see the file store.stdin: written to the task’s standard input, which is then closed.sandbox_profile: the seccomp policy. The compiler runs under the default (compile_v2, which allows starting processes). The compiled program runs undernative_v2, which also forbids creating threads, processes and sockets. See choosing sandbox profiles.stop_on_failure: if the compile step fails, the test step is reported as skipped instead of running against a missing binary.
All tasks of the request share the workspace, so the test tasks find the
main binary the compiler wrote. Every task starts in /faber unless it sets
working_dir.
The response
Section titled “The response”{ "steps": [ { "stdout": "", "stderr": "", "exit_code": 0, "cached": false, "stats": { "outcome": "exited", "execution_time_ms": 211, "memory_peak_bytes": 41943040, "...": "..." } }, [ { "stdout": "3\n", "stderr": "", "exit_code": 0, "cached": false, "stats": { "outcome": "exited", "execution_time_ms": 3, "...": "..." } }, { "stdout": "42\n", "stderr": "", "exit_code": 0, "cached": false, "stats": { "outcome": "exited", "execution_time_ms": 2, "...": "..." } } ] ], "cache": { "enabled": false, "hits": 0, "stored": 0 }}Each step result has the shape of its step: an object for the compile task,
an array in the same order for the parallel step. To judge a test, compare
stdout with the expected output and check that exit_code is 0 and
stats.outcome is exited.
When things go wrong
Section titled “When things go wrong”Change a + b to a + c and send the request again. The compiler fails:
{ "steps": [ { "stdout": "", "stderr": "main.c: In function 'main':\nmain.c:6:24: error: 'c' undeclared ...", "exit_code": 1, "cached": false, "stats": { "outcome": "exited", "...": "..." } }, [ { "error": "Skipped because an earlier step failed", "cached": false, "stats": { "outcome": "not_started", "...": "..." } }, { "error": "Skipped because an earlier step failed", "cached": false, "stats": { "outcome": "not_started", "...": "..." } } ] ], "cache": { "enabled": false, "hits": 0, "stored": 0 }}The HTTP status is still 200. A task that fails, times out, runs out of
memory or makes a forbidden syscall is reported in its result; HTTP errors are
reserved for requests Faber could not accept or could not run at all (see
errors).
Skipped tasks have an error instead of stdout/stderr/exit_code. The
execution model lists every outcome.
- Compile once and reuse the binary across requests with the step cache.
- Run many test cases within the limits of one request.
- Use an SDK instead of hand-written JSON.