Skip to content

Commit c2fb113

Browse files
Update Dockerfile
- Update base image from ubuntu:18.04 to ubuntu:24.04 (LTS release) - Update LABELs - Build `task-maker-rust` from source, since the `.deb` package is no longer available - Add support for running multiple workers
1 parent 09a9b7e commit c2fb113

File tree

3 files changed

+299
-39
lines changed

3 files changed

+299
-39
lines changed

tools/docker/Dockerfile

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,117 @@
1-
FROM ubuntu:18.04
2-
LABEL maintainer="Edoardo Morassutto <[email protected]>"
1+
FROM ubuntu:24.04
32

4-
ARG UID=1000
5-
ARG GID=1000
3+
# Example invocation of docker build
4+
# $ docker build \
5+
# --build-arg TM_VERSION=X.Y.Z \
6+
# --build-arg VCS_REF="$(git rev-parse HEAD)" \
7+
# --build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
8+
# -t <yourrepo>/task-maker-rust:X.Y.Z
9+
# .
10+
11+
ARG TM_VERSION # task-maker-rust version (required)
12+
ARG VCS_REF # git commit SHA for v${TM_VERSION}
13+
ARG BUILD_DATE # e.g. 2025-09-04T12:34:56Z (RFC 3339 UTC)
14+
ARG IMAGE_URL
15+
ARG DOCS_URL='https://github.com/olimpiadi-informatica/task-maker-rust#readme'
16+
ARG VENDOR='Olimpiadi Italiane di Informatica'
17+
ARG BASE_NAME='ubuntu:24.04'
18+
ARG BASE_DIGEST='sha256:9cbed754112939e914291337b5e554b07ad7c392491dba6daf25eef1332a22e8'
19+
20+
LABEL \
21+
org.opencontainers.image.title="task-maker-rust" \
22+
org.opencontainers.image.description="task-maker-rust server and worker to build programming tasks for CMS." \
23+
org.opencontainers.image.version="${TM_VERSION}" \
24+
org.opencontainers.image.revision="${VCS_REF}" \
25+
org.opencontainers.image.created="${BUILD_DATE}" \
26+
org.opencontainers.image.url="${IMAGE_URL}" \
27+
org.opencontainers.image.documentation="${DOCS_URL}" \
28+
org.opencontainers.image.source="https://github.com/olimpiadi-informatica/task-maker-rust" \
29+
org.opencontainers.image.authors="task-maker-rust contributors" \
30+
org.opencontainers.image.vendor="${VENDOR}" \
31+
org.opencontainers.image.licenses="MPL-2.0" \
32+
org.opencontainers.image.base.name="${BASE_NAME}" \
33+
org.opencontainers.image.base.digest="${BASE_DIGEST}"
34+
35+
ARG TM_UID=1000
36+
ARG TM_GID=1000
37+
38+
ENV RUST_LOG='info'
39+
ENV RUST_BACKTRACE=1
40+
41+
# run the following as root
42+
USER root
643

