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
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
dist
tmp
.git
.github
.vscode
db/data
docs
scripts/rootfs_cache
14 changes: 14 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Database Configuration
DATABASE_URL=postgres://castletown:castletown@localhost:5432/castletown?sslmode=disable

# MinIO Configuration
MINIO_ENDPOINT=localhost:9000
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_USE_SSL=false
MINIO_BUCKET=castletown

# Testcase Storage Configuration
# Maximum size (in bytes) for storing test case input/output directly in database
# Larger test cases will be stored in MinIO
TESTCASE_MAX_DB_SIZE=10240 # 10KB default
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Test E2E
name: Test Container

on:
push:
Expand Down Expand Up @@ -26,10 +26,10 @@ jobs:
chmod +x umoci
sudo mv umoci /usr/local/bin/umoci

wget https://go.dev/dl/go1.25.1.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.25.1.linux-amd64.tar.gz
wget https://go.dev/dl/go1.25.5.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.25.5.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
go version

- name: Run e2e tests
run: make test-e2e
- name: Run container tests
run: make test-container
35 changes: 0 additions & 35 deletions .github/workflows/test-sandbox.yml

This file was deleted.

60 changes: 60 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
FROM golang:1.25 AS builder
WORKDIR /src

COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
go mod download

COPY . .
RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
go build -ldflags="-s -w" -o /usr/local/bin/castletown ./main.go

