diff --git a/images/README.md b/images/README.md new file mode 100644 index 00000000..0a2462ac --- /dev/null +++ b/images/README.md @@ -0,0 +1,15 @@ +## How to test kernel-images changes locally with docker + +- Make relevant changes to kernel-images example adding a new endpoint at `kernel-images/server/cmd/api/api/computer.go`, example I added `SetCursor()` endpoint. +- Run openApi to generate the boilerplate for the new endpoints with make oapi-generate +- Check changes at `kernel-images/server/lib/oapi/oapi.go` +- `cd kernel-images/images/chromium-headful` +- Build and run the docker image with `./build-docker.sh && ENABLE_WEBRTC=true ./run-docker.sh` +- Open http://localhost:8080/ in your browser +- Now new endpoint should be available for tests example curl command: +```sh +curl -X POST localhost:444/computer/cursor \ + -H "Content-Type: application/json" \ + -d '{"hidden": true}' +``` + diff --git a/images/chromium-headful/Dockerfile b/images/chromium-headful/Dockerfile index 4e87c9a7..031d5545 100644 --- a/images/chromium-headful/Dockerfile +++ b/images/chromium-headful/Dockerfile @@ -1,51 +1,69 @@ FROM docker.io/golang:1.25.0 AS server-builder WORKDIR /workspace/server -ARG TARGETOS +# Allow cross-compilation when building with BuildKit platforms ARG TARGETARCH +ARG TARGETOS +ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-golang1250 ENV CGO_ENABLED=0 COPY server/go.mod ./ COPY server/go.sum ./ -RUN --mount=type=cache,target=/root/.cache/go-build \ - --mount=type=cache,target=/go/pkg/mod \ +RUN --mount=type=cache,target=/root/.cache/go-build,id=$CACHEIDPREFIX-go-build \ + --mount=type=cache,target=/go/pkg/mod,id=$CACHEIDPREFIX-go-pkg-mod \ go mod download COPY server/ . # Build kernel-images API -RUN --mount=type=cache,target=/root/.cache/go-build \ - --mount=type=cache,target=/go/pkg/mod \ +RUN --mount=type=cache,target=/root/.cache/go-build,id=$CACHEIDPREFIX-go-build \ + --mount=type=cache,target=/go/pkg/mod,id=$CACHEIDPREFIX-go-pkg-mod \ GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \ go build -ldflags="-s -w" -o /out/kernel-images-api ./cmd/api # Build chromium launcher -RUN GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \ +RUN --mount=type=cache,target=/root/.cache/go-build,id=$CACHEIDPREFIX-go-build \ + --mount=type=cache,target=/go/pkg/mod,id=$CACHEIDPREFIX-go-pkg-mod \ + GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \ go build -ldflags="-s -w" -o /out/chromium-launcher ./cmd/chromium-launcher # webrtc client FROM node:22-bullseye-slim AS client WORKDIR /src + +# Allow cross-compilation when building with BuildKit platforms +ARG TARGETARCH +ARG TARGETOS +ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-node22bullseye + COPY images/chromium-headful/client/package*.json ./ -RUN --mount=type=cache,target=/root/.npm npm install +RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install COPY images/chromium-headful/client/ . -RUN --mount=type=cache,target=/root/.npm npm run build +RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm run build # xorg dependencies FROM docker.io/ubuntu:22.04 AS xorg-deps WORKDIR /xorg + +# Allow cross-compilation when building with BuildKit platforms +ARG TARGETARCH +ARG TARGETOS +ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-ubuntu2204 + ENV DEBIAN_FRONTEND=noninteractive -RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \ - --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \ + set -eux; \ rm -f /etc/apt/apt.conf.d/docker-clean; \ echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache; \ - set -eux; \ apt-get update; \ apt-get --no-install-recommends -y install \ git gcc pkgconf autoconf automake libtool make xorg-dev xutils-dev; COPY images/chromium-headful/xorg-deps/ /xorg/ # build xf86-video-dummy v0.3.8 with RandR support -RUN set -eux; \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \ + set -eux; \ cd xf86-video-dummy/v0.3.8; \ patch -p1 < ../01_v0.3.8_xdummy-randr.patch; \ autoreconf -v --install; \ @@ -53,23 +71,96 @@ RUN set -eux; \ make -j$(nproc); \ make install; # build custom input driver -RUN set -eux; \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \ + set -eux; \ cd xf86-input-neko; \ ./autogen.sh --prefix=/usr; \ ./configure; \ make -j$(nproc); \ make install; +FROM docker.io/ubuntu:22.04 AS ffmpeg-downloader + +# Allow cross-compilation when building with BuildKit platforms +ARG TARGETARCH +ARG TARGETOS +ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-ubuntu2204 + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \ + set -xe; \ + rm -f /etc/apt/apt.conf.d/docker-clean; \ + echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache; \ + apt-get -yqq update; \ + apt-get -yqq --no-install-recommends install ca-certificates curl xz-utils; + +# Download FFmpeg (latest static build) for the recording server +RUN --mount=type=cache,target=/tmp/cache/ffmpeg,sharing=locked,id=$CACHEIDPREFIX-ffmpeg \ + <<-'EOT' + set -eux + FFMPEG_CACHE_PATH="/tmp/cache/ffmpeg" + case ${TARGETARCH:-amd64} in + "amd64") FFMPEG_TARGET_ARCH="64" ;; + "arm64") FFMPEG_TARGET_ARCH="arm64" ;; + esac + FFMPEG_TARGET=linux${FFMPEG_TARGET_ARCH:?} + ARCHIVE_NAME="ffmpeg-n7.1-latest-${FFMPEG_TARGET}-gpl-7.1.tar.xz" + FFMPEG_CACHED_ARCHIVE_PATH="$FFMPEG_CACHE_PATH/$ARCHIVE_NAME" + FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH="$FFMPEG_CACHED_ARCHIVE_PATH.sha256" + URL="https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/$ARCHIVE_NAME" + TEMPORARY_SHA256_CHECKSUM_PATH=$(mktemp /tmp/tmp_sha256.XXXXXXXXXX) + CONTINUE="true" + echo "Downloading FFmpeg checksum" + if curl --connect-timeout 10 -fsSL "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/checksums.sha256" -o $TEMPORARY_SHA256_CHECKSUM_PATH; then + grep -F "$ARCHIVE_NAME" $TEMPORARY_SHA256_CHECKSUM_PATH > $FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH + else + echo "Failed to connect to ffmpeg static build provider for checksum." + echo "Checking for cached version to use." + if [ -f "$FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH" ]; then + echo "Found cached checksum." + else + echo "Unable to locate cached checksum." + CONTINUE="false" + fi + fi + rm $TEMPORARY_SHA256_CHECKSUM_PATH + + if [ "$CONTINUE" = "false" ]; then + exit 1 + fi + + echo "Checking cache for FFmpeg archive and validating checksum" + if (cd $FFMPEG_CACHE_PATH && sha256sum --check $FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH); then + echo "Checksum validated, using cached FFmpeg archive" + else + echo "Downloading FFmpeg static build from $URL" + curl -fsSL "$URL" -o $FFMPEG_CACHED_ARCHIVE_PATH + echo "Validating checksum of FFmpeg static build download" + (cd $FFMPEG_CACHE_PATH && sha256sum --check $FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH) + fi + + tar -xJf $FFMPEG_CACHED_ARCHIVE_PATH -C /tmp + install -m755 /tmp/ffmpeg-*/bin/ffmpeg /usr/local/bin/ffmpeg + install -m755 /tmp/ffmpeg-*/bin/ffprobe /usr/local/bin/ffprobe + rm -rf /tmp/ffmpeg* +EOT + FROM ghcr.io/onkernel/neko/base:3.0.8-v1.3.0 AS neko # ^--- now has event.SYSTEM_PONG with legacy support to keepalive FROM node:22-bullseye-slim AS node-22 FROM docker.io/ubuntu:22.04 +# Allow cross-compilation when building with BuildKit platforms +ARG TARGETARCH +ARG TARGETOS +ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-ubuntu2204 + ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_PRIORITY=high -RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \ - --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \ rm -f /etc/apt/apt.conf.d/docker-clean; \ echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache; \ apt-get update && \ @@ -119,50 +210,21 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptca fonts-nanum \ fontconfig \ unzip && \ - apt-get clean && fc-cache -f + fc-cache -f # install ffmpeg manually since the version available in apt is from the 4.x branch due to #drama. -# as of writing these static builds will be the latest 7.0.x release. -RUN --mount=type=cache,target=/tmp/cache/ffmpeg,id=ffmpeg \ - <<-'EOT' - set -eux - URL="https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz" - echo "Downloading FFmpeg MD5 checksum" - if ! curl --connect-timeout 10 -fsSL "${URL}.md5" -o /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5; then - echo "Failed to connect to ffmpeg static build provider for MD5 checksum." - echo "Checking for cached version to use." - if [ ! -f /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5 ]; then - echo "Unable to locate cached MD5 checksum. Exiting." - exit 1 - else - echo "Found cached MD5 checksum." - fi - fi - sed -i -e 's/ .*$/ \/tmp\/cache\/ffmpeg\/ffmpeg.tar.xz/' /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5 - echo "Checking cache for FFmpeg archive and validating MD5 checksum" - if md5sum --check /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5; then - echo "Checksum validated, using cached FFmpeg archive" - else - echo "Downloading FFmpeg static build from $URL" - curl -fsSL "$URL" -o /tmp/cache/ffmpeg/ffmpeg.tar.xz - echo "Validating MD5 checksum of FFmpeg static build download" - md5sum --check /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5 - fi - tar -xJf /tmp/cache/ffmpeg/ffmpeg.tar.xz -C /tmp - install -m755 /tmp/ffmpeg-*/ffmpeg /usr/local/bin/ffmpeg - install -m755 /tmp/ffmpeg-*/ffprobe /usr/local/bin/ffprobe - rm -rf /tmp/ffmpeg* -EOT +COPY --from=ffmpeg-downloader /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg +COPY --from=ffmpeg-downloader /usr/local/bin/ffprobe /usr/local/bin/ffprobe # runtime ENV USERNAME=root -RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \ - --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \ set -eux; \ apt-get update; \ apt-get --no-install-recommends -y install \ - wget ca-certificates python2 supervisor xclip xdotool \ - pulseaudio dbus-x11 xserver-xorg-video-dummy \ + wget ca-certificates python2 supervisor xclip xdotool unclutter \ + pulseaudio dbus-x11 xserver-xorg-video-dummy rtkit upower \ libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx7 \ x11-xserver-utils \ gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ @@ -187,14 +249,15 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptca /home/$USERNAME/.local/share/xorg; \ chmod 1777 /var/log/neko; \ chown $USERNAME /var/log/neko/ /tmp/runtime-$USERNAME; \ - chown -R $USERNAME:$USERNAME /home/$USERNAME; + chown -R $USERNAME:$USERNAME /home/$USERNAME; \ + chmod 777 /etc/pulse; # install chromium and sqlite3 for debugging the cookies file -RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \ - --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \ add-apt-repository -y ppa:xtradeb/apps; -RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \ - --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \ apt update -y && \ apt -y install chromium && \ apt --no-install-recommends -y install sqlite3; @@ -214,8 +277,8 @@ RUN set -eux; \ ln -sf /usr/local/lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack; \ fi -# Install TypeScript and Playwright globally -RUN --mount=type=cache,target=/root/.npm npm install -g typescript playwright-core tsx +# Install TypeScript, Playwright, Patchright globally +RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install -g typescript playwright-core patchright tsx # setup desktop env & app ENV DISPLAY_NUM=1 @@ -225,6 +288,10 @@ ENV WITHDOCKER=true COPY images/chromium-headful/xorg.conf /etc/neko/xorg.conf COPY images/chromium-headful/neko.yaml /etc/neko/neko.yaml +COPY images/chromium-headful/default.pa /etc/pulse/default.pa +COPY images/chromium-headful/daemon.conf /etc/pulse/daemon.conf +COPY images/chromium-headful/dbus-pulseaudio.conf /etc/dbus-1/system.d/pulseaudio.conf +COPY images/chromium-headful/dbus-mpris.conf /etc/dbus-1/system.d/mpris.conf COPY --from=neko /usr/bin/neko /usr/bin/neko COPY --from=client /src/dist/ /var/www COPY --from=xorg-deps /usr/local/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/xorg/modules/drivers/dummy_drv.so @@ -244,6 +311,11 @@ COPY --from=server-builder /out/chromium-launcher /usr/local/bin/chromium-launch # Copy the Playwright executor runtime COPY server/runtime/playwright-executor.ts /usr/local/lib/playwright-executor.ts -RUN useradd -m -s /bin/bash kernel +RUN useradd -m -s /bin/bash kernel && \ + usermod -aG audio,video,pulse,pulse-access kernel + +# Environment variables for audio +ENV XDG_RUNTIME_DIR=/tmp/runtime-kernel +ENV PULSE_SERVER=unix:/tmp/runtime-kernel/pulse/native ENTRYPOINT [ "/wrapper.sh" ] diff --git a/images/chromium-headful/build-unikernel.sh b/images/chromium-headful/build-unikernel.sh index ab860b43..7730468a 100755 --- a/images/chromium-headful/build-unikernel.sh +++ b/images/chromium-headful/build-unikernel.sh @@ -3,7 +3,7 @@ # Move to the script's directory so relative paths work regardless of the caller CWD SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) cd "$SCRIPT_DIR" -source "$SCRIPT_DIR/../../shared/ensure-common-build-run-vars.sh" chromium-headful +source "$SCRIPT_DIR/../../shared/ensure-common-build-run-vars.sh" chromium-headful require-ukc-vars source "$SCRIPT_DIR/../../shared/erofs-utils.sh" # Ensure the mkfs.erofs tool is available @@ -25,6 +25,8 @@ docker cp cnt-"$app_name":/ ./.rootfs rm -f initrd || true sudo mkfs.erofs --all-root -d2 -E noinline_data -b 4096 initrd ./.rootfs +echo "Image index/name: $UKC_INDEX/$IMAGE" + # Package the unikernel (and the new initrd) to KraftCloud kraft pkg \ --name $UKC_INDEX/$IMAGE \ diff --git a/images/chromium-headful/client/Dockerfile b/images/chromium-headful/client/Dockerfile index 85e77c36..44b9eccf 100644 --- a/images/chromium-headful/client/Dockerfile +++ b/images/chromium-headful/client/Dockerfile @@ -3,15 +3,20 @@ FROM $BASE_IMAGE AS client WORKDIR /src +# Allow cross-compilation when building with BuildKit platforms +ARG TARGETARCH +ARG TARGETOS +ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-node18bullseye + # # install dependencies COPY package*.json ./ -RUN --mount=type=cache,target=/root/.npm npm install +RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install # # build client COPY . . -RUN --mount=type=cache,target=/root/.npm npm run build +RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm run build # # artifacts from this stage diff --git a/images/chromium-headful/client/public/browserconfig.xml b/images/chromium-headful/client/public/browserconfig.xml index ededce1f..0fd3ece4 100644 --- a/images/chromium-headful/client/public/browserconfig.xml +++ b/images/chromium-headful/client/public/browserconfig.xml @@ -3,7 +3,7 @@ - #19bd9c + #7B42F6 diff --git a/images/chromium-headful/client/public/index.html b/images/chromium-headful/client/public/index.html index b9546790..9ffe2c34 100644 --- a/images/chromium-headful/client/public/index.html +++ b/images/chromium-headful/client/public/index.html @@ -9,9 +9,9 @@ - - - + + + diff --git a/images/chromium-headful/client/public/kernel.svg b/images/chromium-headful/client/public/kernel.svg new file mode 100644 index 00000000..b9a86da4 --- /dev/null +++ b/images/chromium-headful/client/public/kernel.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/chromium-headful/client/public/site.webmanifest b/images/chromium-headful/client/public/site.webmanifest index 03c2b31f..4d1b7924 100644 --- a/images/chromium-headful/client/public/site.webmanifest +++ b/images/chromium-headful/client/public/site.webmanifest @@ -13,7 +13,7 @@ "type": "image/png" } ], - "theme_color": "#19bd9c", - "background_color": "#19bd9c", + "theme_color": "#7B42F6", + "background_color": "#7B42F6", "display": "standalone" } diff --git a/images/chromium-headful/client/src/assets/styles/_variables.scss b/images/chromium-headful/client/src/assets/styles/_variables.scss index f74ac679..de85520b 100644 --- a/images/chromium-headful/client/src/assets/styles/_variables.scss +++ b/images/chromium-headful/client/src/assets/styles/_variables.scss @@ -21,7 +21,7 @@ $background-modifier-accent: hsla(0, 0%, 100%, 0.06); $elevation-low: 0 1px 0 rgba(4, 4, 5, 0.2), 0 1.5px 0 rgba(6, 6, 7, 0.05), 0 2px 0 rgba(4, 4, 5, 0.05); $elevation-high: 0 8px 16px rgba(0, 0, 0, 0.24); -$style-primary: #19bd9c; +$style-primary: #7B42F6; $style-error: #d32f2f; $menu-height: 40px; diff --git a/images/chromium-headful/client/src/components/connect.vue b/images/chromium-headful/client/src/components/connect.vue index c850c642..2d175244 100644 --- a/images/chromium-headful/client/src/components/connect.vue +++ b/images/chromium-headful/client/src/components/connect.vue @@ -11,8 +11,7 @@
-
-
+
@@ -103,40 +102,25 @@ .loader { width: 90px; height: 90px; - position: relative; margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; - .bounce1, - .bounce2 { + .spinning-logo { width: 100%; height: 100%; - border-radius: 50%; - background-color: $style-primary; - opacity: 0.6; - position: absolute; - top: 0; - left: 0; - - -webkit-animation: bounce 2s infinite ease-in-out; - animation: bounce 2s infinite ease-in-out; - } - - .bounce2 { - -webkit-animation-delay: -1s; - animation-delay: -1s; + animation: spin 2s linear infinite; } } } - @keyframes bounce { - 0%, - 100% { - transform: scale(0); - -webkit-transform: scale(0); + @keyframes spin { + from { + transform: rotate(0deg); } - 50% { - transform: scale(1); - -webkit-transform: scale(1); + to { + transform: rotate(360deg); } } } diff --git a/images/chromium-headful/client/src/components/video.vue b/images/chromium-headful/client/src/components/video.vue index 4c4fe0f8..69b5bb27 100644 --- a/images/chromium-headful/client/src/components/video.vue +++ b/images/chromium-headful/client/src/components/video.vue @@ -1,6 +1,6 @@