744
# install dependencies
8-
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yy \
45+
RUN apt-get update \
46+
&& DEBIAN_FRONTEND=noninteractive apt-get install -yy \
947
asymptote \
1048
build-essential \
1149
fpc \
1250
latexmk \
1351
libseccomp-dev \
14-
python \
15-
python-sortedcontainers \
52+
libssl-dev \
1653
python3 \
1754
python3-sortedcontainers \
1855
texlive \
1956
texlive-latex-extra \
2057
wget \
58+
curl \
2159
&& rm -rf /var/lib/apt/lists/*
2260

23-
# task-maker-rust version (required)
24-
ARG TM_VERSION
61+
# delete default ubuntu user, this removes also the group
62+
RUN userdel --remove ubuntu
63+
64+
# create a group and a user called taskmaker, create the home directory
65+
RUN groupadd ${TM_GID:+-g "${TM_GID}"} taskmaker
66+
RUN useradd -m -g taskmaker ${TM_UID:+-u "${TM_UID}"} taskmaker
67+
68+
# use /opt/rustup for rustup
69+
RUN mkdir -p /opt/rustup && chown taskmaker:taskmaker /opt/rustup
70+
71+
# use /opt/task-maker-rust as work directory
72+
RUN mkdir -p /opt/task-maker-rust && chown taskmaker:taskmaker /opt/task-maker-rust
73+
74+
# install rust and build task-maker-rust as unprivileged user
75+
USER taskmaker
76+
77+
# install rust and cargo
78+
WORKDIR /opt/rustup
79+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o rustup-init \
80+
&& chmod a+x ./rustup-init \
81+
&& ./rustup-init -y
2582

2683
# install task-maker-rust
27-
RUN (test -n "$TM_VERSION" || (echo "Please use --build-arg TM_VERSION=0.3.X" >&2 && exit 1)) \
28-
&& wget https://github.com/olimpiadi-informatica/task-maker-rust/releases/download/v${TM_VERSION}/task-maker-rust_${TM_VERSION}_amd64.deb \
29-
&& dpkg -i task-maker-rust_${TM_VERSION}_amd64.deb \
30-
&& rm task-maker-rust_${TM_VERSION}_amd64.deb
84+
WORKDIR /opt/task-maker-rust
85+
RUN (test -n "$TM_VERSION" || (echo "Please use --build-arg TM_VERSION=X.Y.Z" >&2 && exit 1)) \
86+
&& wget https://github.com/olimpiadi-informatica/task-maker-rust/archive/refs/tags/"v${TM_VERSION}.tar.gz" \
87+
&& tar -xvzf "v${TM_VERSION}.tar.gz" \
88+
&& rm "v${TM_VERSION}.tar.gz"
3189

32-
# drop root privileges
33-
RUN groupadd -g $GID user \
34-
&& useradd -m -g $GID -u $UID user
35-
USER user
90+
# cargo build
91+
RUN . /home/taskmaker/.cargo/env \
92+
&& (cd /opt/task-maker-rust/"task-maker-rust-${TM_VERSION}" && cargo build --release)
93+
94+
# symlink `task-maker` and `tusk-maker-tools` into /usr/local/bin/
95+
USER root
96+
RUN ln -s /opt/task-maker-rust/"task-maker-rust-${TM_VERSION}"/target/release/task-maker /usr/local/bin/ \
97+
&& ln -s /opt/task-maker-rust/"task-maker-rust-${TM_VERSION}"/target/release/task-maker /usr/local/bin/task-maker-rust \
98+
&& ln -s /opt/task-maker-rust/"task-maker-rust-${TM_VERSION}"/target/release/task-maker-tools /usr/local/bin/
99+
100+
# run everything as a unprivileged user
101+
# (docker still needs --privileged to run because rask-maker-rust needs privileges to create a sandbox)
102+
USER taskmaker
103+
WORKDIR /home/taskmaker
36104

37105
# server-client port
38106
EXPOSE 27182
39107
# server-worker port
40108
EXPOSE 27183
41109

42110
# start task-maker-rust server and worker
43-
ADD entrypoint.sh healthcheck.sh /
44-
CMD /entrypoint.sh
111+
ADD entrypoint.sh healthcheck.sh /home/taskmaker
112+
113+
ENTRYPOINT ["/home/taskmaker/entrypoint.sh"]
45114

46115
# check the status of the server and the workers
47-
HEALTHCHECK --interval=5s CMD /healthcheck.sh
116+
HEALTHCHECK --interval=5s \
117+
CMD /home/taskmaker/healthcheck.sh || exit 1

tools/docker/entrypoint.sh

Lines changed: 200 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,222 @@
11
#!/usr/bin/env bash
2+
set -euo pipefail
3+
IFS=$'\n\t'
4+
25

36
server_args=${SERVER_ARGS:-}
47
worker_args=${WORKER_ARGS:-}
58
server_addr=${SERVER_ADDR:-127.0.0.1:27183}
69
spawn_server=${SPAWN_SERVER:-true}
710
spawn_worker=${SPAWN_WORKER:-true}
11+
tm_loglevel=${TM_LOGLEVEL:-info}
12+
13+
# default: nproc - 1
14+
default_nworkers=$(( $(nproc) - 1 ))
15+
nworkers=$default_nworkers
16+
help_flag=false
17+
18+
#################### helpers
19+
function check_posint() {
20+
local re='^[0-9]+$'
21+
local mynum="$1"
22+
local option="$2"
23+
24+
if ! [[ "$mynum" =~ $re ]] ; then
25+
(echo -n "Error in option '$option': " >&2)
26+
(echo "must be a positive integer, got $mynum." >&2)
27+
exit 1
28+
fi
29+
30+
if ! [ "$mynum" -gt 0 ] ; then
31+
(echo "Error in option '$option': must be positive, got $mynum." >&2)
32+
exit 1
33+
fi
34+
}
35+
#################### end: helpers
36+
37+
#################### help
38+
function short_usage() {
39+
(>&2 echo \
40+
"Usage:
41+
$(basename "$0") [options]")
42+
}
43+
44+
function usage() {
45+
(>&2 short_usage )
46+
(>&2 echo \
47+
"
48+
Entrypoint for task-maker-rust Docker image.
49+
50+
Options:
51+
-j, --jobs NWORKERS Number of workers to launch [default: <nproc>-1].
52+
-h, --help Show this help and exits.
53+
")
54+
}
55+
56+
# the leading ":" lets us handle errors ourselves
57+
while getopts ":hj:-:" OPT; do
58+
if [ "$OPT" = "-" ]; then
59+
# Long option handling
60+
longopt="${OPTARG%%=*}"
61+
if [ "$longopt" != "$OPTARG" ]; then
62+
# do not use
63+
(echo "Error. Invalid option: --$OPTARG. " \
64+
"Use --opt OPT instead of --opt=OPT." >&2)
65+
exit 1
66+
else
67+
# form: --long VALUE (value may be in next argv)
68+
OPTARG=
69+
fi
70+
71+
case "$longopt" in
72+
help)
73+
OPT="h"
74+
;;
75+
76+
jobs)
77+
OPT="j"
78+
# If no "=VALUE", try to take the next argv as the argument
79+
next="${!OPTIND}"
80+
if [ -n "$next" ] && [[ "$next" != -* ]]; then
81+
OPTARG="$next"
82+
OPTIND=$((OPTIND + 1))
83+
fi
84+
;;
85+
86+
*)
87+
echo "Error. Bad long option --$longopt" >&2
88+
exit 1
89+
;;
90+
esac
91+
fi
92+
93+
case "$OPT" in
94+
h)
95+
help_flag=true
96+
;;
97+
98+
j)
99+
check_posint "$OPTARG" '-j/--jobs'
100+
nworkers="$OPTARG"
101+
;;
8102

9-
export RUST_LOG=info
10-
export RUST_BACKTRACE=1
103+
\?)
104+
echo "Error. Invalid option: -$OPTARG" >&2
105+
exit 1
106+
;;
107+
108+
:)
109+
echo "Error. Option $OPTARG requires an argument." >&2
110+
exit 1
111+
;;
112+
esac
113+
done
114+
115+
if $help_flag; then
116+
usage
117+
exit 0
118+
fi
119+
#################### end: help
11120

12121
server_store=$(mktemp -d tmserver.XXXXXXX -p /tmp)
13-
worker_store=$(mktemp -d tmworker.XXXXXXX -p /tmp)
14122

15-
function spawn_server() {
16-
task-maker-rust --store-dir "$server_store" $server_args --server
123+
# create nworkers worker stores directories
124+
worker_base=$(mktemp -u tmworker.XXXXXXX -p /tmp)
125+
declare -a worker_stores=()
126+
for ((i=1; i<=nworkers; i++)); do
127+
idx=$(printf "%02d" "$i")
128+
dir="${worker_base}-${idx}"
129+
mkdir -p "$dir"
130+
worker_stores+=("$dir")
131+
done
132+
133+
# cleanup exit trap
134+
function cleanup {
135+
# remove server and worker stores
136+
rm -rf /tmp/tmserver.*
137+
for wstore in $(find /tmp -type d -name 'tmworker.*'); do
138+
rm -rf $wstore
139+
done
17140
}
18-
function spawn_worker() {
19-
task-maker-rust --store-dir "$worker_store" $worker_args --worker $server_addr
141+
trap cleanup EXIT
142+
143+
function loglevel_verbosity_flag() {
144+
case "$1" in
145+
error)
146+
echo ""
147+
;;
148+
warn)
149+
echo "-v"
150+
;;
151+
info)
152+
echo "-vv"
153+
;;
154+
debug)
155+
echo "-vvv"
156+
;;
157+
*)
158+
(echo "Unknown level: $1. Default to 'error'." >&2);
159+
return 1
160+
;;
161+
esac
20162
}
21163

164+
function spawn_tmserver() {
165+
local v="$1"
166+
167+
if [[ "${tm_loglevel}" == 'debug' ]]; then
168+
set -x
169+
fi
170+
171+
task-maker-tools ${v:+$v} \
172+
server \
173+
--store-dir "$server_store" \
174+
$server_args
175+
}
176+
177+
function spawn_tmworker() {
178+
local v="$1"
179+
local store_dir="$2"
180+
181+
if [[ "${tm_loglevel}" == 'debug' ]]; then
182+
set -x
183+
fi
184+
185+
task-maker-tools ${v:+$v} \
186+
worker \
187+
--store-dir "$store_dir" \
188+
$worker_args \
189+
"$server_addr"
190+
}
191+
192+
verbosity_flag=$(loglevel_verbosity_flag "${tm_loglevel}")
193+
22194
# worker only
23-
if [[ $spawn_server != true && $spawn_worker == true ]]; then
24-
spawn_worker
195+
if [[ $spawn_server == false && $spawn_worker == true ]]; then
196+
for ((i=0; i<${#worker_stores[@]}; i++)); do
197+
spawn_tmworker "$verbosity_flag" "${worker_stores[$i]}" &
198+
done
199+
wait
200+
25201
# server only
26-
elif [[ $spawn_server == true && $spawn_worker != true ]]; then
27-
spawn_server
202+
elif [[ $spawn_server == true && $spawn_worker == false ]]; then
203+
spawn_tmserver "$verbosity_flag"
204+
28205
# server+worker
29206
elif [[ $spawn_server == true && $spawn_worker == true ]]; then
30207
# run the workers in background, but wait for the server
31-
( sleep 2s && spawn_worker ) &
32-
spawn_server
208+
(
209+
sleep 2s
210+
for ((i=0; i<${#worker_stores[@]}; i++)); do
211+
spawn_tmworker "$verbosity_flag" "${worker_stores[$i]}" &
212+
done
213+
wait
214+
) &
215+
spawn_tmserver "$verbosity_flag"
216+
33217
# nothing to spawn
34218
else
35219
bash
36-
fi
220+
fi
221+
222+
exit 0

0 commit comments

Comments
 (0)