feat: Display boot progress via web (#358)

This commit is contained in:
Kroese 2024-01-20 16:02:17 +01:00 committed by GitHub
parent 27cae73c4a
commit 910b2a8c4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 191 additions and 114 deletions

View file

@ -6,22 +6,21 @@ ARG DEBCONF_NONINTERACTIVE_SEEN "true"
RUN apt-get update \
&& apt-get --no-install-recommends -y install \
tini \
wget \
tini \
wget \
ovmf \
socat \
nginx \
nginx \
swtpm \
procps \
iptables \
iproute2 \
procps \
iptables \
iproute2 \
apt-utils \
dnsmasq \
net-tools \
dnsmasq \
net-tools \
qemu-utils \
ca-certificates \
netcat-openbsd \
qemu-system-x86 \
ca-certificates \
netcat-openbsd \
qemu-system-x86 \
&& apt-get clean \
&& novnc="1.4.0" \
&& mkdir -p /usr/share/novnc \
@ -29,12 +28,14 @@ RUN apt-get update \
&& tar -xf /tmp/novnc.tar.gz -C /tmp/ \
&& cd /tmp/noVNC-"$novnc" \
&& mv app core vendor package.json *.html /usr/share/novnc \
&& sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY ./src /run/
COPY nginx.conf /etc/nginx/sites-enabled/novnc.conf
COPY ./web /var/www/
RUN chmod +x /run/*.sh
RUN mv /var/www/nginx.conf /etc/nginx/sites-enabled/web.conf
VOLUME /storage
EXPOSE 22 5900 8006

View file

@ -15,4 +15,14 @@ DEV_OPTS="$DEV_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0
ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $BOOT_OPTS $DEV_OPTS $USB_OPTS $ARGUMENTS"
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
if [[ "${DISPLAY,,}" == "web" ]]; then
rm -f /dev/shm/index.html
else
if [[ "${DISPLAY,,}" == "vnc" ]]; then
html "You can now connect to VNC on port 5900." "0"
else
html "The virtual machine was booted successfully." "0"
fi
fi
return 0

View file

@ -116,7 +116,9 @@ createDisk() {
fi
fi
info "Creating a $DISK_TYPE $DISK_DESC image in $DISK_FMT format with a size of $DISK_SPACE..."
MSG="Creating a $DISK_TYPE $DISK_DESC image in $DISK_FMT format with a size of $DISK_SPACE..."
info "$MSG" && html "$MSG"
local FAIL="Could not create a $DISK_TYPE $DISK_FMT $DISK_DESC image of $DISK_SPACE ($DISK_FILE)"
case "${DISK_FMT,,}" in
@ -199,7 +201,9 @@ resizeDisk() {
fi
local GB=$(( (CUR_SIZE + 1073741823)/1073741824 ))
info "Resizing $DISK_DESC from ${GB}G to $DISK_SPACE..."
MSG="Resizing $DISK_DESC from ${GB}G to $DISK_SPACE..."
info "$MSG" && html "$MSG"
local FAIL="Could not resize the $DISK_TYPE $DISK_FMT $DISK_DESC image from ${GB}G to $DISK_SPACE ($DISK_FILE)"
case "${DISK_FMT,,}" in
@ -266,7 +270,8 @@ convertDisk() {
fi
fi
info "Converting $DISK_DESC to $DST_FMT, please wait until completed..."
MSG="Converting $DISK_DESC to $DST_FMT, please wait until completed..."
info "$MSG" && html "$MSG"
local CONV_FLAGS="-p"
local DISK_PARAM="$DISK_ALLOC"
@ -305,7 +310,8 @@ convertDisk() {
fi
fi
info "Conversion of $DISK_DESC to $DST_FMT completed succesfully!"
MSG="Conversion of $DISK_DESC to $DST_FMT completed succesfully!"
info "$MSG" && html "$MSG"
return 0
}
@ -420,6 +426,8 @@ addDevice () {
return 0
}
html "Initializing disks..."
DISK1_FILE="$STORAGE/data"
DISK2_FILE="/storage2/data2"
DISK3_FILE="/storage3/data3"
@ -484,4 +492,5 @@ else
addDisk "userdata4" "$DISK4_FILE" "$DISK_EXT" "disk4" "$DISK4_SIZE" "4" "0xd" "$DISK_FMT" || exit $?
fi
html "Initialized disks successfully..."
return 0

View file

@ -1,9 +1,8 @@
#!/usr/bin/env bash
set -Eeuo pipefail
echo " Starting QEMU for Docker v$(</run/version)..."
echo " For support visit https://github.com/qemus/qemu-docker"
echo
APP="QEMU"
SUPPORT="https://github.com/qemus/qemu-docker"
cd /run
@ -18,10 +17,6 @@ cd /run
trap - ERR
if [[ "${DISPLAY,,}" == "web" ]]; then
nginx -e stderr
fi
info "Booting image using $VERS..."
[[ "$DEBUG" == [Yy1]* ]] && set -x

View file

@ -1,10 +1,6 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Display wait message
MSG="Please wait while the ISO is being downloaded..."
/run/server.sh "QEMU" "$MSG" &
# Check if running with interactive TTY or redirected to docker log
if [ -t 1 ]; then
PROGRESS="--progress=bar:noscroll"
@ -30,7 +26,8 @@ BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
TMP="$STORAGE/${BASE%.*}.tmp"
rm -f "$TMP"
info "Downloading $BASE as boot image..."
MSG="Downloading $BASE as boot image..."
info "$MSG" && html "$MSG"
{ wget "$BOOT" -O "$TMP" -q --no-check-certificate --show-progress "$PROGRESS"; rc=$?; } || :
@ -45,4 +42,5 @@ fi
mv -f "$TMP" "$STORAGE/$BASE"
html "Download finished successfully..."
return 0

View file

@ -95,16 +95,14 @@ getPorts() {
local list=$1
local vnc="5900"
local novnc="8006"
local web="8006"
if [[ "${DISPLAY,,}" == "vnc" ]] && [[ "$list" != *"$vnc"* ]]; then
[ -z "$list" ] && list="$web" || list="$list,$web"
if [[ "${DISPLAY,,}" == "vnc" ]] || [[ "${DISPLAY,,}" == "web" ]]; then
[ -z "$list" ] && list="$vnc" || list="$list,$vnc"
fi
if [[ "${DISPLAY,,}" == "web" ]] && [[ "$list" != *"$novnc"* ]]; then
[ -z "$list" ] && list="$vnc,$novnc" || list="$list,$vnc,$novnc"
fi
[ -z "$list" ] && return 0
if [[ "$list" != *","* ]]; then
@ -195,33 +193,6 @@ configureNAT() {
return 0
}
closeNetwork() {
exec 30<&- || true
exec 40<&- || true
if [[ "$DHCP" == [Yy1]* ]]; then
fKill "server.sh"
ip link set "$VM_NET_TAP" down || true
ip link delete "$VM_NET_TAP" || true
else
fKill "dnsmasq"
ip link set "$VM_NET_TAP" down promisc off || true
ip link delete "$VM_NET_TAP" || true
ip link set dockerbridge down || true
ip link delete dockerbridge || true
fi
return 0
}
getInfo() {
if [ -z "$VM_NET_DEV" ]; then
@ -255,8 +226,6 @@ getInfo() {
# Configure Network
# ######################################
fKill "server.sh"
if [ ! -c /dev/vhost-net ]; then
if mknod /dev/vhost-net c 10 238; then
chmod 660 /dev/vhost-net
@ -264,6 +233,7 @@ if [ ! -c /dev/vhost-net ]; then
fi
getInfo
html "Initializing network..."
if [[ "$DEBUG" == [Yy1]* ]]; then
info "Container IP is $IP with gateway $GATEWAY on interface $VM_NET_DEV" && echo
@ -289,4 +259,5 @@ fi
NET_OPTS="$NET_OPTS -device virtio-net-pci,romfile=,netdev=hostnet0,mac=$VM_NET_MAC,id=net0"
html "Initialized network successfully..."
return 0

View file

@ -10,6 +10,10 @@ trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
echo " Starting $APP for Docker v$(</run/version)..."
echo " For support visit $SUPPORT"
echo
# Docker environment variables
: "${BOOT:=""}" # URL of the ISO file
@ -22,28 +26,47 @@ trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
# Helper variables
STORAGE="/storage"
PAGE="/dev/shm/index.html"
TEMPLATE="/var/www/index.html"
FOOTER1="$APP for Docker v$(</run/version)"
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
KERNEL=$(uname -r | cut -b 1)
MINOR=$(uname -r | cut -d '.' -f2)
ARCH=$(dpkg --print-architecture)
VERS=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1)
# Check folder
STORAGE="/storage"
[ ! -d "$STORAGE" ] && error "Storage folder ($STORAGE) not found!" && exit 13
# Helper functions
fKill () {
local name=$1
html()
{
local title="<title>$APP</title>"
local body="$1"
if [[ "$body" == *"..." ]]; then
body="<p class=\"loading\">${body/.../}</p>"
fi
{ pkill -f "$name" || true; } 2>/dev/null
local timeout="4999"
[ -n "${2:-}" ] && timeout="$2"
local script="<script>setTimeout(() => { document.location.reload(); }, $timeout);</script>"
[[ "$timeout" == "0" ]] && script=""
while pgrep -f -l "$name" >/dev/null; do
sleep 0.1
done
local HTML
HTML=$(<"$TEMPLATE")
HTML="${HTML/\[1\]/$title}"
HTML="${HTML/\[2\]/$script}"
HTML="${HTML/\[3\]/$body}"
HTML="${HTML/\[4\]/$FOOTER1}"
HTML="${HTML/\[5\]/$FOOTER2}"
return 0
echo "$HTML" > "$PAGE"
return 0
}
addPackage () {
@ -55,7 +78,8 @@ addPackage () {
return 0
fi
info "Installing $desc..."
MSG="Installing $desc..."
info "$MSG" && html "$MSG"
DEBIAN_FRONTEND=noninteractive apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
@ -63,4 +87,9 @@ addPackage () {
return 0
}
# Start webserver
cp -r /var/www/* /dev/shm
html "Starting $APP..."
nginx -e stderr
return 0

View file

@ -1,32 +0,0 @@
#!/usr/bin/env bash
set -eu
TMP_FILE=$(mktemp -q /dev/shm/server.XXXXXX)
stop() {
trap - SIGINT EXIT
{ pkill -f socat || true; } 2>/dev/null
[ -f "$TMP_FILE" ] && rm -f "$TMP_FILE"
}
trap 'stop' EXIT SIGINT SIGTERM SIGHUP
html()
{
local h="<!DOCTYPE html><HTML><HEAD><TITLE>$2</TITLE>"
h="$h<STYLE>body { color: white; background-color: #125bdb; font-family: Verdana,"
h="$h Arial,sans-serif; } a, a:hover, a:active, a:visited { color: white; }</STYLE></HEAD>"
h="$h<BODY><BR><BR><H1><CENTER>$1</CENTER></H1></BODY></HTML>"
echo "$h"
}
BODY="$2<script>setTimeout(() => { document.location.reload(); }, 4999);</script>"
HTML=$(html "$BODY" "$1")
printf '%b' "HTTP/1.1 200 OK\nContent-Length: ${#HTML}\nConnection: close\n\n$HTML" > "$TMP_FILE"
socat TCP4-LISTEN:80,reuseaddr,fork,crlf SYSTEM:"cat $TMP_FILE" 2> /dev/null &
socat TCP4-LISTEN:8006,reuseaddr,fork,crlf SYSTEM:"cat $TMP_FILE" 2> /dev/null & wait $!
exit

26
web/index.html Normal file
View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
[1]
<meta http-equiv="Cache-Control" content="no-cache" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="style.css" />
[2]
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<h1>[3]</h1>
</div>
<div id="empty-space">
</div>
<div id="footer">
[4]<br />
[5]
</div>
</div>
</body>
</html>

View file

@ -14,17 +14,28 @@ server {
include /etc/nginx/mime.types;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 500;
gzip_disable "msie6";
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
add_header Cache-Control "no-cache";
location / {
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 500;
gzip_disable "msie6";
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
root /dev/shm;
add_header Cache-Control "no-cache";
if ( -f /dev/shm/index.html) {
break;
}
try_files /index.html @vnc;
}
location @vnc {
root /usr/share/novnc;
index vnc.html;

59
web/style.css Normal file
View file

@ -0,0 +1,59 @@
body {
color: white;
background-color: #125bdb;
font-family: Verdana, Arial, sans-serif;
}
#content-wrap {
text-align: center;
padding: 20px;
margin-top: 100px;
}
#footer {
width: 98%;
position: fixed;
bottom: 0px;
height: 40px;
text-align: center;
}
#empty-space {
height: 40px;
/* Same height as footer */
}
a,
a:hover,
a:active,
a:visited {
color: white;
}
.loading:after {
content: " .";
animation: dots 1s steps(5, end) infinite;
}
@keyframes dots {
0%,
20% {
color: rgba(0, 0, 0, 0);
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
}
40% {
color: white;
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
}
60% {
text-shadow: 0.25em 0 0 white, 0.5em 0 0 rgba(0, 0, 0, 0);
}
80%,
100% {
text-shadow: 0.25em 0 0 white, 0.5em 0 0 white;
}
}