Skip to content

Commit 16e034b

Browse files
blarghmateyCopilot
andcommitted
fix: address PR #14 review feedback
- docker-compose.yml: remove unused GRADER_BACKEND env var, fix duplicate volumes key by merging into one list, tag sample-grader with image: grader-base:local so conf.d/600.json reference resolves - Dockerfile: standardise CMD config path to /etc/xqueue-watcher to match docker-compose and Kubernetes manifests - metrics.py: remove OTEL_METRIC_EXPORT_INTERVAL from docstring since it is not wired up in _build_meter_provider() - containergrader.py: add pod template metadata labels so the NetworkPolicy podSelector (app.kubernetes.io/component=xqueue-grader) actually matches grading pods; set automount_service_account_token=False on the grading pod spec to reduce blast radius if the NetworkPolicy is misconfigured; add _parse_memory_bytes() helper and use it for the Docker backend mem_limit so Kubernetes-style strings like '256Mi' are converted to bytes rather than passed raw (which Docker does not accept) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d524236 commit 16e034b

File tree

4 files changed

+37
-12
lines changed

4 files changed

+37
-12
lines changed

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ RUN uv sync --frozen --no-dev
3232

3333
USER app
3434

35-
CMD ["xqueue-watcher", "-d", "/edx/etc/xqueue_watcher"]
35+
CMD ["xqueue-watcher", "-d", "/etc/xqueue-watcher"]
3636

3737
FROM base AS edx.org
3838
USER app
39-
CMD ["xqueue-watcher", "-d", "/edx/etc/xqueue_watcher"]
39+
CMD ["xqueue-watcher", "-d", "/etc/xqueue-watcher"]

docker-compose.yml

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,19 @@ services:
2727
- ./conf.d:/etc/xqueue-watcher/conf.d:rw
2828
# Mount local grader scripts for rapid iteration.
2929
- ./data:/graders:rw
30-
environment:
31-
# Use docker backend so grading containers run locally via the Docker socket.
32-
GRADER_BACKEND: docker
33-
# Give xqueue-watcher access to the Docker socket so it can spawn grader containers.
34-
# Remove this if you don't need the docker backend locally.
35-
volumes:
30+
# Give xqueue-watcher access to the Docker socket so it can spawn grader containers.
3631
- /var/run/docker.sock:/var/run/docker.sock
3732
extra_hosts:
3833
- "host.docker.internal:host-gateway"
39-
command: python -m xqueue_watcher -d /etc/xqueue-watcher
34+
command: xqueue-watcher -d /etc/xqueue-watcher
4035

4136
# sample-grader: an example grader image for local testing.
4237
# Course teams replace this with their own image.
4338
sample-grader:
4439
build:
4540
context: .
4641
dockerfile: grader_support/Dockerfile.base
42+
image: grader-base:local
4743
# This service is not started automatically — it exists so `docker compose build`
4844
# builds the base image that course grader images extend.
4945
profiles: ["build-only"]

xqueue_watcher/containergrader.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,15 @@ def _build_k8s_job(self, job_name, grader_path, code, seed, grader_config=None):
169169
active_deadline_seconds=self.timeout,
170170
ttl_seconds_after_finished=300,
171171
template=k8s_client.V1PodTemplateSpec(
172+
metadata=k8s_client.V1ObjectMeta(
173+
labels={
174+
"app.kubernetes.io/component": "xqueue-grader",
175+
"app.kubernetes.io/managed-by": "xqueue-watcher",
176+
}
177+
),
172178
spec=k8s_client.V1PodSpec(
173179
restart_policy="Never",
180+
automount_service_account_token=False,
174181
security_context=k8s_client.V1PodSecurityContext(
175182
run_as_non_root=True,
176183
run_as_user=1000,
@@ -301,7 +308,7 @@ def _run_docker(self, grader_path, code, seed, grader_config=None):
301308
working_dir="/grader",
302309
environment=env,
303310
volumes={grader_dir: {"bind": "/graders", "mode": "ro"}},
304-
mem_limit=self.memory_limit,
311+
mem_limit=_parse_memory_bytes(self.memory_limit),
305312
nano_cpus=int(_parse_cpu_millis(self.cpu_limit) * 1_000_000),
306313
network_disabled=True,
307314
read_only=True,
@@ -393,3 +400,27 @@ def _parse_cpu_millis(cpu_str):
393400
if cpu_str.endswith("m"):
394401
return float(cpu_str[:-1])
395402
return float(cpu_str) * 1000
403+
404+
405+
def _parse_memory_bytes(memory_str):
406+
"""Convert a Kubernetes/Docker memory string to bytes for the Docker API.
407+
408+
Handles IEC binary suffixes (Ki, Mi, Gi, Ti) and SI decimal suffixes
409+
(K, M, G, T). Plain integers are returned unchanged.
410+
411+
Examples:
412+
"256Mi" -> 268435456
413+
"1Gi" -> 1073741824
414+
"512M" -> 512000000
415+
"1024" -> 1024
416+
"""
417+
s = str(memory_str).strip()
418+
iec = {"Ti": 1024**4, "Gi": 1024**3, "Mi": 1024**2, "Ki": 1024}
419+
si = {"T": 1000**4, "G": 1000**3, "M": 1000**2, "K": 1000}
420+
for suffix, factor in iec.items():
421+
if s.endswith(suffix):
422+
return int(float(s[: -len(suffix)]) * factor)
423+
for suffix, factor in si.items():
424+
if s.endswith(suffix):
425+
return int(float(s[: -len(suffix)]) * factor)
426+
return int(s)

xqueue_watcher/metrics.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
Service name attached to every metric (default: ``xqueue-watcher``).
1414
``OTEL_RESOURCE_ATTRIBUTES``
1515
Additional resource attributes as ``key=value,...`` pairs.
16-
``OTEL_METRIC_EXPORT_INTERVAL``
17-
Export interval in milliseconds (SDK default: 60 000).
1816
"""
1917

2018
import os

0 commit comments

Comments
 (0)