diff --git a/.github/workflows/run-samples.yml b/.github/workflows/run-samples.yml index fe50b5f..f8ba93b 100644 --- a/.github/workflows/run-samples.yml +++ b/.github/workflows/run-samples.yml @@ -216,6 +216,10 @@ jobs: echo "::warning::Emulator was not ready within 400s (attempt ${attempt}/${attempts})." docker logs localstack-main --tail 100 2>&1 || true python -m localstack_cli.cli.main stop || true + # stop does not wait for Docker to remove the container, and a start that still finds it fails with + # 'container named "localstack-main" already exists'. + docker rm -f localstack-main >/dev/null 2>&1 || true + for _ in $(seq 1 30); do docker container inspect localstack-main >/dev/null 2>&1 || break; sleep 1; done done echo "::error::Emulator failed to become ready after ${attempts} attempts." exit 1 @@ -307,6 +311,10 @@ jobs: source .venv/bin/activate echo "::warning::${MATRIX_NAME} failed once; retrying on a fresh emulator." python -m localstack_cli.cli.main stop || true + # stop does not wait for Docker to remove the container, and a start that still finds it fails with + # 'container named "localstack-main" already exists'. + docker rm -f localstack-main >/dev/null 2>&1 || true + for _ in $(seq 1 30); do docker container inspect localstack-main >/dev/null 2>&1 || break; sleep 1; done python -m localstack_cli.cli.main start -d python -m localstack_cli.cli.main wait -t 600 make test SHARD="${SHARD}" SPLITS="${SPLITS}" diff --git a/samples/function-app-front-door/python/scripts/call-front-door.sh b/samples/function-app-front-door/python/scripts/call-front-door.sh index 48c82b9..f7f3eea 100755 --- a/samples/function-app-front-door/python/scripts/call-front-door.sh +++ b/samples/function-app-front-door/python/scripts/call-front-door.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" PROFILE_NAME="${PREFIX}-catalog-afd-${SUFFIX}" ENDPOINT_NAME="${PREFIX}-catalog-${SUFFIX}" diff --git a/samples/function-app-front-door/python/scripts/cleanup.sh b/samples/function-app-front-door/python/scripts/cleanup.sh index ede7f93..e6cf3c0 100755 --- a/samples/function-app-front-door/python/scripts/cleanup.sh +++ b/samples/function-app-front-door/python/scripts/cleanup.sh @@ -8,7 +8,7 @@ # accounts and the shared App Service plan are all inside it. # ============================================================================= -PREFIX='local' +PREFIX="${PREFIX:-local}" RESOURCE_GROUP_NAME="${PREFIX}-rg" CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" diff --git a/samples/function-app-front-door/python/scripts/deploy.sh b/samples/function-app-front-door/python/scripts/deploy.sh index 197242c..dbdfcf8 100755 --- a/samples/function-app-front-door/python/scripts/deploy.sh +++ b/samples/function-app-front-door/python/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" APP_SERVICE_PLAN_NAME="${PREFIX}-catalog-app-service-plan-${SUFFIX}" diff --git a/samples/function-app-front-door/python/scripts/validate.sh b/samples/function-app-front-door/python/scripts/validate.sh index b269c82..803fe8f 100755 --- a/samples/function-app-front-door/python/scripts/validate.sh +++ b/samples/function-app-front-door/python/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" PRIMARY_FUNCTION_APP_NAME="${PREFIX}-catalog-primary-${SUFFIX}" SECONDARY_FUNCTION_APP_NAME="${PREFIX}-catalog-secondary-${SUFFIX}" diff --git a/samples/function-app-managed-identity/python/bicep/deploy.sh b/samples/function-app-managed-identity/python/bicep/deploy.sh index 9773b08..d1ca5da 100755 --- a/samples/function-app-managed-identity/python/bicep/deploy.sh +++ b/samples/function-app-managed-identity/python/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="$PREFIX-rg" diff --git a/samples/function-app-managed-identity/python/scripts/system-managed-identity.sh b/samples/function-app-managed-identity/python/scripts/system-managed-identity.sh index dd04639..43925aa 100755 --- a/samples/function-app-managed-identity/python/scripts/system-managed-identity.sh +++ b/samples/function-app-managed-identity/python/scripts/system-managed-identity.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" FUNCTION_APP_NAME="${PREFIX}-functionapp-${SUFFIX}" diff --git a/samples/function-app-managed-identity/python/scripts/test.sh b/samples/function-app-managed-identity/python/scripts/test.sh index 02cea01..e62c1ae 100755 --- a/samples/function-app-managed-identity/python/scripts/test.sh +++ b/samples/function-app-managed-identity/python/scripts/test.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" FILE_PATH="input.txt" INPUT_CONTAINER_NAME="input" OUTPUT_CONTAINER_NAME="output" diff --git a/samples/function-app-managed-identity/python/scripts/user-managed-identity.sh b/samples/function-app-managed-identity/python/scripts/user-managed-identity.sh index 6432874..06015f1 100755 --- a/samples/function-app-managed-identity/python/scripts/user-managed-identity.sh +++ b/samples/function-app-managed-identity/python/scripts/user-managed-identity.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='northeurope' STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" MANAGED_IDENTITY_NAME="${PREFIX}-identity-${SUFFIX}" diff --git a/samples/function-app-managed-identity/python/scripts/validate.sh b/samples/function-app-managed-identity/python/scripts/validate.sh index 4b9dd3e..021bee6 100755 --- a/samples/function-app-managed-identity/python/scripts/validate.sh +++ b/samples/function-app-managed-identity/python/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" INPUT_STORAGE_CONTAINER_NAME='input' diff --git a/samples/function-app-managed-identity/python/terraform/deploy.sh b/samples/function-app-managed-identity/python/terraform/deploy.sh index 674a8be..22eeec3 100755 --- a/samples/function-app-managed-identity/python/terraform/deploy.sh +++ b/samples/function-app-managed-identity/python/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' MANAGED_IDENTITY_TYPE='UserAssigned' # SystemAssigned or UserAssigned CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" diff --git a/samples/function-app-service-bus/dotnet/README.md b/samples/function-app-service-bus/dotnet/README.md index c178a48..7f08efd 100644 --- a/samples/function-app-service-bus/dotnet/README.md +++ b/samples/function-app-service-bus/dotnet/README.md @@ -103,7 +103,9 @@ Found matching container [ls-local-func-test-tdkqjh] Container [ls-local-func-test-tdkqjh] found successfully Getting IP address for container [ls-local-func-test-tdkqjh]... IP address [172.17.0.7] retrieved successfully for container [ls-local-func-test-tdkqjh] -Getting the host port mapped to internal port 80 in container [ls-local-func-test-tdkqjh]... +Getting the port the function app listens on in container [ls-local-func-test-tdkqjh]... +Function app listens on port [80] in container [ls-local-func-test-tdkqjh] +Getting the host port mapped to port [80] in container [ls-local-func-test-tdkqjh]... Mapped host port [42330] retrieved successfully for container [ls-local-func-test-tdkqjh] Calling HTTP trigger function to retrieve the last [100] greetings via hostname [babo-func-test.azurewebsites.azure.localhost.localstack.cloud:4566]... { diff --git a/samples/function-app-service-bus/dotnet/bicep/deploy.sh b/samples/function-app-service-bus/dotnet/bicep/deploy.sh index 79ca625..9a03d5a 100755 --- a/samples/function-app-service-bus/dotnet/bicep/deploy.sh +++ b/samples/function-app-service-bus/dotnet/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="${PREFIX}-rg" diff --git a/samples/function-app-service-bus/dotnet/scripts/call-http-trigger.sh b/samples/function-app-service-bus/dotnet/scripts/call-http-trigger.sh index 1268060..a8384f2 100755 --- a/samples/function-app-service-bus/dotnet/scripts/call-http-trigger.sh +++ b/samples/function-app-service-bus/dotnet/scripts/call-http-trigger.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-http-trigger.sh [function-app-name] +# +# The function app to call is the one named by the first argument, else by FUNCTION_APP_NAME, else the only function app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,10 +70,40 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_http_trigger_function() { - # Get the function app name - echo "Getting function app name..." - function_app_name=$(az functionapp list --query '[0].name' --output tsv) + # The function app to call: the one the caller named, else the only one in the subscription + function_app_name="$1" + if [ -z "$function_app_name" ]; then + echo "Getting the name of the only function app in the subscription..." + function_app_count=$(az functionapp list --query "length(@)" --output tsv) + if [ "$function_app_count" != "1" ]; then + echo "Error: Found [${function_app_count:-0}] function apps; name the one to call: $0 " + exit 1 + fi + function_app_name=$(az functionapp list --query "[0].name" --output tsv) + fi if [ -n "$function_app_name" ]; then echo "Function app [$function_app_name] successfully retrieved." @@ -79,7 +114,7 @@ call_http_trigger_function() { # Get the resource group name echo "Getting resource group name for function app [$function_app_name]..." - resource_group_name=$(az functionapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az functionapp list --query "[?name=='${function_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -126,9 +161,19 @@ call_http_trigger_function() { exit 1 fi - # Get the mapped host port for function app HTTP trigger (internal port 80) - echo "Getting the host port mapped to internal port 80 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "80") + # Get the port the function app listens on in its container + echo "Getting the port the function app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Function app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the function app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -145,10 +190,10 @@ call_http_trigger_function() { echo "Failed to retrieve function hostname" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the GetGreetings HTTP trigger function to retrieve the last greetings via the container IP address echo "Calling HTTP trigger function to retrieve the last [$greeting_count] greetings via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip/api/greetings?count=$greeting_count" | jq + curl --max-time 10 -s "http://$container_ip:$container_port/api/greetings?count=$greeting_count" | jq else echo "Failed to retrieve container IP address" fi @@ -162,4 +207,4 @@ call_http_trigger_function() { fi } -call_http_trigger_function \ No newline at end of file +call_http_trigger_function "${1:-${FUNCTION_APP_NAME:-}}" diff --git a/samples/function-app-service-bus/dotnet/scripts/deploy.sh b/samples/function-app-service-bus/dotnet/scripts/deploy.sh index 91a51fe..420add4 100755 --- a/samples/function-app-service-bus/dotnet/scripts/deploy.sh +++ b/samples/function-app-service-bus/dotnet/scripts/deploy.sh @@ -1,7 +1,7 @@ #!/bin/bash -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" diff --git a/samples/function-app-service-bus/dotnet/scripts/validate.sh b/samples/function-app-service-bus/dotnet/scripts/validate.sh index ae43236..b3b75e0 100755 --- a/samples/function-app-service-bus/dotnet/scripts/validate.sh +++ b/samples/function-app-service-bus/dotnet/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" FUNCTION_APP_SUBNET_NSG_NAME="${PREFIX}-func-subnet-nsg-${SUFFIX}" diff --git a/samples/function-app-service-bus/dotnet/terraform/deploy.sh b/samples/function-app-service-bus/dotnet/terraform/deploy.sh index 7509c53..8fd6be2 100755 --- a/samples/function-app-service-bus/dotnet/terraform/deploy.sh +++ b/samples/function-app-service-bus/dotnet/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" ZIPFILE="functionapp.zip" diff --git a/samples/function-app-storage-http/dotnet/bicep/deploy.sh b/samples/function-app-storage-http/dotnet/bicep/deploy.sh index cf41950..1440792 100755 --- a/samples/function-app-storage-http/dotnet/bicep/deploy.sh +++ b/samples/function-app-storage-http/dotnet/bicep/deploy.sh @@ -3,7 +3,9 @@ # Variables TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" -RESOURCE_GROUP_NAME="local-func-storage-rg" +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" +RESOURCE_GROUP_NAME="${PREFIX}-func-storage-rg" LOCATION="westeurope" VALIDATE_TEMPLATE=1 USE_WHAT_IF=0 @@ -45,7 +47,7 @@ if [[ $VALIDATE_TEMPLATE == 1 ]]; then --template-file $TEMPLATE \ --parameters $PARAMETERS \ --parameters \ - location=$LOCATION + location=$LOCATION prefix="$PREFIX" suffix="$SUFFIX" if [[ $? == 0 ]]; then echo "Bicep template [$TEMPLATE] validation succeeded" @@ -61,7 +63,7 @@ if [[ $VALIDATE_TEMPLATE == 1 ]]; then --template-file $TEMPLATE \ --parameters $PARAMETERS \ --parameters \ - location=$LOCATION) + location=$LOCATION prefix="$PREFIX" suffix="$SUFFIX") if [[ $? == 0 ]]; then echo "Bicep template [$TEMPLATE] validation succeeded" @@ -80,7 +82,7 @@ if DEPLOYMENT_OUTPUTS=$(az deployment group create \ --only-show-errors \ --template-file $TEMPLATE \ --parameters $PARAMETERS \ - --parameters location=$LOCATION \ + --parameters location=$LOCATION prefix="$PREFIX" suffix="$SUFFIX" \ --query 'properties.outputs' -o json); then echo "Bicep template [$TEMPLATE] deployed successfully. Outputs:" echo "$DEPLOYMENT_OUTPUTS" | jq . diff --git a/samples/function-app-storage-http/dotnet/scripts/call-http-triggers.sh b/samples/function-app-storage-http/dotnet/scripts/call-http-triggers.sh index 7409291..73f819b 100755 --- a/samples/function-app-storage-http/dotnet/scripts/call-http-triggers.sh +++ b/samples/function-app-storage-http/dotnet/scripts/call-http-triggers.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-http-triggers.sh [function-app-name] +# +# The function app to call is the one named by the first argument, else by FUNCTION_APP_NAME, else the only function app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,10 +70,40 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_http_trigger_functions() { - # Get the function app name - echo "Getting function app name..." - function_app_name=$(az functionapp list --query '[0].name' --output tsv) + # The function app to call: the one the caller named, else the only one in the subscription + function_app_name="$1" + if [ -z "$function_app_name" ]; then + echo "Getting the name of the only function app in the subscription..." + function_app_count=$(az functionapp list --query "length(@)" --output tsv) + if [ "$function_app_count" != "1" ]; then + echo "Error: Found [${function_app_count:-0}] function apps; name the one to call: $0 " + exit 1 + fi + function_app_name=$(az functionapp list --query "[0].name" --output tsv) + fi if [ -n "$function_app_name" ]; then echo "Function app [$function_app_name] successfully retrieved." @@ -79,7 +114,7 @@ call_http_trigger_functions() { # Get the resource group name echo "Getting resource group name for function app [$function_app_name]..." - resource_group_name=$(az functionapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az functionapp list --query "[?name=='${function_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -127,9 +162,19 @@ call_http_trigger_functions() { exit 1 fi - # Get the mapped host port for function app HTTP trigger (internal port 80) - echo "Getting the host port mapped to internal port 80 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "80") + # Get the port the function app listens on in its container + echo "Getting the port the function app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Function app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the function app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -150,14 +195,14 @@ call_http_trigger_functions() { echo "Failed to retrieve function hostname" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the GET HTTP trigger function that returns a player status in a specified game session via the container IP address echo "Calling HTTP trigger function to retrieve player [$player_name] status in game session [$game_session] via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip/api/player/$game_session/$player_name/status" | jq + curl --max-time 10 -s "http://$container_ip:$container_port/api/player/$game_session/$player_name/status" | jq # Call the POST HTTP trigger function that returns the game session details via the container IP address echo "Calling HTTP trigger function to retrieve game session [$game_session] details via container IP address [$container_ip]..." - curl --max-time 10 -s -X POST -H "Content-Type: application/json" -d "{\"gameId\": $game_session}" "http://$container_ip/api/game/session" | jq + curl --max-time 10 -s -X POST -H "Content-Type: application/json" -d "{\"gameId\": $game_session}" "http://$container_ip:$container_port/api/game/session" | jq else echo "Failed to retrieve container IP address" fi @@ -175,4 +220,4 @@ call_http_trigger_functions() { fi } -call_http_trigger_functions \ No newline at end of file +call_http_trigger_functions "${1:-${FUNCTION_APP_NAME:-}}" diff --git a/samples/function-app-storage-http/dotnet/scripts/deploy.sh b/samples/function-app-storage-http/dotnet/scripts/deploy.sh index 8481903..7c16c92 100755 --- a/samples/function-app-storage-http/dotnet/scripts/deploy.sh +++ b/samples/function-app-storage-http/dotnet/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' FUNCTION_APP_NAME="${PREFIX}-func-${SUFFIX}" STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" diff --git a/samples/function-app-storage-http/dotnet/terraform/deploy.sh b/samples/function-app-storage-http/dotnet/terraform/deploy.sh index 6578b8f..7263a26 100755 --- a/samples/function-app-storage-http/dotnet/terraform/deploy.sh +++ b/samples/function-app-storage-http/dotnet/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' #system or user -SUFFIX='test' +PREFIX="${PREFIX:-local}" #system or user +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" ZIPFILE="function_app.zip" diff --git a/samples/web-app-app-configuration/dotnet/README.md b/samples/web-app-app-configuration/dotnet/README.md index ee4a152..f884767 100644 --- a/samples/web-app-app-configuration/dotnet/README.md +++ b/samples/web-app-app-configuration/dotnet/README.md @@ -104,7 +104,7 @@ The store and the vault have globally unique names: on Azure, export your own `S ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the *Vacation Planner* UI with the seeded activities and can add, edit, and remove activities. diff --git a/samples/web-app-app-configuration/dotnet/scripts/call-web-app.sh b/samples/web-app-app-configuration/dotnet/scripts/call-web-app.sh index b521aed..f98ceac 100755 --- a/samples/web-app-app-configuration/dotnet/scripts/call-web-app.sh +++ b/samples/web-app-app-configuration/dotnet/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,13 +70,43 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { # Web app port - local web_app_port=8000 - - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + local web_app_port + + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -82,7 +117,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -128,8 +163,18 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 8000) - echo "Getting the host port mapped to internal port $web_app_port in container [$container_name]..." + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + web_app_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$web_app_port" ]; then + echo "Web app listens on port [$web_app_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$web_app_port] in container [$container_name]..." host_port=$(get_docker_container_port_mapping "$container_name" "$web_app_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then @@ -155,7 +200,7 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$web_app_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." curl --max-time 10 -s "http://$container_ip:$web_app_port/" 1> /dev/null @@ -198,4 +243,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-app-configuration/python/README.md b/samples/web-app-app-configuration/python/README.md index 37d4378..8d10431 100644 --- a/samples/web-app-app-configuration/python/README.md +++ b/samples/web-app-app-configuration/python/README.md @@ -101,7 +101,7 @@ The store and the vault have globally unique names: on Azure, export your own `S ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the *Vacation Planner* UI with the seeded activities and can add, edit, and remove activities. diff --git a/samples/web-app-app-configuration/python/scripts/call-web-app.sh b/samples/web-app-app-configuration/python/scripts/call-web-app.sh index b521aed..f98ceac 100755 --- a/samples/web-app-app-configuration/python/scripts/call-web-app.sh +++ b/samples/web-app-app-configuration/python/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,13 +70,43 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { # Web app port - local web_app_port=8000 - - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + local web_app_port + + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -82,7 +117,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -128,8 +163,18 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 8000) - echo "Getting the host port mapped to internal port $web_app_port in container [$container_name]..." + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + web_app_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$web_app_port" ]; then + echo "Web app listens on port [$web_app_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$web_app_port] in container [$container_name]..." host_port=$(get_docker_container_port_mapping "$container_name" "$web_app_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then @@ -155,7 +200,7 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$web_app_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." curl --max-time 10 -s "http://$container_ip:$web_app_port/" 1> /dev/null @@ -198,4 +243,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-cosmosdb-mongodb-api/dotnet/README.md b/samples/web-app-cosmosdb-mongodb-api/dotnet/README.md index 59ee963..41e010f 100644 --- a/samples/web-app-cosmosdb-mongodb-api/dotnet/README.md +++ b/samples/web-app-cosmosdb-mongodb-api/dotnet/README.md @@ -71,7 +71,7 @@ All deployment methods have been fully tested against Azure and the LocalStack f ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the following user interface for adding and removing activities: diff --git a/samples/web-app-cosmosdb-mongodb-api/dotnet/bicep/deploy.sh b/samples/web-app-cosmosdb-mongodb-api/dotnet/bicep/deploy.sh index cd7172f..b3681de 100755 --- a/samples/web-app-cosmosdb-mongodb-api/dotnet/bicep/deploy.sh +++ b/samples/web-app-cosmosdb-mongodb-api/dotnet/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="${PREFIX}-rg" diff --git a/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/call-web-app.sh b/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/call-web-app.sh index 9c3945f..6620a4c 100755 --- a/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/call-web-app.sh +++ b/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,10 +70,40 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -79,7 +114,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -125,9 +160,19 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 8000) - echo "Getting the host port mapped to internal port 8000 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "8000") + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Web app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -152,10 +197,10 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip:8000/" 1> /dev/null + curl --max-time 10 -s "http://$container_ip:$container_port/" 1> /dev/null if [ $? == 0 ]; then echo "Web app call via container IP address [$container_ip] succeeded." @@ -195,4 +240,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/deploy.sh b/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/deploy.sh index eab28e6..921f5b7 100755 --- a/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/deploy.sh +++ b/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" diff --git a/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/validate.sh b/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/validate.sh index fd2815c..297ac8c 100755 --- a/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/validate.sh +++ b/samples/web-app-cosmosdb-mongodb-api/dotnet/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" WEB_APP_SUBNET_NSG_NAME="${PREFIX}-webapp-subnet-nsg-${SUFFIX}" diff --git a/samples/web-app-cosmosdb-mongodb-api/dotnet/terraform/deploy.sh b/samples/web-app-cosmosdb-mongodb-api/dotnet/terraform/deploy.sh index fd202c9..050fc10 100755 --- a/samples/web-app-cosmosdb-mongodb-api/dotnet/terraform/deploy.sh +++ b/samples/web-app-cosmosdb-mongodb-api/dotnet/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" ZIPFILE="planner_website.zip" diff --git a/samples/web-app-cosmosdb-mongodb-api/python/README.md b/samples/web-app-cosmosdb-mongodb-api/python/README.md index 80ffb4a..cc1fcc1 100644 --- a/samples/web-app-cosmosdb-mongodb-api/python/README.md +++ b/samples/web-app-cosmosdb-mongodb-api/python/README.md @@ -71,7 +71,7 @@ All deployment methods have been fully tested against Azure and the LocalStack f ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the following user interface for adding and removing activities: diff --git a/samples/web-app-cosmosdb-mongodb-api/python/bicep/deploy.sh b/samples/web-app-cosmosdb-mongodb-api/python/bicep/deploy.sh index 2a8cbbb..ba06a3b 100755 --- a/samples/web-app-cosmosdb-mongodb-api/python/bicep/deploy.sh +++ b/samples/web-app-cosmosdb-mongodb-api/python/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="${PREFIX}-rg" diff --git a/samples/web-app-cosmosdb-mongodb-api/python/scripts/call-web-app.sh b/samples/web-app-cosmosdb-mongodb-api/python/scripts/call-web-app.sh index 45e5ad2..6620a4c 100755 --- a/samples/web-app-cosmosdb-mongodb-api/python/scripts/call-web-app.sh +++ b/samples/web-app-cosmosdb-mongodb-api/python/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,10 +70,40 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -79,7 +114,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -125,9 +160,19 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 80) - echo "Getting the host port mapped to internal port 80 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "80") + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Web app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -152,10 +197,10 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip/" 1> /dev/null + curl --max-time 10 -s "http://$container_ip:$container_port/" 1> /dev/null if [ $? == 0 ]; then echo "Web app call via container IP address [$container_ip] succeeded." @@ -195,4 +240,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-cosmosdb-mongodb-api/python/scripts/deploy.sh b/samples/web-app-cosmosdb-mongodb-api/python/scripts/deploy.sh index e4dc109..832f603 100755 --- a/samples/web-app-cosmosdb-mongodb-api/python/scripts/deploy.sh +++ b/samples/web-app-cosmosdb-mongodb-api/python/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" diff --git a/samples/web-app-cosmosdb-mongodb-api/python/scripts/validate.sh b/samples/web-app-cosmosdb-mongodb-api/python/scripts/validate.sh index fd2815c..297ac8c 100755 --- a/samples/web-app-cosmosdb-mongodb-api/python/scripts/validate.sh +++ b/samples/web-app-cosmosdb-mongodb-api/python/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" WEB_APP_SUBNET_NSG_NAME="${PREFIX}-webapp-subnet-nsg-${SUFFIX}" diff --git a/samples/web-app-cosmosdb-mongodb-api/python/terraform/deploy.sh b/samples/web-app-cosmosdb-mongodb-api/python/terraform/deploy.sh index 5a3e0dd..d407122 100755 --- a/samples/web-app-cosmosdb-mongodb-api/python/terraform/deploy.sh +++ b/samples/web-app-cosmosdb-mongodb-api/python/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" ZIPFILE="planner_website.zip" diff --git a/samples/web-app-cosmosdb-nosql-api/dotnet/README.md b/samples/web-app-cosmosdb-nosql-api/dotnet/README.md index 925da47..8a6f55d 100644 --- a/samples/web-app-cosmosdb-nosql-api/dotnet/README.md +++ b/samples/web-app-cosmosdb-nosql-api/dotnet/README.md @@ -50,7 +50,7 @@ Deploy the application to LocalStack for Azure using: ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the following user interface for adding and removing activities: diff --git a/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/call-web-app.sh b/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/call-web-app.sh index a82964c..6f4db11 100755 --- a/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/call-web-app.sh +++ b/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,10 +70,40 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -79,7 +114,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -125,9 +160,19 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 80) - echo "Getting the host port mapped to internal port 80 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "80") + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Web app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -153,10 +198,10 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip/" 1> /dev/null + curl --max-time 10 -s "http://$container_ip:$container_port/" 1> /dev/null if [ $? == 0 ]; then echo "Web app call via container IP address [$container_ip] succeeded." @@ -196,4 +241,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/deploy.sh b/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/deploy.sh index 6c77fc6..ef3049f 100755 --- a/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/deploy.sh +++ b/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" APP_SERVICE_PLAN_NAME="${PREFIX}-app-service-plan-${SUFFIX}" diff --git a/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/validate.sh b/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/validate.sh index 542412a..2dcc342 100755 --- a/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/validate.sh +++ b/samples/web-app-cosmosdb-nosql-api/dotnet/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" WEB_APP_NAME="${PREFIX}-webapp-nosql-${SUFFIX}" APP_SERVICE_PLAN_NAME="${WEB_APP_NAME}" diff --git a/samples/web-app-cosmosdb-nosql-api/python/README.md b/samples/web-app-cosmosdb-nosql-api/python/README.md index b9b2818..317275c 100644 --- a/samples/web-app-cosmosdb-nosql-api/python/README.md +++ b/samples/web-app-cosmosdb-nosql-api/python/README.md @@ -50,7 +50,7 @@ Deploy the application to LocalStack for Azure using: ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the following user interface for adding and removing activities: diff --git a/samples/web-app-cosmosdb-nosql-api/python/scripts/call-web-app.sh b/samples/web-app-cosmosdb-nosql-api/python/scripts/call-web-app.sh index a82964c..6f4db11 100755 --- a/samples/web-app-cosmosdb-nosql-api/python/scripts/call-web-app.sh +++ b/samples/web-app-cosmosdb-nosql-api/python/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,10 +70,40 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -79,7 +114,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -125,9 +160,19 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 80) - echo "Getting the host port mapped to internal port 80 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "80") + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Web app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -153,10 +198,10 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip/" 1> /dev/null + curl --max-time 10 -s "http://$container_ip:$container_port/" 1> /dev/null if [ $? == 0 ]; then echo "Web app call via container IP address [$container_ip] succeeded." @@ -196,4 +241,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-cosmosdb-nosql-api/python/scripts/deploy.sh b/samples/web-app-cosmosdb-nosql-api/python/scripts/deploy.sh index 1adc031..d21700c 100755 --- a/samples/web-app-cosmosdb-nosql-api/python/scripts/deploy.sh +++ b/samples/web-app-cosmosdb-nosql-api/python/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" APP_SERVICE_PLAN_NAME="${PREFIX}-app-service-plan-${SUFFIX}" diff --git a/samples/web-app-cosmosdb-nosql-api/python/scripts/validate.sh b/samples/web-app-cosmosdb-nosql-api/python/scripts/validate.sh index 542412a..2dcc342 100755 --- a/samples/web-app-cosmosdb-nosql-api/python/scripts/validate.sh +++ b/samples/web-app-cosmosdb-nosql-api/python/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" WEB_APP_NAME="${PREFIX}-webapp-nosql-${SUFFIX}" APP_SERVICE_PLAN_NAME="${WEB_APP_NAME}" diff --git a/samples/web-app-custom-image/dotnet/bicep/deploy.sh b/samples/web-app-custom-image/dotnet/bicep/deploy.sh index 9c16125..e90e37b 100755 --- a/samples/web-app-custom-image/dotnet/bicep/deploy.sh +++ b/samples/web-app-custom-image/dotnet/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" ACR_TEMPLATE="acr.bicep" ACR_PARAMETERS="acr.bicepparam" MAIN_TEMPLATE="main.bicep" diff --git a/samples/web-app-custom-image/dotnet/scripts/call-web-app.sh b/samples/web-app-custom-image/dotnet/scripts/call-web-app.sh index 0c77196..2641bb3 100755 --- a/samples/web-app-custom-image/dotnet/scripts/call-web-app.sh +++ b/samples/web-app-custom-image/dotnet/scripts/call-web-app.sh @@ -1,8 +1,8 @@ #!/bin/bash set -euo pipefail -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" WEB_APP_NAME="${PREFIX}-webapp-${SUFFIX}" diff --git a/samples/web-app-custom-image/dotnet/scripts/deploy.sh b/samples/web-app-custom-image/dotnet/scripts/deploy.sh index fbd7ada..afa6a03 100755 --- a/samples/web-app-custom-image/dotnet/scripts/deploy.sh +++ b/samples/web-app-custom-image/dotnet/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" ACR_NAME="${PREFIX}acr${SUFFIX}" diff --git a/samples/web-app-custom-image/dotnet/scripts/validate.sh b/samples/web-app-custom-image/dotnet/scripts/validate.sh index b58fb61..519f558 100755 --- a/samples/web-app-custom-image/dotnet/scripts/validate.sh +++ b/samples/web-app-custom-image/dotnet/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash set -euo pipefail -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" ACR_NAME="${PREFIX}acr${SUFFIX}" MANAGED_IDENTITY_NAME="${PREFIX}-identity-${SUFFIX}" diff --git a/samples/web-app-custom-image/dotnet/terraform/deploy.sh b/samples/web-app-custom-image/dotnet/terraform/deploy.sh index 63e42fb..c9e826e 100755 --- a/samples/web-app-custom-image/dotnet/terraform/deploy.sh +++ b/samples/web-app-custom-image/dotnet/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" IMAGE_NAME="custom-image-webapp" diff --git a/samples/web-app-custom-image/python/bicep/deploy.sh b/samples/web-app-custom-image/python/bicep/deploy.sh index 9c16125..e90e37b 100755 --- a/samples/web-app-custom-image/python/bicep/deploy.sh +++ b/samples/web-app-custom-image/python/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" ACR_TEMPLATE="acr.bicep" ACR_PARAMETERS="acr.bicepparam" MAIN_TEMPLATE="main.bicep" diff --git a/samples/web-app-custom-image/python/scripts/call-web-app.sh b/samples/web-app-custom-image/python/scripts/call-web-app.sh index 0c77196..2641bb3 100755 --- a/samples/web-app-custom-image/python/scripts/call-web-app.sh +++ b/samples/web-app-custom-image/python/scripts/call-web-app.sh @@ -1,8 +1,8 @@ #!/bin/bash set -euo pipefail -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" WEB_APP_NAME="${PREFIX}-webapp-${SUFFIX}" diff --git a/samples/web-app-custom-image/python/scripts/deploy.sh b/samples/web-app-custom-image/python/scripts/deploy.sh index fbd7ada..afa6a03 100755 --- a/samples/web-app-custom-image/python/scripts/deploy.sh +++ b/samples/web-app-custom-image/python/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" ACR_NAME="${PREFIX}acr${SUFFIX}" diff --git a/samples/web-app-custom-image/python/scripts/validate.sh b/samples/web-app-custom-image/python/scripts/validate.sh index b58fb61..519f558 100755 --- a/samples/web-app-custom-image/python/scripts/validate.sh +++ b/samples/web-app-custom-image/python/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash set -euo pipefail -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" ACR_NAME="${PREFIX}acr${SUFFIX}" MANAGED_IDENTITY_NAME="${PREFIX}-identity-${SUFFIX}" diff --git a/samples/web-app-custom-image/python/terraform/deploy.sh b/samples/web-app-custom-image/python/terraform/deploy.sh index 63e42fb..c9e826e 100755 --- a/samples/web-app-custom-image/python/terraform/deploy.sh +++ b/samples/web-app-custom-image/python/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" IMAGE_NAME="custom-image-webapp" diff --git a/samples/web-app-managed-identity/dotnet/README.md b/samples/web-app-managed-identity/dotnet/README.md index 884eb97..f4308a4 100644 --- a/samples/web-app-managed-identity/dotnet/README.md +++ b/samples/web-app-managed-identity/dotnet/README.md @@ -82,7 +82,7 @@ All deployment methods have been fully tested with both user-assigned and system ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the following user interface for adding and removing activities: diff --git a/samples/web-app-managed-identity/dotnet/bicep/deploy.sh b/samples/web-app-managed-identity/dotnet/bicep/deploy.sh index 6ce9027..d4cc198 100755 --- a/samples/web-app-managed-identity/dotnet/bicep/deploy.sh +++ b/samples/web-app-managed-identity/dotnet/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="$PREFIX-rg" diff --git a/samples/web-app-managed-identity/dotnet/scripts/api.sh b/samples/web-app-managed-identity/dotnet/scripts/api.sh index 71c0deb..c11db53 100755 --- a/samples/web-app-managed-identity/dotnet/scripts/api.sh +++ b/samples/web-app-managed-identity/dotnet/scripts/api.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' NEW_LOCATION='northeurope' RANDOM_SUFFIX=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 4) diff --git a/samples/web-app-managed-identity/dotnet/scripts/call-web-app.sh b/samples/web-app-managed-identity/dotnet/scripts/call-web-app.sh index a82964c..6f4db11 100755 --- a/samples/web-app-managed-identity/dotnet/scripts/call-web-app.sh +++ b/samples/web-app-managed-identity/dotnet/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,10 +70,40 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -79,7 +114,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -125,9 +160,19 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 80) - echo "Getting the host port mapped to internal port 80 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "80") + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Web app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -153,10 +198,10 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip/" 1> /dev/null + curl --max-time 10 -s "http://$container_ip:$container_port/" 1> /dev/null if [ $? == 0 ]; then echo "Web app call via container IP address [$container_ip] succeeded." @@ -196,4 +241,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-managed-identity/dotnet/scripts/cli.sh b/samples/web-app-managed-identity/dotnet/scripts/cli.sh index a72c1c5..af19a3f 100755 --- a/samples/web-app-managed-identity/dotnet/scripts/cli.sh +++ b/samples/web-app-managed-identity/dotnet/scripts/cli.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RANDOM_SUFFIX=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 4) MANAGED_IDENTITY_NAME="${PREFIX}-identity-${SUFFIX}-${RANDOM_SUFFIX}" diff --git a/samples/web-app-managed-identity/dotnet/scripts/system-assigned.sh b/samples/web-app-managed-identity/dotnet/scripts/system-assigned.sh index a5dc656..eec215f 100755 --- a/samples/web-app-managed-identity/dotnet/scripts/system-assigned.sh +++ b/samples/web-app-managed-identity/dotnet/scripts/system-assigned.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" APP_SERVICE_PLAN_NAME="${PREFIX}-app-service-plan-${SUFFIX}" diff --git a/samples/web-app-managed-identity/dotnet/scripts/user-assigned.sh b/samples/web-app-managed-identity/dotnet/scripts/user-assigned.sh index a318226..014aad1 100755 --- a/samples/web-app-managed-identity/dotnet/scripts/user-assigned.sh +++ b/samples/web-app-managed-identity/dotnet/scripts/user-assigned.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" APP_SERVICE_PLAN_NAME="${PREFIX}-app-service-plan-${SUFFIX}" diff --git a/samples/web-app-managed-identity/dotnet/scripts/validate.sh b/samples/web-app-managed-identity/dotnet/scripts/validate.sh index 67198f8..9280330 100755 --- a/samples/web-app-managed-identity/dotnet/scripts/validate.sh +++ b/samples/web-app-managed-identity/dotnet/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" CONTAINER_NAME='activities' diff --git a/samples/web-app-managed-identity/dotnet/terraform/deploy.sh b/samples/web-app-managed-identity/dotnet/terraform/deploy.sh index 25dbf65..a594810 100755 --- a/samples/web-app-managed-identity/dotnet/terraform/deploy.sh +++ b/samples/web-app-managed-identity/dotnet/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' MANAGED_IDENTITY_TYPE='UserAssigned' # SystemAssigned or UserAssigned CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" diff --git a/samples/web-app-managed-identity/python/README.md b/samples/web-app-managed-identity/python/README.md index 7912751..970f8c2 100644 --- a/samples/web-app-managed-identity/python/README.md +++ b/samples/web-app-managed-identity/python/README.md @@ -82,7 +82,7 @@ All deployment methods have been fully tested with both user-assigned and system ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the following user interface for adding and removing activities: diff --git a/samples/web-app-managed-identity/python/bicep/deploy.sh b/samples/web-app-managed-identity/python/bicep/deploy.sh index 149e131..cc9020f 100755 --- a/samples/web-app-managed-identity/python/bicep/deploy.sh +++ b/samples/web-app-managed-identity/python/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="$PREFIX-rg" diff --git a/samples/web-app-managed-identity/python/scripts/api.sh b/samples/web-app-managed-identity/python/scripts/api.sh index 71c0deb..c11db53 100755 --- a/samples/web-app-managed-identity/python/scripts/api.sh +++ b/samples/web-app-managed-identity/python/scripts/api.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' NEW_LOCATION='northeurope' RANDOM_SUFFIX=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 4) diff --git a/samples/web-app-managed-identity/python/scripts/call-web-app.sh b/samples/web-app-managed-identity/python/scripts/call-web-app.sh index a82964c..6f4db11 100755 --- a/samples/web-app-managed-identity/python/scripts/call-web-app.sh +++ b/samples/web-app-managed-identity/python/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,10 +70,40 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -79,7 +114,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -125,9 +160,19 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 80) - echo "Getting the host port mapped to internal port 80 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "80") + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Web app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -153,10 +198,10 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip/" 1> /dev/null + curl --max-time 10 -s "http://$container_ip:$container_port/" 1> /dev/null if [ $? == 0 ]; then echo "Web app call via container IP address [$container_ip] succeeded." @@ -196,4 +241,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-managed-identity/python/scripts/cli.sh b/samples/web-app-managed-identity/python/scripts/cli.sh index a72c1c5..af19a3f 100755 --- a/samples/web-app-managed-identity/python/scripts/cli.sh +++ b/samples/web-app-managed-identity/python/scripts/cli.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RANDOM_SUFFIX=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 4) MANAGED_IDENTITY_NAME="${PREFIX}-identity-${SUFFIX}-${RANDOM_SUFFIX}" diff --git a/samples/web-app-managed-identity/python/scripts/system-assigned.sh b/samples/web-app-managed-identity/python/scripts/system-assigned.sh index 2850aa8..ca2facd 100755 --- a/samples/web-app-managed-identity/python/scripts/system-assigned.sh +++ b/samples/web-app-managed-identity/python/scripts/system-assigned.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" APP_SERVICE_PLAN_NAME="${PREFIX}-app-service-plan-${SUFFIX}" diff --git a/samples/web-app-managed-identity/python/scripts/user-assigned.sh b/samples/web-app-managed-identity/python/scripts/user-assigned.sh index c688671..b50054d 100755 --- a/samples/web-app-managed-identity/python/scripts/user-assigned.sh +++ b/samples/web-app-managed-identity/python/scripts/user-assigned.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" APP_SERVICE_PLAN_NAME="${PREFIX}-app-service-plan-${SUFFIX}" diff --git a/samples/web-app-managed-identity/python/scripts/validate.sh b/samples/web-app-managed-identity/python/scripts/validate.sh index 67198f8..9280330 100755 --- a/samples/web-app-managed-identity/python/scripts/validate.sh +++ b/samples/web-app-managed-identity/python/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" STORAGE_ACCOUNT_NAME="${PREFIX}storage${SUFFIX}" CONTAINER_NAME='activities' diff --git a/samples/web-app-managed-identity/python/terraform/deploy.sh b/samples/web-app-managed-identity/python/terraform/deploy.sh index 4647b3f..9569581 100755 --- a/samples/web-app-managed-identity/python/terraform/deploy.sh +++ b/samples/web-app-managed-identity/python/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' MANAGED_IDENTITY_TYPE='UserAssigned' # SystemAssigned or UserAssigned CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" diff --git a/samples/web-app-mysql-flexible-server/dotnet/README.md b/samples/web-app-mysql-flexible-server/dotnet/README.md index 847b8bb..97f1e5a 100644 --- a/samples/web-app-mysql-flexible-server/dotnet/README.md +++ b/samples/web-app-mysql-flexible-server/dotnet/README.md @@ -64,7 +64,7 @@ All three variants provision the same topology: VNet + pe-subnet hosting a Priva ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the *Vacation Planner* UI with the seeded activities and can add, edit, and remove activities. diff --git a/samples/web-app-mysql-flexible-server/dotnet/bicep/deploy.sh b/samples/web-app-mysql-flexible-server/dotnet/bicep/deploy.sh index 6eacd28..4d27607 100755 --- a/samples/web-app-mysql-flexible-server/dotnet/bicep/deploy.sh +++ b/samples/web-app-mysql-flexible-server/dotnet/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="${PREFIX}-rg" diff --git a/samples/web-app-mysql-flexible-server/dotnet/scripts/call-web-app.sh b/samples/web-app-mysql-flexible-server/dotnet/scripts/call-web-app.sh index b521aed..f98ceac 100755 --- a/samples/web-app-mysql-flexible-server/dotnet/scripts/call-web-app.sh +++ b/samples/web-app-mysql-flexible-server/dotnet/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,13 +70,43 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { # Web app port - local web_app_port=8000 - - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + local web_app_port + + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -82,7 +117,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -128,8 +163,18 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 8000) - echo "Getting the host port mapped to internal port $web_app_port in container [$container_name]..." + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + web_app_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$web_app_port" ]; then + echo "Web app listens on port [$web_app_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$web_app_port] in container [$container_name]..." host_port=$(get_docker_container_port_mapping "$container_name" "$web_app_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then @@ -155,7 +200,7 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$web_app_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." curl --max-time 10 -s "http://$container_ip:$web_app_port/" 1> /dev/null @@ -198,4 +243,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-mysql-flexible-server/dotnet/scripts/deploy.sh b/samples/web-app-mysql-flexible-server/dotnet/scripts/deploy.sh index 74e4921..ade1ba2 100755 --- a/samples/web-app-mysql-flexible-server/dotnet/scripts/deploy.sh +++ b/samples/web-app-mysql-flexible-server/dotnet/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" diff --git a/samples/web-app-mysql-flexible-server/dotnet/scripts/validate.sh b/samples/web-app-mysql-flexible-server/dotnet/scripts/validate.sh index 7538368..b5a0926 100755 --- a/samples/web-app-mysql-flexible-server/dotnet/scripts/validate.sh +++ b/samples/web-app-mysql-flexible-server/dotnet/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" WEB_APP_SUBNET_NSG_NAME="${PREFIX}-webapp-subnet-nsg-${SUFFIX}" diff --git a/samples/web-app-mysql-flexible-server/dotnet/terraform/deploy.sh b/samples/web-app-mysql-flexible-server/dotnet/terraform/deploy.sh index 739e2e2..d07794d 100755 --- a/samples/web-app-mysql-flexible-server/dotnet/terraform/deploy.sh +++ b/samples/web-app-mysql-flexible-server/dotnet/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' MYSQL_ADMIN_USER="${MYSQL_ADMIN_USER:-myadmin}" MYSQL_ADMIN_PASSWORD="${MYSQL_ADMIN_PASSWORD:-P@ssw0rd1234!}" diff --git a/samples/web-app-mysql-flexible-server/python/README.md b/samples/web-app-mysql-flexible-server/python/README.md index 275cf79..4ccb3ac 100644 --- a/samples/web-app-mysql-flexible-server/python/README.md +++ b/samples/web-app-mysql-flexible-server/python/README.md @@ -64,7 +64,7 @@ All three variants provision the same topology: VNet + pe-subnet hosting a Priva ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the *Vacation Planner* UI with the seeded activities and can add, edit, and remove activities. diff --git a/samples/web-app-mysql-flexible-server/python/bicep/deploy.sh b/samples/web-app-mysql-flexible-server/python/bicep/deploy.sh index 0018e7e..9d9c056 100755 --- a/samples/web-app-mysql-flexible-server/python/bicep/deploy.sh +++ b/samples/web-app-mysql-flexible-server/python/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="${PREFIX}-rg" diff --git a/samples/web-app-mysql-flexible-server/python/scripts/call-web-app.sh b/samples/web-app-mysql-flexible-server/python/scripts/call-web-app.sh index b521aed..f98ceac 100755 --- a/samples/web-app-mysql-flexible-server/python/scripts/call-web-app.sh +++ b/samples/web-app-mysql-flexible-server/python/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,13 +70,43 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { # Web app port - local web_app_port=8000 - - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + local web_app_port + + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -82,7 +117,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -128,8 +163,18 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 8000) - echo "Getting the host port mapped to internal port $web_app_port in container [$container_name]..." + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + web_app_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$web_app_port" ]; then + echo "Web app listens on port [$web_app_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$web_app_port] in container [$container_name]..." host_port=$(get_docker_container_port_mapping "$container_name" "$web_app_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then @@ -155,7 +200,7 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$web_app_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." curl --max-time 10 -s "http://$container_ip:$web_app_port/" 1> /dev/null @@ -198,4 +243,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-mysql-flexible-server/python/scripts/deploy.sh b/samples/web-app-mysql-flexible-server/python/scripts/deploy.sh index 3738fd1..9787b81 100755 --- a/samples/web-app-mysql-flexible-server/python/scripts/deploy.sh +++ b/samples/web-app-mysql-flexible-server/python/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" diff --git a/samples/web-app-mysql-flexible-server/python/scripts/validate.sh b/samples/web-app-mysql-flexible-server/python/scripts/validate.sh index 7538368..b5a0926 100755 --- a/samples/web-app-mysql-flexible-server/python/scripts/validate.sh +++ b/samples/web-app-mysql-flexible-server/python/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" WEB_APP_SUBNET_NSG_NAME="${PREFIX}-webapp-subnet-nsg-${SUFFIX}" diff --git a/samples/web-app-mysql-flexible-server/python/terraform/deploy.sh b/samples/web-app-mysql-flexible-server/python/terraform/deploy.sh index b7eea10..d41747e 100755 --- a/samples/web-app-mysql-flexible-server/python/terraform/deploy.sh +++ b/samples/web-app-mysql-flexible-server/python/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' MYSQL_ADMIN_USER="${MYSQL_ADMIN_USER:-myadmin}" MYSQL_ADMIN_PASSWORD="${MYSQL_ADMIN_PASSWORD:-P@ssw0rd1234!}" diff --git a/samples/web-app-postgresql-flexible-server/dotnet/README.md b/samples/web-app-postgresql-flexible-server/dotnet/README.md index dfdb347..0375590 100644 --- a/samples/web-app-postgresql-flexible-server/dotnet/README.md +++ b/samples/web-app-postgresql-flexible-server/dotnet/README.md @@ -64,7 +64,7 @@ All three variants provision the same topology: VNet + pe-subnet hosting a Priva ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the *Vacation Planner* UI with the three seeded activities (*Go to Paris*, *Go to London*, *Go to Mexico*) and can add, edit, and remove activities. diff --git a/samples/web-app-postgresql-flexible-server/dotnet/bicep/deploy.sh b/samples/web-app-postgresql-flexible-server/dotnet/bicep/deploy.sh index e136ff4..6391c13 100755 --- a/samples/web-app-postgresql-flexible-server/dotnet/bicep/deploy.sh +++ b/samples/web-app-postgresql-flexible-server/dotnet/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="${PREFIX}-rg" diff --git a/samples/web-app-postgresql-flexible-server/dotnet/scripts/call-web-app.sh b/samples/web-app-postgresql-flexible-server/dotnet/scripts/call-web-app.sh index b521aed..f98ceac 100755 --- a/samples/web-app-postgresql-flexible-server/dotnet/scripts/call-web-app.sh +++ b/samples/web-app-postgresql-flexible-server/dotnet/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,13 +70,43 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { # Web app port - local web_app_port=8000 - - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + local web_app_port + + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -82,7 +117,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -128,8 +163,18 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 8000) - echo "Getting the host port mapped to internal port $web_app_port in container [$container_name]..." + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + web_app_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$web_app_port" ]; then + echo "Web app listens on port [$web_app_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$web_app_port] in container [$container_name]..." host_port=$(get_docker_container_port_mapping "$container_name" "$web_app_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then @@ -155,7 +200,7 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$web_app_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." curl --max-time 10 -s "http://$container_ip:$web_app_port/" 1> /dev/null @@ -198,4 +243,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-postgresql-flexible-server/dotnet/scripts/deploy.sh b/samples/web-app-postgresql-flexible-server/dotnet/scripts/deploy.sh index 71d5bdc..275044e 100755 --- a/samples/web-app-postgresql-flexible-server/dotnet/scripts/deploy.sh +++ b/samples/web-app-postgresql-flexible-server/dotnet/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" diff --git a/samples/web-app-postgresql-flexible-server/dotnet/scripts/validate.sh b/samples/web-app-postgresql-flexible-server/dotnet/scripts/validate.sh index b76a693..99f8575 100755 --- a/samples/web-app-postgresql-flexible-server/dotnet/scripts/validate.sh +++ b/samples/web-app-postgresql-flexible-server/dotnet/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" WEB_APP_SUBNET_NSG_NAME="${PREFIX}-webapp-subnet-nsg-${SUFFIX}" diff --git a/samples/web-app-postgresql-flexible-server/dotnet/terraform/deploy.sh b/samples/web-app-postgresql-flexible-server/dotnet/terraform/deploy.sh index 448d315..0d1a388 100755 --- a/samples/web-app-postgresql-flexible-server/dotnet/terraform/deploy.sh +++ b/samples/web-app-postgresql-flexible-server/dotnet/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' PG_ADMIN_USER="pgadmin" PG_ADMIN_PASSWORD="P@ssw0rd1234!" diff --git a/samples/web-app-postgresql-flexible-server/python/README.md b/samples/web-app-postgresql-flexible-server/python/README.md index f811fb2..d842caf 100644 --- a/samples/web-app-postgresql-flexible-server/python/README.md +++ b/samples/web-app-postgresql-flexible-server/python/README.md @@ -64,7 +64,7 @@ All three variants provision the same topology: VNet + pe-subnet hosting a Priva ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the *Vacation Planner* UI with the three seeded activities (*Go to Paris*, *Go to London*, *Go to Mexico*) and can add, edit, and remove activities. diff --git a/samples/web-app-postgresql-flexible-server/python/bicep/deploy.sh b/samples/web-app-postgresql-flexible-server/python/bicep/deploy.sh index d4ed0a7..84146aa 100755 --- a/samples/web-app-postgresql-flexible-server/python/bicep/deploy.sh +++ b/samples/web-app-postgresql-flexible-server/python/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="${PREFIX}-rg" diff --git a/samples/web-app-postgresql-flexible-server/python/scripts/call-web-app.sh b/samples/web-app-postgresql-flexible-server/python/scripts/call-web-app.sh index b521aed..f98ceac 100755 --- a/samples/web-app-postgresql-flexible-server/python/scripts/call-web-app.sh +++ b/samples/web-app-postgresql-flexible-server/python/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,13 +70,43 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + call_web_app() { # Web app port - local web_app_port=8000 - - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + local web_app_port + + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -82,7 +117,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -128,8 +163,18 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 8000) - echo "Getting the host port mapped to internal port $web_app_port in container [$container_name]..." + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + web_app_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$web_app_port" ]; then + echo "Web app listens on port [$web_app_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$web_app_port] in container [$container_name]..." host_port=$(get_docker_container_port_mapping "$container_name" "$web_app_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then @@ -155,7 +200,7 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$web_app_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." curl --max-time 10 -s "http://$container_ip:$web_app_port/" 1> /dev/null @@ -198,4 +243,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-postgresql-flexible-server/python/scripts/deploy.sh b/samples/web-app-postgresql-flexible-server/python/scripts/deploy.sh index 000d8b2..01a7511 100755 --- a/samples/web-app-postgresql-flexible-server/python/scripts/deploy.sh +++ b/samples/web-app-postgresql-flexible-server/python/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" diff --git a/samples/web-app-postgresql-flexible-server/python/scripts/validate.sh b/samples/web-app-postgresql-flexible-server/python/scripts/validate.sh index b76a693..99f8575 100755 --- a/samples/web-app-postgresql-flexible-server/python/scripts/validate.sh +++ b/samples/web-app-postgresql-flexible-server/python/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" LOG_ANALYTICS_NAME="${PREFIX}-log-analytics-${SUFFIX}" WEB_APP_SUBNET_NSG_NAME="${PREFIX}-webapp-subnet-nsg-${SUFFIX}" diff --git a/samples/web-app-postgresql-flexible-server/python/terraform/deploy.sh b/samples/web-app-postgresql-flexible-server/python/terraform/deploy.sh index 496637b..161e616 100755 --- a/samples/web-app-postgresql-flexible-server/python/terraform/deploy.sh +++ b/samples/web-app-postgresql-flexible-server/python/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' PG_ADMIN_USER="pgadmin" PG_ADMIN_PASSWORD="P@ssw0rd1234!" diff --git a/samples/web-app-sql-database/dotnet/README.md b/samples/web-app-sql-database/dotnet/README.md index f6ebe9f..419d2f8 100644 --- a/samples/web-app-sql-database/dotnet/README.md +++ b/samples/web-app-sql-database/dotnet/README.md @@ -90,7 +90,7 @@ All deployment methods have been fully tested against Azure and the LocalStack f ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. As an alternative, you can retrieve the URL using the following command and call the Web App using the HTTP or HTTPS protocol: ```bash diff --git a/samples/web-app-sql-database/dotnet/bicep/README.md b/samples/web-app-sql-database/dotnet/bicep/README.md index 98a764c..4e95e22 100644 --- a/samples/web-app-sql-database/dotnet/bicep/README.md +++ b/samples/web-app-sql-database/dotnet/bicep/README.md @@ -43,6 +43,8 @@ The [deploy.sh](deploy.sh) script creates the [Azure Resource Group](https://lea 6. [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/overview): Stores the SQL connection string in a secret and the RSA key that serves as the [TDE protector](https://learn.microsoft.com/en-us/azure/azure-sql/database/transparent-data-encryption-byok-overview) of the SQL server, registered by the [transparent-data-encryption.bicep](modules/transparent-data-encryption.bicep) module. 7. [User-Assigned Managed Identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview): The identity the SQL server uses to reach its TDE protector key in Key Vault. +Bicep cannot create a Key Vault certificate, so after the deployment [deploy.sh](deploy.sh) creates the self-signed certificate the web app serves HTTPS with on port 8443, under the name of its `CERT_NAME` app setting, before it deploys the code. + The web app allows users to plan and manage vacation activities, storing all activity data in the `Activities` table in the `PlannerDB` database. For more information, see [Azure Web App with Azure SQL Database and Azure Key Vault](../README.md). ## Deployment diff --git a/samples/web-app-sql-database/dotnet/bicep/deploy.sh b/samples/web-app-sql-database/dotnet/bicep/deploy.sh index 47ce812..2ae5eac 100755 --- a/samples/web-app-sql-database/dotnet/bicep/deploy.sh +++ b/samples/web-app-sql-database/dotnet/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="$PREFIX-rg" @@ -116,6 +116,8 @@ if DEPLOYMENT_OUTPUTS=$(az deployment group create \ echo "$DEPLOYMENT_JSON" | jq . APP_SERVICE_PLAN_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.appServicePlanName.value') WEB_APP_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.webAppName.value') + KEY_VAULT_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.keyVaultName.value') + CERT_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.certificateName.value') SQL_SERVER_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.sqlServerName.value') SQL_DATABASE_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.sqlDatabaseName.value') echo "Deployment details:" @@ -285,6 +287,29 @@ if [[ $DEPLOY_APP -eq 0 ]]; then exit 0 fi +# Create the certificate the web app serves HTTPS with on port 8443 (its CERT_NAME app setting), before the +# code is deployed and the app starts: Bicep cannot create a Key Vault certificate. +if az keyvault certificate show --vault-name "$KEY_VAULT_NAME" --name "$CERT_NAME" --only-show-errors &>/dev/null; then + echo "Certificate [$CERT_NAME] already exists in Key Vault [$KEY_VAULT_NAME]." +else + echo "Creating certificate [$CERT_NAME] in Key Vault [$KEY_VAULT_NAME]..." + if az keyvault certificate create \ + --vault-name "$KEY_VAULT_NAME" \ + --name "$CERT_NAME" \ + --policy '{ + "issuerParameters": {"name": "Self"}, + "keyProperties": {"exportable": true, "keySize": 2048, "keyType": "RSA", "reuseKey": false}, + "secretProperties": {"contentType": "application/x-pkcs12"}, + "x509CertificateProperties": {"subject": "CN=sample-web-app-sql", "validityInMonths": 12} + }' \ + --only-show-errors 1>/dev/null; then + echo "Certificate [$CERT_NAME] created successfully in Key Vault [$KEY_VAULT_NAME]." + else + echo "Failed to create certificate [$CERT_NAME] in Key Vault [$KEY_VAULT_NAME]." + exit 1 + fi +fi + # Change current directory to source folder cd "../src" || exit diff --git a/samples/web-app-sql-database/dotnet/bicep/main.bicep b/samples/web-app-sql-database/dotnet/bicep/main.bicep index 31075ed..3cd236b 100644 --- a/samples/web-app-sql-database/dotnet/bicep/main.bicep +++ b/samples/web-app-sql-database/dotnet/bicep/main.bicep @@ -321,6 +321,9 @@ var sqlServerName = '${prefix}-sqlserver-${suffix}' var webAppName = '${prefix}-webapp-${suffix}' var appServicePlanName = '${prefix}-app-service-plan-${suffix}' var keyVaultName = '${prefix}-kv-${suffix}' +// The TLS certificate the web app serves HTTPS with on port 8443. Bicep cannot create a Key Vault +// certificate, so deploy.sh creates it under this name after the deployment. +var certificateName = '${prefix}-cert-${suffix}' var sqlConnectionStringSecretName = '${prefix}-secret-${suffix}' var sqlServerIdentityName = '${prefix}-tde-identity-${suffix}' var tdeKeyName = '${prefix}-tde-key-${suffix}' @@ -458,6 +461,9 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { 'get' 'list' ] + certificates: [ + 'get' + ] } } { @@ -522,6 +528,7 @@ resource configAppSettings 'Microsoft.Web/sites/config@2024-11-01' = { SECRET_NAME: sqlConnectionStringSecretName LOGIN_NAME: username KEYVAULT_URI: keyVault.properties.vaultUri + CERT_NAME: certificateName } } @@ -543,4 +550,5 @@ output sqlServerFqdn string = sqlServer.properties.fullyQualifiedDomainName output sqlDatabaseName string = sqlDatabase.name output keyVaultName string = keyVault.name output keyVaultUrl string = keyVault.properties.vaultUri +output certificateName string = certificateName output sqlConnectionStringSecretUri string = sqlConnectionStringSecret.properties.secretUri diff --git a/samples/web-app-sql-database/dotnet/scripts/call-web-app.sh b/samples/web-app-sql-database/dotnet/scripts/call-web-app.sh index e7694df..b48418c 100755 --- a/samples/web-app-sql-database/dotnet/scripts/call-web-app.sh +++ b/samples/web-app-sql-database/dotnet/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,6 +70,28 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + # Distinguished names are compared after normalization: OpenSSL 3 prints "CN = value", Key Vault # returns "CN=value" and OpenSSL 1 printed "/CN=value" - the same subject in three spellings, which # a literal comparison reports as a mismatch. @@ -74,9 +101,17 @@ normalize_dn() { } call_web_app() { - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -87,7 +122,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -133,9 +168,19 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 80) - echo "Getting the host port mapped to internal port 80 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "80") + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Web app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -161,10 +206,10 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip/" 1> /dev/null + curl --max-time 10 -s "http://$container_ip:$container_port/" 1> /dev/null if [ $? == 0 ]; then echo "Web app call via container IP address [$container_ip] succeeded." @@ -248,4 +293,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-sql-database/dotnet/scripts/deploy.sh b/samples/web-app-sql-database/dotnet/scripts/deploy.sh index 48ffaa8..4e3d440 100755 --- a/samples/web-app-sql-database/dotnet/scripts/deploy.sh +++ b/samples/web-app-sql-database/dotnet/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" SQL_SERVER_NAME="${PREFIX}-sqlserver-${SUFFIX}" diff --git a/samples/web-app-sql-database/dotnet/scripts/validate.sh b/samples/web-app-sql-database/dotnet/scripts/validate.sh index c6ca9bf..50cd688 100755 --- a/samples/web-app-sql-database/dotnet/scripts/validate.sh +++ b/samples/web-app-sql-database/dotnet/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" SQL_SERVER_NAME="${PREFIX}-sqlserver-${SUFFIX}" SQL_DATABASE_NAME='PlannerDB' diff --git a/samples/web-app-sql-database/dotnet/terraform/deploy.sh b/samples/web-app-sql-database/dotnet/terraform/deploy.sh index 2e1ab40..4231870 100755 --- a/samples/web-app-sql-database/dotnet/terraform/deploy.sh +++ b/samples/web-app-sql-database/dotnet/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' ADMIN_USER='sqladmin' ADMIN_PASSWORD='P@ssw0rd1234!' diff --git a/samples/web-app-sql-database/python/README.md b/samples/web-app-sql-database/python/README.md index 1fe94e3..a57c52f 100644 --- a/samples/web-app-sql-database/python/README.md +++ b/samples/web-app-sql-database/python/README.md @@ -90,7 +90,7 @@ All deployment methods have been fully tested against Azure and the LocalStack f ## Test -1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App. +1. Retrieve the host port Docker maps to the port the Web App listens on in its container, for example with `docker port ` (the container's name starts with `ls-`). Do not assume port 80: the app listens on the port its `WEBSITES_PORT` app setting names, when it has one. 2. Open a web browser and navigate to `http://localhost:`. 3. If the deployment was successful, you will see the following user interface for adding and removing activities: diff --git a/samples/web-app-sql-database/python/bicep/README.md b/samples/web-app-sql-database/python/bicep/README.md index fd3ca0c..bc634fc 100644 --- a/samples/web-app-sql-database/python/bicep/README.md +++ b/samples/web-app-sql-database/python/bicep/README.md @@ -43,6 +43,8 @@ The [deploy.sh](deploy.sh) script creates the [Azure Resource Group](https://lea 6. [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/overview): Stores the SQL connection string in a secret and the RSA key that serves as the [TDE protector](https://learn.microsoft.com/en-us/azure/azure-sql/database/transparent-data-encryption-byok-overview) of the SQL server, registered by the [transparent-data-encryption.bicep](modules/transparent-data-encryption.bicep) module. 7. [User-Assigned Managed Identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview): The identity the SQL server uses to reach its TDE protector key in Key Vault. +Bicep cannot create a Key Vault certificate, so after the deployment [deploy.sh](deploy.sh) creates the self-signed certificate the web app serves HTTPS with on port 8443, under the name of its `CERT_NAME` app setting, before it deploys the code. + The web app allows users to plan and manage vacation activities, storing all activity data in the `Activities` table in the `PlannerDB` database. For more information, see [Azure Web App with Azure SQL Database and Azure Key Vault](../README.md). ## Deployment diff --git a/samples/web-app-sql-database/python/bicep/deploy.sh b/samples/web-app-sql-database/python/bicep/deploy.sh index efbe9a6..f3b6de1 100755 --- a/samples/web-app-sql-database/python/bicep/deploy.sh +++ b/samples/web-app-sql-database/python/bicep/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" RESOURCE_GROUP_NAME="$PREFIX-rg" @@ -116,6 +116,8 @@ if DEPLOYMENT_OUTPUTS=$(az deployment group create \ echo "$DEPLOYMENT_JSON" | jq . APP_SERVICE_PLAN_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.appServicePlanName.value') WEB_APP_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.webAppName.value') + KEY_VAULT_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.keyVaultName.value') + CERT_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.certificateName.value') SQL_SERVER_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.sqlServerName.value') SQL_DATABASE_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.sqlDatabaseName.value') echo "Deployment details:" @@ -285,6 +287,29 @@ if [[ $DEPLOY_APP -eq 0 ]]; then exit 0 fi +# Create the certificate the web app serves HTTPS with on port 8443 (its CERT_NAME app setting), before the +# code is deployed and the app starts: Bicep cannot create a Key Vault certificate. +if az keyvault certificate show --vault-name "$KEY_VAULT_NAME" --name "$CERT_NAME" --only-show-errors &>/dev/null; then + echo "Certificate [$CERT_NAME] already exists in Key Vault [$KEY_VAULT_NAME]." +else + echo "Creating certificate [$CERT_NAME] in Key Vault [$KEY_VAULT_NAME]..." + if az keyvault certificate create \ + --vault-name "$KEY_VAULT_NAME" \ + --name "$CERT_NAME" \ + --policy '{ + "issuerParameters": {"name": "Self"}, + "keyProperties": {"exportable": true, "keySize": 2048, "keyType": "RSA", "reuseKey": false}, + "secretProperties": {"contentType": "application/x-pkcs12"}, + "x509CertificateProperties": {"subject": "CN=sample-web-app-sql", "validityInMonths": 12} + }' \ + --only-show-errors 1>/dev/null; then + echo "Certificate [$CERT_NAME] created successfully in Key Vault [$KEY_VAULT_NAME]." + else + echo "Failed to create certificate [$CERT_NAME] in Key Vault [$KEY_VAULT_NAME]." + exit 1 + fi +fi + # Change current directory to source folder cd "../src" || exit diff --git a/samples/web-app-sql-database/python/bicep/main.bicep b/samples/web-app-sql-database/python/bicep/main.bicep index f077ace..b12e0c8 100644 --- a/samples/web-app-sql-database/python/bicep/main.bicep +++ b/samples/web-app-sql-database/python/bicep/main.bicep @@ -320,6 +320,9 @@ var sqlServerName = '${prefix}-sqlserver-${suffix}' var webAppName = '${prefix}-webapp-${suffix}' var appServicePlanName = '${prefix}-app-service-plan-${suffix}' var keyVaultName = '${prefix}-kv-${suffix}' +// The TLS certificate the web app serves HTTPS with on port 8443. Bicep cannot create a Key Vault +// certificate, so deploy.sh creates it under this name after the deployment. +var certificateName = '${prefix}-cert-${suffix}' var sqlConnectionStringSecretName = '${prefix}-secret-${suffix}' var sqlServerIdentityName = '${prefix}-tde-identity-${suffix}' var tdeKeyName = '${prefix}-tde-key-${suffix}' @@ -457,6 +460,9 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { 'get' 'list' ] + certificates: [ + 'get' + ] } } { @@ -521,6 +527,7 @@ resource configAppSettings 'Microsoft.Web/sites/config@2024-11-01' = { SECRET_NAME: sqlConnectionStringSecretName LOGIN_NAME: username KEYVAULT_URI: keyVault.properties.vaultUri + CERT_NAME: certificateName } } @@ -542,4 +549,5 @@ output sqlServerFqdn string = sqlServer.properties.fullyQualifiedDomainName output sqlDatabaseName string = sqlDatabase.name output keyVaultName string = keyVault.name output keyVaultUrl string = keyVault.properties.vaultUri +output certificateName string = certificateName output sqlConnectionStringSecretUri string = sqlConnectionStringSecret.properties.secretUri diff --git a/samples/web-app-sql-database/python/scripts/call-web-app.sh b/samples/web-app-sql-database/python/scripts/call-web-app.sh index 272c0f9..74bf6ff 100755 --- a/samples/web-app-sql-database/python/scripts/call-web-app.sh +++ b/samples/web-app-sql-database/python/scripts/call-web-app.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Usage: call-web-app.sh [web-app-name] +# +# The web app to call is the one named by the first argument, else by WEB_APP_NAME, else the only web app in the +# subscription. Name it when several are deployed. + get_docker_container_name_by_prefix() { local app_prefix="$1" local container_name @@ -65,6 +70,28 @@ get_docker_container_port_mapping() { echo "$host_port" } +get_docker_container_app_port() { + local container_name="$1" + local container_port + + if [ -z "$container_name" ]; then + echo "Error: Container name is required" >&2 + return 1 + fi + + # The emulator publishes the port the app listens on in its container, which is not necessarily 80: + # an app listens on the port its WEBSITES_PORT app setting names. Read it from the container. + container_port=$(docker inspect -f '{{range $port, $bindings := .NetworkSettings.Ports}}{{if $bindings}}{{println $port}}{{end}}{{end}}' "$container_name" | grep '/tcp$' | head -n 1) + container_port="${container_port%/tcp}" + + if [ -z "$container_port" ]; then + echo "Error: Container [$container_name] publishes no TCP port" >&2 + return 1 + fi + + echo "$container_port" +} + # Distinguished names are compared after normalization: OpenSSL 3 prints "CN = value", Key Vault # returns "CN=value" and OpenSSL 1 printed "/CN=value" - the same subject in three spellings, which # a literal comparison reports as a mismatch. @@ -74,9 +101,17 @@ normalize_dn() { } call_web_app() { - # Get the web app name - echo "Getting web app name..." - web_app_name=$(az webapp list --query '[0].name' --output tsv) + # The web app to call: the one the caller named, else the only one in the subscription + web_app_name="$1" + if [ -z "$web_app_name" ]; then + echo "Getting the name of the only web app in the subscription..." + web_app_count=$(az webapp list --query "length(@)" --output tsv) + if [ "$web_app_count" != "1" ]; then + echo "Error: Found [${web_app_count:-0}] web apps; name the one to call: $0 " + exit 1 + fi + web_app_name=$(az webapp list --query "[0].name" --output tsv) + fi if [ -n "$web_app_name" ]; then echo "Web app [$web_app_name] successfully retrieved." @@ -87,7 +122,7 @@ call_web_app() { # Get the resource group name echo "Getting resource group name for web app [$web_app_name]..." - resource_group_name=$(az webapp list --query '[0].resourceGroup' --output tsv) + resource_group_name=$(az webapp list --query "[?name=='${web_app_name}'] | [0].resourceGroup" --output tsv) if [ -n "$resource_group_name" ]; then echo "Resource group [$resource_group_name] successfully retrieved." @@ -133,9 +168,19 @@ call_web_app() { exit 1 fi - # Get the mapped host port for web app HTTP trigger (internal port 80) - echo "Getting the host port mapped to internal port 80 in container [$container_name]..." - host_port=$(get_docker_container_port_mapping "$container_name" "80") + # Get the port the web app listens on in its container + echo "Getting the port the web app listens on in container [$container_name]..." + container_port=$(get_docker_container_app_port "$container_name") + + if [ $? -eq 0 ] && [ -n "$container_port" ]; then + echo "Web app listens on port [$container_port] in container [$container_name]" + else + echo "Failed to get the port the web app listens on in container [$container_name]" + fi + + # Get the host port mapped to it + echo "Getting the host port mapped to port [$container_port] in container [$container_name]..." + host_port=$(get_docker_container_port_mapping "$container_name" "$container_port") if [ $? -eq 0 ] && [ -n "$host_port" ]; then echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]" @@ -161,10 +206,10 @@ call_web_app() { echo "Failed to retrieve LocalStack proxy port" fi - if [ -n "$container_ip" ]; then + if [ -n "$container_ip" ] && [ -n "$container_port" ]; then # Call the web app via the container IP address echo "Calling web app [$web_app_name] via container IP address [$container_ip]..." - curl --max-time 10 -s "http://$container_ip/" 1> /dev/null + curl --max-time 10 -s "http://$container_ip:$container_port/" 1> /dev/null if [ $? == 0 ]; then echo "Web app call via container IP address [$container_ip] succeeded." @@ -251,4 +296,4 @@ call_web_app() { fi } -call_web_app \ No newline at end of file +call_web_app "${1:-${WEB_APP_NAME:-}}" diff --git a/samples/web-app-sql-database/python/scripts/deploy.sh b/samples/web-app-sql-database/python/scripts/deploy.sh index 47d00b7..35e080b 100755 --- a/samples/web-app-sql-database/python/scripts/deploy.sh +++ b/samples/web-app-sql-database/python/scripts/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' RESOURCE_GROUP_NAME="${PREFIX}-rg" SQL_SERVER_NAME="${PREFIX}-sqlserver-${SUFFIX}" diff --git a/samples/web-app-sql-database/python/scripts/validate.sh b/samples/web-app-sql-database/python/scripts/validate.sh index c6ca9bf..50cd688 100755 --- a/samples/web-app-sql-database/python/scripts/validate.sh +++ b/samples/web-app-sql-database/python/scripts/validate.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" RESOURCE_GROUP_NAME="${PREFIX}-rg" SQL_SERVER_NAME="${PREFIX}-sqlserver-${SUFFIX}" SQL_DATABASE_NAME='PlannerDB' diff --git a/samples/web-app-sql-database/python/src/gunicorn.conf.py b/samples/web-app-sql-database/python/src/gunicorn.conf.py index cb87ebc..b37a2e4 100644 --- a/samples/web-app-sql-database/python/src/gunicorn.conf.py +++ b/samples/web-app-sql-database/python/src/gunicorn.conf.py @@ -1,4 +1,5 @@ import os +import threading def worker_int(worker): @@ -16,3 +17,27 @@ def worker_abort(worker): # traceback. The WORKER TIMEOUT log line above it is the real diagnostic; # exit at the C level to suppress the spurious trace. os._exit(1) + + +def when_ready(server): + # App Service starts the app with gunicorn, so the `if __name__ == '__main__'` branch of app.py, which serves + # HTTPS on port 8443 with the Key Vault certificate, never runs there. Start that listener here, once, in the + # gunicorn master, next to the HTTP port App Service proxies to. + vault_uri = os.environ.get("KEYVAULT_URI") + cert_name = os.environ.get("CERT_NAME") + if not (vault_uri and cert_name): + return + try: + from werkzeug.serving import make_server + + from app import app + from certificates import get_ssl_context_from_keyvault + + https_server = make_server( + "0.0.0.0", 8443, app, threaded=True, ssl_context=get_ssl_context_from_keyvault(vault_uri, cert_name) + ) + except Exception: + server.log.exception("HTTPS on port 8443 is disabled: certificate [%s] could not be loaded", cert_name) + return + threading.Thread(target=https_server.serve_forever, name="https-8443", daemon=True).start() + server.log.info("HTTPS enabled on port 8443 with Key Vault certificate [%s]", cert_name) diff --git a/samples/web-app-sql-database/python/terraform/deploy.sh b/samples/web-app-sql-database/python/terraform/deploy.sh index c64b9e1..1314271 100755 --- a/samples/web-app-sql-database/python/terraform/deploy.sh +++ b/samples/web-app-sql-database/python/terraform/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash # Variables -PREFIX='local' -SUFFIX='test' +PREFIX="${PREFIX:-local}" +SUFFIX="${SUFFIX:-test}" LOCATION='westeurope' ADMIN_USER='sqladmin' ADMIN_PASSWORD='P@ssw0rd1234!'