diff --git a/scripts/start_ursim.sh b/scripts/start_ursim.sh index 977fb16cf..cd366057f 100755 --- a/scripts/start_ursim.sh +++ b/scripts/start_ursim.sh @@ -404,27 +404,46 @@ post_setup_polyscopex() curl -L -o "$urcapx_file" "$URCAPX_DOWNLOAD_URL" fi - # Prefer a published host mapping for HTTP (needed on Docker Desktop / NAT where the - # container IP is unreachable). Fall back to the container IP when nothing is published. - local forwarded_endpoint http_host - if forwarded_endpoint=$(get_forwarded_access_endpoint 80); then - http_host="$forwarded_endpoint" - else - http_host="$IP_ADDRESS" + # UrService is reachable on the container IP from another container on ursim_net + # (CI). A published host port is required on Docker Desktop, where that IP is + # not routed. Probe both, and install the URCap through whichever answers. + local forwarded_endpoint="" + forwarded_endpoint=$(get_forwarded_access_endpoint 80) || forwarded_endpoint="" + local -a base_urls=("http://${IP_ADDRESS}") + if [[ -n "$forwarded_endpoint" ]]; then + base_urls+=("http://${forwarded_endpoint}") fi - local base_url="http://${http_host}" - local urcaps_url="${base_url}/universal-robots/urservice/api/v1/urcaps" + local urcaps_path="/universal-robots/urservice/api/v1/urcaps" + # Overall wait for UrService. Override with URSIM_URSERVICE_TIMEOUT (seconds). + local urservice_timeout="${URSIM_URSERVICE_TIMEOUT:-300}" + local urcaps_url="" + local base status_code + local deadline=$((SECONDS + urservice_timeout)) echo -ne "Starting URSim. Waiting for UrService to be up..." - status_code=$(curl --retry-connrefused -f --write-out "%{http_code}" --silent --output /dev/null "$urcaps_url") - - until [ "$status_code" == "200" ] - do - sleep 1 - echo -ne "." - status_code=$(curl --retry-connrefused -f --write-out "%{http_code}" --silent --output /dev/null "$urcaps_url") + while [[ -z "$urcaps_url" && "$SECONDS" -lt "$deadline" ]]; do + for base in "${base_urls[@]}"; do + status_code=$( + curl --connect-timeout 2 --max-time 5 --silent --output /dev/null \ + --write-out "%{http_code}" "${base}${urcaps_path}" || true + ) + if [[ "$status_code" == "200" ]]; then + urcaps_url="${base}${urcaps_path}" + break + fi + done + if [[ -z "$urcaps_url" ]]; then + sleep 1 + echo -ne "." + fi done + if [[ -z "$urcaps_url" ]]; then + echo "" + echo "Timed out after ${urservice_timeout}s waiting for UrService. Tried: ${base_urls[*]}" + return 1 + fi + echo ""; echo "UrService is up" echo "Installing URCapX $urcapx_file" @@ -666,7 +685,13 @@ main() { trap "$TRAP_CMD" SIGINT SIGTERM - eval "post_setup_${ROBOT_SERIES}" + if ! "post_setup_${ROBOT_SERIES}"; then + if [ "$DETACHED" = false ]; then + docker container kill "$CONTAINER_NAME" > /dev/null 2>&1 || true + docker container wait "$CONTAINER_NAME" > /dev/null 2>&1 || true + fi + exit 1 + fi if [ "$DETACHED" = false ]; then echo "To exit, press CTRL+C" diff --git a/tests/test_start_ursim.bats b/tests/test_start_ursim.bats index 5c2e802a0..f86e4d050 100644 --- a/tests/test_start_ursim.bats +++ b/tests/test_start_ursim.bats @@ -981,7 +981,7 @@ stub_docker_port() { [[ "$output" != *"Access PolyScope X: http://192."* ]] } -@test "post_setup_polyscopex uses forwarded endpoint for http calls" { +@test "post_setup_polyscopex uses container IP for http calls when it responds" { IP_ADDRESS="192.168.56.101" CONTAINER_NAME="ursim" DOCKER_PORT_MAP='80=127.0.0.1:8000' @@ -1000,15 +1000,73 @@ stub_docker_port() { touch "$URCAP_STORAGE/external-control-0.0.0.urcapx" URSIM_VERSION="10.8.0" + run post_setup_polyscopex + echo "$output" + echo "curl log:"; cat "$CURL_LOG" + [ "$status" -eq 0 ] + grep -F "http://192.168.56.101/universal-robots/urservice/api/v1/urcaps" "$CURL_LOG" + ! grep -F "http://127.0.0.1:8000/universal-robots/urservice/api/v1/urcaps" "$CURL_LOG" + rm -f "$CURL_LOG" +} + +@test "post_setup_polyscopex uses forwarded endpoint when container IP is unreachable" { + IP_ADDRESS="192.168.56.101" + CONTAINER_NAME="ursim" + DOCKER_PORT_MAP='80=127.0.0.1:8000' + stub_docker_port + get_download_url_urcapx() { URCAPX_VERSION="0.0.0"; URCAPX_DOWNLOAD_URL=""; } + CURL_LOG=$(mktemp) + curl() { + printf '%s\n' "$*" >> "$CURL_LOG" + if [[ "$*" == *"--form"* ]]; then + return 0 + fi + if [[ "$*" == *"http://192.168.56.101/universal-robots/urservice/api/v1/urcaps"* ]]; then + echo "000" + return 7 + fi + echo "200" + } + URCAP_STORAGE=/tmp/ursim-test-urcaps-post-setup + mkdir -p "$URCAP_STORAGE" + touch "$URCAP_STORAGE/external-control-0.0.0.urcapx" + URSIM_VERSION="10.8.0" + run post_setup_polyscopex echo "$output" echo "curl log:"; cat "$CURL_LOG" [ "$status" -eq 0 ] grep -F "http://127.0.0.1:8000/universal-robots/urservice/api/v1/urcaps" "$CURL_LOG" - ! grep -F "http://192.168.56.101/universal-robots/urservice/api/v1/urcaps" "$CURL_LOG" rm -f "$CURL_LOG" } +@test "post_setup_polyscopex times out when UrService does not respond" { + IP_ADDRESS="192.168.56.101" + CONTAINER_NAME="ursim" + DOCKER_PORT_MAP='80=127.0.0.1:8000' + stub_docker_port + get_download_url_urcapx() { URCAPX_VERSION="0.0.0"; URCAPX_DOWNLOAD_URL=""; } + curl() { + if [[ "$*" == *"--form"* ]]; then + return 0 + fi + echo "000" + return 7 + } + URCAP_STORAGE=/tmp/ursim-test-urcaps-post-setup + mkdir -p "$URCAP_STORAGE" + touch "$URCAP_STORAGE/external-control-0.0.0.urcapx" + URSIM_VERSION="10.8.0" + URSIM_URSERVICE_TIMEOUT=1 + + run post_setup_polyscopex + echo "$output" + [ "$status" -ne 0 ] + [[ "$output" == *"Timed out after 1s waiting for UrService."* ]] + [[ "$output" == *"http://192.168.56.101"* ]] + [[ "$output" == *"http://127.0.0.1:8000"* ]] +} + @test "post_setup_polyscopex falls back to IP_ADDRESS for http calls when unpublished" { IP_ADDRESS="192.168.56.101" CONTAINER_NAME="ursim"