From 7426daf3b8381873d89cacf9f51cb541d876bccc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 29 Mar 2024 23:56:01 +0100 Subject: [PATCH 1/2] tests: verify basic function of built images --- .github/workflows/test.yaml | 139 ++++++++++++++++++++++++++++++++---- 1 file changed, 127 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d1b161d4..a8a5cea1 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -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}" + jobs: - container: + image-test: runs-on: ubuntu-22.04 timeout-minutes: 10 strategy: @@ -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 From 47a79ff9ab35592ca2c56e6f3ed7a36436bf6c74 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 30 Mar 2024 07:20:35 +0100 Subject: [PATCH 2/2] Update inline comment about initial attempt failure --- .github/workflows/test.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a8a5cea1..dc2b8c4d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -128,7 +128,8 @@ jobs: # 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. + # proxy/server handshake. This issue is tracked for this project by + # https://github.com/jupyterhub/jupyter-remote-desktop-proxy/issues/105. # websocat --binary --one-message --exit-on-eof 'ws://localhost:8888/desktop-websockify/?token=secret' 2>&1 \ | tee -a /dev/stderr \