From a09cb84bdde06bb38e35859636747dc1b628878a Mon Sep 17 00:00:00 2001 From: Demeisen Date: Sun, 3 Nov 2024 12:52:29 +0000 Subject: [PATCH] feat: implement dynamic port configuration - Add .ports configuration file for clean port mapping - Separate internal from external ports - Add port availability validation - Update all components to use configured ports - Add gettext-base for template processing This change prevents port conflicts when running multiple instances and provides a cleaner separation between internal and external ports. Fixes #147 --- .ports | 11 ++ computer-use-demo/Dockerfile | 2 + computer-use-demo/image/entrypoint.sh | 13 +- computer-use-demo/image/http_server.py | 6 +- computer-use-demo/image/novnc_startup.sh | 13 +- .../{index.html => index.html.template} | 4 +- computer-use-demo/image/x11vnc_startup.sh | 29 +-- computer-use-demo/run.sh | 168 ++++++++++++++++++ 8 files changed, 210 insertions(+), 36 deletions(-) create mode 100644 .ports rename computer-use-demo/image/static_content/{index.html => index.html.template} (91%) create mode 100755 computer-use-demo/run.sh diff --git a/.ports b/.ports new file mode 100644 index 00000000..0f1ab514 --- /dev/null +++ b/.ports @@ -0,0 +1,11 @@ +# INTERNAL ports end in 1, EXTERNAL ports end in 0 +PORT_VNC_INTERNAL=5901 +PORT_VNC_EXTERNAL=5900 + +PORT_NOVNC_INTERNAL=6081 +PORT_NOVNC_EXTERNAL=6080 + +PORT_HTTP_INTERNAL=8081 +PORT_HTTP_EXTERNAL=8080 +PORT_STREAMLIT_INTERNAL=8501 +PORT_STREAMLIT_EXTERNAL=8500 diff --git a/computer-use-demo/Dockerfile b/computer-use-demo/Dockerfile index f3b00255..721d2184 100644 --- a/computer-use-demo/Dockerfile +++ b/computer-use-demo/Dockerfile @@ -7,6 +7,7 @@ RUN apt-get update && \ apt-get -y upgrade && \ apt-get -y install \ build-essential \ + gettext-base \ # UI Requirements xvfb \ xterm \ @@ -18,6 +19,7 @@ RUN apt-get update && \ x11vnc \ # Python/pyenv reqs build-essential \ + gettext-base \ libssl-dev \ zlib1g-dev \ libbz2-dev \ diff --git a/computer-use-demo/image/entrypoint.sh b/computer-use-demo/image/entrypoint.sh index c0a5e677..1eaabd0c 100755 --- a/computer-use-demo/image/entrypoint.sh +++ b/computer-use-demo/image/entrypoint.sh @@ -1,15 +1,22 @@ #!/bin/bash set -e +# Source and export all variables from .ports +set -a +source /host/.ports || exit 1 +set +a + ./start_all.sh ./novnc_startup.sh -python http_server.py > /tmp/server_logs.txt 2>&1 & +# Generate index.html from template +envsubst < static_content/index.html.template > static_content/index.html -STREAMLIT_SERVER_PORT=8501 python -m streamlit run computer_use_demo/streamlit.py > /tmp/streamlit_stdout.log & +python http_server.py > /tmp/server_logs.txt 2>&1 & +STREAMLIT_SERVER_PORT=$PORT_STREAMLIT_INTERNAL python -m streamlit run computer_use_demo/streamlit.py > /tmp/streamlit_stdout.log & echo "✨ Computer Use Demo is ready!" -echo "➡️ Open http://localhost:8080 in your browser to begin" +echo "➡️ Open http://localhost:$PORT_HTTP_EXTERNAL in your browser to begin" # Keep the container running tail -f /dev/null diff --git a/computer-use-demo/image/http_server.py b/computer-use-demo/image/http_server.py index 082ff4de..560823d4 100644 --- a/computer-use-demo/image/http_server.py +++ b/computer-use-demo/image/http_server.py @@ -2,6 +2,8 @@ import socket from http.server import HTTPServer, SimpleHTTPRequestHandler +PORT = int(os.environ['PORT_HTTP_INTERNAL']) + class HTTPServerV6(HTTPServer): address_family = socket.AF_INET6 @@ -9,9 +11,9 @@ class HTTPServerV6(HTTPServer): def run_server(): os.chdir(os.path.dirname(__file__) + "/static_content") - server_address = ("::", 8080) + server_address = ("::", PORT) httpd = HTTPServerV6(server_address, SimpleHTTPRequestHandler) - print("Starting HTTP server on port 8080...") # noqa: T201 + print(f"Starting HTTP server on port {PORT}...") # noqa: T201 httpd.serve_forever() diff --git a/computer-use-demo/image/novnc_startup.sh b/computer-use-demo/image/novnc_startup.sh index da56816c..79093524 100755 --- a/computer-use-demo/image/novnc_startup.sh +++ b/computer-use-demo/image/novnc_startup.sh @@ -3,19 +3,22 @@ echo "starting noVNC" # Start noVNC with explicit websocket settings /opt/noVNC/utils/novnc_proxy \ - --vnc localhost:5900 \ - --listen 6080 \ + --vnc localhost:$PORT_VNC_INTERNAL \ + --listen $PORT_NOVNC_INTERNAL \ --web /opt/noVNC \ > /tmp/novnc.log 2>&1 & # Wait for noVNC to start timeout=10 while [ $timeout -gt 0 ]; do - if netstat -tuln | grep -q ":6080 "; then + if netstat -tuln | grep -q ":$PORT_NOVNC_INTERNAL "; then break fi sleep 1 - ((timeout--)) + timeout=$((timeout - 1)) done -echo "noVNC started successfully" +if [ $timeout -eq 0 ]; then + echo "Failed to start noVNC" >&2 + exit 1 +fi diff --git a/computer-use-demo/image/static_content/index.html b/computer-use-demo/image/static_content/index.html.template similarity index 91% rename from computer-use-demo/image/static_content/index.html rename to computer-use-demo/image/static_content/index.html.template index cfd2ae13..27377e7b 100644 --- a/computer-use-demo/image/static_content/index.html +++ b/computer-use-demo/image/static_content/index.html.template @@ -29,13 +29,13 @@
diff --git a/computer-use-demo/image/x11vnc_startup.sh b/computer-use-demo/image/x11vnc_startup.sh index ad4b352c..41df1022 100755 --- a/computer-use-demo/image/x11vnc_startup.sh +++ b/computer-use-demo/image/x11vnc_startup.sh @@ -1,11 +1,11 @@ #!/bin/bash -echo "starting vnc" +echo "starting vnc $PORT_VNC_INTERNAL" (x11vnc -display $DISPLAY \ -forever \ -shared \ -wait 50 \ - -rfbport 5900 \ + -rfbport "$PORT_VNC_INTERNAL" \ -nopw \ 2>/tmp/x11vnc_stderr.log) & @@ -14,33 +14,14 @@ x11vnc_pid=$! # Wait for x11vnc to start timeout=10 while [ $timeout -gt 0 ]; do - if netstat -tuln | grep -q ":5900 "; then + if netstat -tuln | grep -q ":$PORT_VNC_INTERNAL "; then break fi sleep 1 - ((timeout--)) + timeout=$((timeout - 1)) done if [ $timeout -eq 0 ]; then - echo "x11vnc failed to start, stderr output:" >&2 - cat /tmp/x11vnc_stderr.log >&2 + echo "Failed to start x11vnc" >&2 exit 1 fi - -: > /tmp/x11vnc_stderr.log - -# Monitor x11vnc process in the background -( - while true; do - if ! kill -0 $x11vnc_pid 2>/dev/null; then - echo "x11vnc process crashed, restarting..." >&2 - if [ -f /tmp/x11vnc_stderr.log ]; then - echo "x11vnc stderr output:" >&2 - cat /tmp/x11vnc_stderr.log >&2 - rm /tmp/x11vnc_stderr.log - fi - exec "$0" - fi - sleep 5 - done -) & diff --git a/computer-use-demo/run.sh b/computer-use-demo/run.sh new file mode 100755 index 00000000..3ca088e2 --- /dev/null +++ b/computer-use-demo/run.sh @@ -0,0 +1,168 @@ +#!/bin/bash +set -e + +# Directory containing this script +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Source port configuration +source .ports || { + echo "Error: Failed to load port configuration from .ports" >&2 + exit 1 +} + +# Function to check if a port is available +check_port() { + local port=$1 + if netstat -tuln | grep -q ":${port} "; then + echo "Error: Port ${port} is already in use" >&2 + return 1 + fi + return 0 +} + +# Check all external ports +echo "Checking port availability..." +for port in $PORT_VNC_EXTERNAL $PORT_NOVNC_EXTERNAL $PORT_HTTP_EXTERNAL $PORT_STREAMLIT_EXTERNAL; do + check_port $port || exit 1 +done +echo "All ports are available." + +IMAGE_NAME="" +CONTAINER_NAME="" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --image) + IMAGE_NAME="$2" + shift 2 + ;; + --container) + CONTAINER_NAME="$2" + shift 2 + ;; + *) + echo "Usage: $0 --image NAME --container NAME" + exit 1 + ;; + esac +done + +# Verify required arguments +if [ -z "$IMAGE_NAME" ] || [ -z "$CONTAINER_NAME" ]; then + echo "Error: --image NAME and --container NAME are required" + echo "Usage: $0 --image NAME --container NAME" + exit 1 +fi + +# Change to script directory and source .env +cd "$(dirname "$0")" +if [ ! -f .env ]; then + echo "Error: .env file not found" + exit 1 +fi +source .env + +if [ -z "$ANTHROPIC_API_KEY" ]; then + echo "Error: ANTHROPIC_API_KEY not set in .env" + exit 1 +fi + +# Helper functions for ID formatting +short_id() { + echo "$1" | cut -c1-4 +} + +container_name() { + local id=$1 + local name=$2 + echo "$name ($(short_id $id)..)" +} + +# Function to check if container is running +container_is_running() { + docker inspect -f '{{.State.Running}}' "$CONTAINER_ID" 2>/dev/null | grep -q true + return $? +} + +# Remove existing container if it exists +if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then + echo "Removing existing $CONTAINER_NAME container..." + docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 +fi + +# Start container and get container ID +CONTAINER_ID=$(docker run -d \ + -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \ + -v "$(pwd)":/host \ + -p ${PORT_VNC_EXTERNAL}:${PORT_VNC_INTERNAL} \ + -p ${PORT_STREAMLIT_EXTERNAL}:${PORT_STREAMLIT_INTERNAL} \ + -p ${PORT_NOVNC_EXTERNAL}:${PORT_NOVNC_INTERNAL} \ + -p ${PORT_HTTP_EXTERNAL}:${PORT_HTTP_INTERNAL} \ + --name "$CONTAINER_NAME" \ + -it "$IMAGE_NAME") + +if [ -z "$CONTAINER_ID" ]; then + echo "Error: Failed to start container." + exit 1 +fi + +# Get image ID for nice display +IMAGE_ID=$(docker inspect --format='{{.Id}}' "$IMAGE_NAME" 2>/dev/null | sed 's/sha256://') + +echo "Container $(container_name "$CONTAINER_ID" "$CONTAINER_NAME") started from image $IMAGE_NAME ($(short_id "$IMAGE_ID")..)" + +# Wait until the container is fully running +echo "Waiting for container to reach running state..." +until container_is_running; do + sleep 1 +done +echo "Container is now running." + +# Start log streaming in background +docker logs -f "$CONTAINER_ID" & +LOG_PID=$! + +# Initialize signal handling +SIGNAL_RECEIVED=0 +CLEANUP_DONE=0 + +# Cleanup function +cleanup() { + if [ "$CLEANUP_DONE" -eq 1 ]; then + return + fi + CLEANUP_DONE=1 + + echo -e "\nShutting down..." + echo "Stopping container $(container_name "$CONTAINER_ID" "$CONTAINER_NAME")..." + result=$(docker stop "$CONTAINER_ID" 2>&1) + if [ "$result" = "$CONTAINER_ID" ]; then + echo "✅ Stopped" + else + echo "⚠️ Unexpected output: $result" + fi + + echo "Cleaning up background processes..." + kill $LOG_PID 2>/dev/null + + echo "Shutdown complete." +} +trap 'SIGNAL_RECEIVED=1; cleanup' EXIT INT TERM + +echo "Container started. Access points:" +echo "- Combined interface: http://localhost:${PORT_HTTP_EXTERNAL}" +echo "- Streamlit interface: http://localhost:${PORT_STREAMLIT_EXTERNAL}" +echo "- Desktop view: http://localhost:${PORT_NOVNC_EXTERNAL}/vnc.html" +echo "- Direct VNC: vnc://localhost:${PORT_VNC_EXTERNAL}" + +# Monitor loop +while container_is_running; do + sleep 1 +done + +# Only show error if we didn't receive SIGINT/SIGTERM +if [ "${SIGNAL_RECEIVED:-0}" != "1" ]; then + echo "Container stopped unexpectedly." + exit 1 +fi