feat: Display boot progress via web (#358)
This commit is contained in:
parent
27cae73c4a
commit
910b2a8c4e
11 changed files with 191 additions and 114 deletions
|
@ -9,7 +9,6 @@ RUN apt-get update \
|
|||
tini \
|
||||
wget \
|
||||
ovmf \
|
||||
socat \
|
||||
nginx \
|
||||
swtpm \
|
||||
procps \
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
17
src/disk.sh
17
src/disk.sh
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
47
src/reset.sh
47
src/reset.sh
|
@ -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,26 +26,45 @@ 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>"
|
||||
|
||||
{ pkill -f "$name" || true; } 2>/dev/null
|
||||
local body="$1"
|
||||
if [[ "$body" == *"..." ]]; then
|
||||
body="<p class=\"loading\">${body/.../}</p>"
|
||||
fi
|
||||
|
||||
while pgrep -f -l "$name" >/dev/null; do
|
||||
sleep 0.1
|
||||
done
|
||||
local timeout="4999"
|
||||
[ -n "${2:-}" ] && timeout="$2"
|
||||
local script="<script>setTimeout(() => { document.location.reload(); }, $timeout);</script>"
|
||||
[[ "$timeout" == "0" ]] && script=""
|
||||
|
||||
local HTML
|
||||
HTML=$(<"$TEMPLATE")
|
||||
HTML="${HTML/\[1\]/$title}"
|
||||
HTML="${HTML/\[2\]/$script}"
|
||||
HTML="${HTML/\[3\]/$body}"
|
||||
HTML="${HTML/\[4\]/$FOOTER1}"
|
||||
HTML="${HTML/\[5\]/$FOOTER2}"
|
||||
|
||||
echo "$HTML" > "$PAGE"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
@ -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
|
||||
|
|
|
@ -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
26
web/index.html
Normal 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>
|
|
@ -14,8 +14,6 @@ server {
|
|||
|
||||
include /etc/nginx/mime.types;
|
||||
|
||||
location / {
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
|
@ -26,6 +24,19 @@ server {
|
|||
|
||||
add_header Cache-Control "no-cache";
|
||||
|
||||
location / {
|
||||
|
||||
root /dev/shm;
|
||||
|
||||
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
59
web/style.css
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue