Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix image tests: vncserver, websockify, jupyter-remote-desktop-proxy #101

Merged
merged 2 commits into from
Mar 30, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 127 additions & 12 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,18 @@ on:
tags: ["**"]
workflow_dispatch:

defaults:
run:
# Both TigerVNC and TurboVNC reports "the input device is not a TTY" if
# started without a TTY. GitHub Actions environments doesn't come with one,
# so this provides one.
#
# ref: https://github.com/actions/runner/issues/241#issuecomment-842566950
#
shell: script --quiet --return --log-out /dev/null --command "bash -e {0}"

Comment on lines +15 to +24
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yuvipanda I think this may be what you need to get things working locally for you work also in github actions - it made the a difference to me.

I think also there are ipv4 / ipv6 differences, and I now know there were TigerVNC and TurboVNC differences with regards to listening on localhost or not by default, and that websockify will make things listen to localhost anyhow by intercepting the port bind system call or something like that - its messy!

Anyhow, with this, I got it working fully finally.

jobs:
container:
image-test:
runs-on: ubuntu-22.04
timeout-minutes: 10
strategy:
Expand All @@ -28,21 +38,126 @@ jobs:

- name: Build image
run: |
docker build --build-arg vncserver=${{ matrix.vncserver }} -t jupyter-remote-desktop-proxy .
docker build --progress=plain --build-arg vncserver=${{ matrix.vncserver }} -t test .

- name: (inside container) websockify --help
run: |
docker run test websockify --help

- name: (inside container) vncserver -help
run: |
# -help flag is not available for TurboVNC, but it emits the -help
# equivalent information anyhow if passed -help, but also errors. Due
# to this, we fallback to use the errorcode of vncsrever -list.
docker run test bash -c "vncserver -help || vncserver -list > /dev/null"

- name: Install websocat, a test dependency"
run: |
wget -q https://github.com/vi/websocat/releases/download/v1.12.0/websocat.x86_64-unknown-linux-musl \
-O /usr/local/bin/websocat
chmod +x /usr/local/bin/websocat

- name: Test vncserver
if: always()
run: |
container_id=$(docker run -d -it -p 5901:5901 test vncserver -xstartup /opt/install/jupyter_remote_desktop_proxy/share/xstartup -verbose -fg -geometry 1680x1050 -SecurityTypes None -rfbport 5901)
sleep 1

echo "::group::Install netcat, a test dependency"
docker exec --user root $container_id bash -c '
apt update
apt install -y netcat
'
echo "::endgroup::"

docker exec -it $container_id timeout --preserve-status 1 nc -v localhost 5901 2>&1 | tee -a /dev/stderr | \
grep --quiet RFB && echo "Passed test" || { echo "Failed test" && TEST_OK=false; }

echo "::group::vncserver logs"
docker exec $container_id bash -c 'cat ~/.vnc/*.log'
echo "::endgroup::"

docker stop $container_id > /dev/null
if [ "$TEST_OK" == "false" ]; then
echo "One or more tests failed!"
exit 1
fi

- name: Test websockify'ed vncserver
if: always()
run: |
container_id=$(docker run -d -it -p 5901:5901 test websockify --verbose --log-file=/tmp/websockify.log --heartbeat=30 5901 -- vncserver -xstartup /opt/install/jupyter_remote_desktop_proxy/share/xstartup -verbose -fg -geometry 1680x1050 -SecurityTypes None -rfbport 5901)
sleep 1

echo "::group::Install websocat, a test dependency"
docker exec --user root $container_id bash -c '
wget -q https://github.com/vi/websocat/releases/download/v1.12.0/websocat.x86_64-unknown-linux-musl \
-O /usr/local/bin/websocat
chmod +x /usr/local/bin/websocat
'
echo "::endgroup::"

docker exec -it $container_id websocat --binary --one-message --exit-on-eof "ws://localhost:5901/" 2>&1 | tee -a /dev/stderr | \
grep --quiet RFB && echo "Passed test" || { echo "Failed test" && TEST_OK=false; }

echo "::group::websockify logs"
docker exec $container_id bash -c "cat /tmp/websockify.log"
echo "::endgroup::"

- name: Smoke test image
echo "::group::vncserver logs"
docker exec $container_id bash -c 'cat ~/.vnc/*.log'
echo "::endgroup::"

docker stop $container_id > /dev/null
if [ "$TEST_OK" == "false" ]; then
echo "One or more tests failed!"
exit 1
fi

- name: Test project's proxy to websockify'ed vncserver
if: always()
run: |
container_id=$(docker run -d -p 8888:8888 -e JUPYTER_TOKEN=secret jupyter-remote-desktop-proxy)
container_id=$(docker run -d -it -p 8888:8888 -e JUPYTER_TOKEN=secret test)
sleep 3

curl --silent --fail 'http://localhost:8888/desktop/?token=secret' | grep --quiet 'Jupyter Remote Desktop Proxy' && echo "Passed get index.html test" || { echo "Failed get index.html test" && TEST_OK=false; }
curl --silent --fail 'http://localhost:8888/desktop/static/dist/viewer.js?token=secret' > /dev/null && echo "Passed get viewer.js test" || { echo "Failed get viewer.js test" && TEST_OK=false; }

# The first attempt often fails, but the second always(?) succeeds.
#
# This could be related to jupyter-server-proxy's issue
# https://github.com/jupyterhub/jupyter-server-proxy/issues/459
# because the client/proxy websocket handshake completes before the
# proxy/server handshake.
#
websocat --binary --one-message --exit-on-eof 'ws://localhost:8888/desktop-websockify/?token=secret' 2>&1 \
| tee -a /dev/stderr \
| grep --quiet RFB \
&& echo "Passed initial websocket test" \
|| { \
echo "Failed initial websocket test" \
&& sleep 1 \
&& websocat --binary --one-message --exit-on-eof 'ws://localhost:8888/desktop-websockify/?token=secret' 2>&1 \
| tee -a /dev/stderr \
| grep --quiet RFB \
&& echo "Passed second websocket test" \
|| { echo "Failed second websocket test" && TEST_OK=false; } \
}

echo "::group::jupyter_server logs"
docker logs $container_id
echo "::endgroup::"

echo "::group::vncserver logs"
docker exec $container_id bash -c 'cat ~/.vnc/*.log'
echo "::endgroup::"

# -help flag is only available for TigerVNC, where TurboVNC can't
# print info without returning an error code.
docker exec $container_id vncserver -help || true
docker exec $container_id vncserver -list
timeout 5 docker stop $container_id > /dev/null && echo "Passed SIGTERM test" || { echo "Failed SIGTERM test" && TEST_OK=false; }

sleep 10
curl 'http://localhost:8888/desktop/?token=secret' | grep 'Jupyter Remote Desktop Proxy'
# Test if the built JS file is present in the image
curl 'http://localhost:8888/desktop/dist/viewer.js?token=secret' > /dev/null
if [ "$TEST_OK" == "false" ]; then
echo "One or more tests failed!"
exit 1
fi

# TODO: Check VNC desktop works, e.g. by comparing Playwright screenshots
# https://playwright.dev/docs/test-snapshots