Three marks were put on a real panel and judged at the size the bar actually draws them. A bell sat inches from Plasma's own notification bell and meant something entirely different. A handset with an outgoing arrow read as a telephone feature. Breeze has people and it has telephones and the two never meet, so this is drawn in Breeze's language instead: an adult, a child, and one small speech bubble, on Breeze's 22-unit grid with its ColorScheme-Text class, so it recolours with the session and needs no second file for the light theme. Quick Launch carries it. The Icon widget copies the entry into the user's home so an update could never change it; a task manager reads the entry live but also shows every running window, which filled the auxiliary corner with the lesson's tasks. One defect found on the way, and it affected the Wonder Ink icon that is already shipping: gdk-pixbuf recognises an SVG by sniffing the first bytes, and a licence-length comment above the opening tag pushes it out of that window. Every GTK program reported "couldn't recognize the image file format" while Qt rendered the same file happily. The comments live inside <svg> now. Co-Authored-By: Claude Opus 5 <[email protected]>
156 lines
5.2 KiB
Bash
Executable File
156 lines
5.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# A Parent Call sanctuary that cannot reach the sanctuary.
|
|
#
|
|
# The real coordinator, from the control computer, running in the lab
|
|
# against a broker on loopback and a ProPresenter that is a mock. The
|
|
# church's own networks are rejected from inside the container first, so
|
|
# a wrong host in a config file cannot put a child's name on the real
|
|
# screen. Coordinator development has done that once already.
|
|
#
|
|
# tools/lab/parent-call-rig up [MODE] # off | approval | auto
|
|
# tools/lab/parent-call-rig mode MODE
|
|
# tools/lab/parent-call-rig approve|reject
|
|
# tools/lab/parent-call-rig watch
|
|
# tools/lab/parent-call-rig triggers
|
|
# tools/lab/parent-call-rig down
|
|
set -uo pipefail
|
|
|
|
NAME="${LAB_NAME:-classroom-v2-lab}"
|
|
SRC="${PARENT_CALL_SRC:-classroom-control:/home/media/docker/parent-call}"
|
|
ROOT=classroom/parent-call
|
|
|
|
inlab() { docker exec "$NAME" "$@"; }
|
|
|
|
guard() {
|
|
# Container-local. This netns belongs to the container alone, so
|
|
# nothing on the host or the church network is changed by it.
|
|
inlab sh -c '
|
|
iptables -C OUTPUT -d 10.30.0.0/16 -j REJECT 2>/dev/null ||
|
|
iptables -I OUTPUT -d 10.30.0.0/16 -j REJECT
|
|
iptables -C OUTPUT -d 10.40.0.0/16 -j REJECT 2>/dev/null ||
|
|
iptables -I OUTPUT -d 10.40.0.0/16 -j REJECT'
|
|
|
|
for target in 10.40.0.2/1883 10.30.0.2/1025; do
|
|
if inlab timeout 4 sh -c "exec 3<>/dev/tcp/${target}" 2>/dev/null; then
|
|
echo "REFUSING TO CONTINUE: ${target} is still reachable" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
echo " church networks unreachable from the lab"
|
|
}
|
|
|
|
case "${1:-up}" in
|
|
up)
|
|
mode="${2:-approval}"
|
|
|
|
inlab sh -c 'apt-get install -y -qq --no-install-recommends \
|
|
mosquitto mosquitto-clients python3-paho-mqtt iptables >/dev/null 2>&1'
|
|
guard
|
|
|
|
inlab sh -c 'mkdir -p /opt/pc-test'
|
|
ssh -o BatchMode=yes "${SRC%%:*}" "cd ${SRC#*:} && tar cf - src" \
|
|
| docker exec -i "$NAME" sh -c 'cd /opt/pc-test && tar xf -'
|
|
echo " coordinator source copied from the control computer"
|
|
|
|
docker exec -i "$NAME" sh -c 'cat > /opt/pc-test/mosquitto.conf' <<CONF
|
|
# Loopback only. Nothing published here can leave the container.
|
|
listener 1883 127.0.0.1
|
|
allow_anonymous true
|
|
persistence false
|
|
CONF
|
|
|
|
docker exec -i "$NAME" sh -c 'cat > /opt/pc-test/coordinator.env' <<ENV
|
|
MQTT_HOST=127.0.0.1
|
|
MQTT_PORT=1883
|
|
MQTT_CLIENT_ID=parent-call-lab
|
|
TOPIC_ROOT=$ROOT
|
|
PROPRESENTER_BASE_URL=http://127.0.0.1:1025
|
|
PROPRESENTER_MESSAGE='Parent Caller'
|
|
PROPRESENTER_CHILD_TOKEN=Name
|
|
PROPRESENTER_ROOM_TOKEN=Room
|
|
PROPRESENTER_TIMEOUT=4
|
|
PROPRESENTER_CLEAR_AFTER=20
|
|
ROOM_NAMES='nursery=Nursery,k2=K-2,older=Older Kids'
|
|
REQUEST_TTL=180
|
|
MAX_CHILD_LENGTH=40
|
|
MAX_ROOM_LENGTH=32
|
|
MAX_CLOCK_SKEW=120
|
|
DEFAULT_MODE=$mode
|
|
HEALTH_PORT=8099
|
|
ENV
|
|
|
|
docker exec -i "$NAME" sh -c 'cat > /opt/propresenter-mock.py' \
|
|
< "$(dirname "$0")/propresenter-mock.py"
|
|
|
|
# Debian's own mosquitto unit would listen where the lab does not want
|
|
# it to. The rig runs its own, on loopback, and nothing else.
|
|
inlab sh -c 'systemctl disable --now mosquitto >/dev/null 2>&1;
|
|
pkill -f propresenter-mock; pkill -f parent_call.main; pkill mosquitto;
|
|
fuser -k 1025/tcp 1883/tcp 2>/dev/null; true'
|
|
sleep 3
|
|
inlab sh -c 'setsid mosquitto -c /opt/pc-test/mosquitto.conf > /tmp/mosquitto.log 2>&1 &'
|
|
inlab sh -c 'setsid python3 /opt/propresenter-mock.py 1025 > /tmp/ppmock.log 2>&1 &'
|
|
sleep 2
|
|
inlab sh -c 'cd /opt/pc-test && set -a && . ./coordinator.env && set +a &&
|
|
setsid env PYTHONPATH=/opt/pc-test/src python3 -m parent_call.main \
|
|
> /tmp/coordinator.log 2>&1 &'
|
|
sleep 4
|
|
|
|
# The room points at the loopback broker, never at the church's.
|
|
# docker exec needs -i or the heredoc reaches nothing at all, which is
|
|
# how this silently left the room pointing nowhere.
|
|
docker exec -i "$NAME" python3 - <<'PY'
|
|
import pathlib
|
|
p = pathlib.Path("/etc/ekklesia-classroom/device.conf")
|
|
t = p.read_text()
|
|
t = t.replace("MQTT_ENABLED=false", "MQTT_ENABLED=true")
|
|
if "MQTT_HOST=127.0.0.1" not in t:
|
|
t = t.replace("MQTT_HOST=", "MQTT_HOST=127.0.0.1", 1)
|
|
if "\nROOM=" not in t:
|
|
t += "\n# Lab only.\nROOM=nursery\n"
|
|
p.write_text(t)
|
|
PY
|
|
|
|
"$0" mode "$mode"
|
|
inlab tail -3 /tmp/coordinator.log
|
|
echo " rig up in mode $mode"
|
|
;;
|
|
|
|
mode)
|
|
inlab mosquitto_pub -h 127.0.0.1 -t "$ROOT/mode/set" -m "${2:?mode}"
|
|
sleep 1
|
|
echo -n " mode is now: "
|
|
inlab timeout 3 mosquitto_sub -h 127.0.0.1 -t "$ROOT/mode" -C 1 || true
|
|
;;
|
|
|
|
approve|reject)
|
|
id=$(inlab timeout 3 mosquitto_sub -h 127.0.0.1 -t "$ROOT/pending" -C 1 |
|
|
python3 -c 'import json,sys; print(json.load(sys.stdin).get("id") or "")')
|
|
[ -n "$id" ] || { echo " nothing is waiting"; exit 0; }
|
|
inlab mosquitto_pub -h 127.0.0.1 -t "$ROOT/$1" -m "{\"id\": \"$id\"}"
|
|
echo " $1ed $id"
|
|
;;
|
|
|
|
watch)
|
|
inlab mosquitto_sub -h 127.0.0.1 -t "$ROOT/#" -v
|
|
;;
|
|
|
|
triggers)
|
|
inlab sh -c 'cat /tmp/propresenter-mock.log 2>/dev/null || echo "(none)"'
|
|
;;
|
|
|
|
log)
|
|
inlab tail -n "${2:-25}" /tmp/coordinator.log
|
|
;;
|
|
|
|
down)
|
|
inlab sh -c 'pkill -f parent_call.main; pkill -f propresenter-mock; pkill mosquitto; true'
|
|
echo " rig down"
|
|
;;
|
|
|
|
*)
|
|
sed -n '2,14p' "$0"
|
|
exit 1
|
|
;;
|
|
esac
|