FROM debian:13-slim AS rootfs
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
skopeo \
umoci \
uidmap \
&& rm -rf /var/lib/apt/lists/*

COPY scripts/rootfs.sh /tmp/rootfs.sh
RUN chmod +x /tmp/rootfs.sh \
&& JUDGE_IMAGES_DIR=/var/castletown/images /tmp/rootfs.sh

FROM debian:13-slim AS runtime
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
bash \
ca-certificates \
curl \
fuse-overlayfs \
iptables \
tini \
uidmap \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /opt/castletown

COPY --from=builder /usr/local/bin/castletown /usr/local/bin/castletown
COPY --from=rootfs /var/castletown/images /var/castletown/images
COPY scripts/rootfs.sh /opt/castletown/scripts/rootfs.sh
COPY docker/entrypoint.sh /usr/local/bin/castletown-entrypoint

RUN chmod +x /usr/local/bin/castletown-entrypoint /opt/castletown/scripts/rootfs.sh

ENV CASTLETOWN_SKIP_ROOTFS=1 \
BLOB_ROOT=/var/castletown/blobs \
WORK_ROOT=/tmp/castletown/work \
JUDGE_IMAGES_DIR=/var/castletown/images \
JUDGE_OVERLAYFS_DIR=/tmp/castletown/overlayfs \
STORAGE_DIR=/tmp/castletown/storage \
JUDGE_LIBCONTAINER_DIR=/tmp/castletown/libcontainer \
JUDGE_ROOTFS_DIR=/tmp/castletown/rootfs \
JUDGE_DISK_CACHE_DIR=/var/castletown/testcases \
PROBLEM_CACHE_DIR=/var/castletown/problems

ENTRYPOINT ["/usr/bin/tini","--","castletown-entrypoint"]
CMD ["castletown","start"]
68 changes: 44 additions & 24 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,62 @@ all:
.PHONY: prepare-dirs
prepare-dirs:
@sudo mkdir -p /tmp/castletown/storage
@sudo mkdir -p /tmp/castletown/images
@sudo mkdir -p /tmp/castletown/libcontainer
@sudo mkdir -p /tmp/castletown/overlayfs
@sudo mkdir -p /tmp/castletown/rootfs
@sudo mkdir -p /tmp/castletown/work
@sudo mkdir -p /var/castletown/images
@sudo mkdir -p /var/castletown/testcases

.PHONY: make-rootfs
make-rootfs: prepare-dirs
sudo bash scripts/rootfs.sh

.PHONY: test-sandbox
test-sandbox: make-rootfs
@echo "Running sandbox tests..."
sudo env "PATH=$$PATH:/usr/local/go/bin" go test github.com/joshjms/castletown/sandbox -v

.PHONY: test-job
test-job: make-rootfs
@echo "Running job tests..."
sudo env "PATH=$$PATH:/usr/local/go/bin" go test github.com/joshjms/castletown/job -v

.PHONY: test-e2e
test-e2e: prepare-dirs make-rootfs
@echo "Running end-to-end tests..."
@echo "Building castletown..."
@sudo env "PATH=$$PATH:/usr/local/go/bin" go build -o tmp/castletown main.go
@echo "Starting castletown server..."
@sudo tmp/castletown server &
@sleep 2
sudo env "PATH=$$PATH:/usr/local/go/bin" go test -v ./tests/e2e -timeout 2m
@sudo pkill castletown
@sudo rm -rf tmp

.PHONY: build
build:
bash scripts/build.sh

.PHONY: dev
dev:
sudo env "PATH=$$PATH:/usr/local/go/bin" go run main.go server
sudo env "PATH=$$PATH:/usr/local/go/bin" go run main.go start

.PHONY: docker-up
docker-up:
docker compose up --build -d

.PHONY: docker-down
docker-down:
docker compose down

.PHONY: docker-logs
docker-logs:
docker compose logs -f

.PHONY: migrate-up
migrate-up:
migrate -path db/migrations -database "postgres://castletown:castletown@localhost:5432/castletown?sslmode=disable" up

.PHONY: migrate-down
migrate-down:
migrate -path db/migrations -database "postgres://castletown:castletown@localhost:5432/castletown?sslmode=disable" down

.PHONY: migrate-create
migrate-create:
@read -p "Enter migration name: " name; \
migrate create -ext sql -dir db/migrations -seq $$name


.PHONY: test-grader
test-grader:
@echo "Running grader tests..."
sudo env "PATH=$$PATH:/usr/local/go/bin" go test github.com/joshjms/castletown/internal/grader -v

.PHONY: test-worker
test-worker: make-rootfs
@echo "Running worker tests..."
sudo env "PATH=$$PATH:/usr/local/go/bin" go test github.com/joshjms/castletown/internal/worker -v

.PHONY: test-container
test-container: make-rootfs
@echo "Running container tests..."
sudo env "PATH=$$PATH:/usr/local/go/bin" go test github.com/joshjms/castletown/internal/container -v
41 changes: 34 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
# castletown

## Usage
## Docker quickstart

```
castletown server [flags]
```
1. Install Docker (24.x or newer) with Compose v2 and ensure you can run privileged containers.
2. From the repository root run:
```bash
docker compose up --build
```
The first boot downloads the `gcc:15-bookworm` root filesystem via `skopeo`/`umoci`, so expect the initial `castletown` container start to take a few minutes.
3. Watch the worker logs:
```bash
docker compose logs -f castletown
```
4. When you are done:
```bash
docker compose down
```

## Getting Started
The Compose stack launches:

You can find the guide [here](docs/getting-started.md).
- `castletown`: the sandbox worker. Runs privileged so it can create nested containers, exposes metrics on `:9090`, and consumes submissions from RabbitMQ.
- `postgres`: stores problems, submissions, and metadata.
- `rabbitmq`: queue that feeds submissions to the worker (management UI on <http://localhost:15672>).
- `minio`: placeholder object storage for large blobs and artifacts (console on <http://localhost:9001>).

Named Docker volumes keep the worker stateful directories (`/tmp/castletown/*`, `/var/castletown/*`) so that cached images, overlays, and problem artifacts survive container restarts.

### Useful commands

- Rebuild just the worker image: `docker compose build castletown`
- Tail only dependency logs: `docker compose logs -f postgres rabbitmq minio`
- Open a shell inside the worker: `docker compose exec castletown bash`
- Skip the automatic `gcc-15-bookworm` bootstrap if you already populated `castletown-images`: `CASTLETOWN_SKIP_ROOTFS=1 docker compose up`

## Manual setup

If you need to run Castletown directly on a host (without Docker), follow the more detailed [Getting Started guide](docs/getting-started.md) to prepare cgroup delegation, rootfs images, and prerequisites.

## Contributing

TODO
Contributions are welcome! Please open an issue or pull request with improvements. When changing the worker runtime, make sure the Docker image stays reproducible and update this README accordingly.
Loading
Loading