diff --git a/deploy/docker/fs/opt/appsmith/pg-utils.sh b/deploy/docker/fs/opt/appsmith/pg-utils.sh new file mode 100755 index 000000000000..315446f552d7 --- /dev/null +++ b/deploy/docker/fs/opt/appsmith/pg-utils.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +waitForPostgresAvailability() { + if [ -z "$PG_DB_HOST" ]; then + tlog "PostgreSQL host name is empty. Check env variables. Error. Exiting java setup" + exit 2 + else + + MAX_RETRIES=50 + RETRYSECONDS=10 + retry_count=0 + while true; do + su postgres -c "pg_isready -h '${PG_DB_HOST}' -p '${PG_DB_PORT}'" + status=$? + + case $status in + 0) + tlog "PostgreSQL host '$PG_DB_HOST' is ready." + break + ;; + 1) + tlog "PostgreSQL host '$PG_DB_HOST' is rejecting connections e.g. due to being in recovery mode or not accepting connections eg. connections maxed out." + ;; + 2) + tlog "PostgreSQL host '$PG_DB_HOST' is not responding or running." + ;; + 3) + tlog "The connection check failed e.g. due to network issues or incorrect parameters." + ;; + *) + tlog "pg_isready exited with unexpected status code: $status" + break + ;; + esac + + retry_count=$((retry_count + 1)) + if [ $retry_count -le $MAX_RETRIES ]; then + tlog "PostgreSQL connection failed. Retrying attempt $retry_count/$MAX_RETRIES in $RETRYSECONDS seconds..." + sleep $RETRYSECONDS + else + tlog "Exceeded maximum retry attempts ($MAX_RETRIES). Exiting." + # use exit code 2 to indicate that the script failed to connect to postgres and supervisor conf is set not to restart the program for 2. + exit 2 + fi + + done + fi +} + +# for PostgreSQL, we use APPSMITH_DB_URL=postgresql://username:password@postgresserver:5432/dbname +# Args: +# conn_string (string): PostgreSQL connection string +# Returns: +# None +# Example: +# postgres syntax +# "postgresql://user:password@localhost:5432/appsmith" +# "postgresql://user:password@localhost/appsmith" +# "postgresql://user@localhost:5432/appsmith" +# "postgresql://user@localhost/appsmith" +extract_postgres_db_params() { + local conn_string=$1 + + # Use node to parse the URI and extract components + IFS=' ' read -r USER PASSWORD HOST PORT DB <<<"$(node -e " + const connectionString = process.argv[1]; + const pgUri = connectionString.startsWith(\"postgresql://\") + ? connectionString + : 'http://' + connectionString; //Prepend a fake scheme for URL parsing + const url = require('url'); + const parsedUrl = new url.URL(pgUri); + + // Extract the pathname and remove the leading '/' + const db = parsedUrl.pathname.substring(1); + + // Default the port to 5432 if it's empty + const port = parsedUrl.port || '5432'; + + console.log(\`\${parsedUrl.username || '-'} \${parsedUrl.password || '-'} \${parsedUrl.hostname} \${port} \${db}\`); + " "$conn_string")" + + # Now, set the environment variables + export PG_DB_USER="$USER" + export PG_DB_PASSWORD="$PASSWORD" + export PG_DB_HOST="$HOST" + export PG_DB_PORT="$PORT" + export PG_DB_NAME="$DB" +} + +# Example usage of the functions +# waitForPostgresAvailability +# extract_postgres_db_params "postgresql://user:password@localhost:5432/dbname" \ No newline at end of file diff --git a/deploy/docker/fs/opt/appsmith/run-java.sh b/deploy/docker/fs/opt/appsmith/run-java.sh index 675c8e26511e..ed88e26e1191 100755 --- a/deploy/docker/fs/opt/appsmith/run-java.sh +++ b/deploy/docker/fs/opt/appsmith/run-java.sh @@ -1,5 +1,8 @@ #!/bin/bash +# Source the helper script +source pg-utils.sh + set -o errexit set -o pipefail set -o nounset @@ -29,6 +32,12 @@ match-proxy-url() { [[ -n $proxy_host ]] } +# Extract the database parameters from the APPSMITH_DB_URL and wait for the database to be available +if [[ "$mode" == "pg" ]]; then + extract_postgres_db_params "$APPSMITH_DB_URL" + waitForPostgresAvailability +fi + if match-proxy-url "${HTTP_PROXY-}"; then extra_args+=(-Dhttp.proxyHost="$proxy_host" -Dhttp.proxyPort="$proxy_port") if [[ -n $proxy_user ]]; then diff --git a/deploy/docker/tests/test-pg-utils.sh b/deploy/docker/tests/test-pg-utils.sh new file mode 100755 index 000000000000..56bec144560b --- /dev/null +++ b/deploy/docker/tests/test-pg-utils.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +set -e + +# Include the script to be tested +source /Users/appsmith/Work/appsmith-ce/deploy/docker/fs/opt/appsmith/pg-utils.sh + +assert_equals() { + if [ "$1" != "$2" ]; then + echo "Assertion failed: expected '$2', but got '$1'" + return 1 + fi +} + +# Test extract_postgres_db_params function +test_extract_postgres_db_params_valid_db_string() { + local conn_string="postgresql://user:password@localhost:5432/dbname" + extract_postgres_db_params "$conn_string" + + if [ "$PG_DB_USER" != "user" ] || [ "$PG_DB_PASSWORD" != "password" ] || [ "$PG_DB_HOST" != "localhost" ] || [ "$PG_DB_PORT" != "5432" ] || [ "$PG_DB_NAME" != "dbname" ]; then + echo "Test failed: test_extract_postgres_db_params_valid_db_string did not extract parameters correctly" + echo_params + exit 1 + fi + + echo "Test passed: test_extract_postgres_db_params_valid_db_string" +} + +test_extract_postgres_db_params_empty_dbname() { + local conn_string="postgresql://user:password@localhost:5432" + extract_postgres_db_params "$conn_string" + + if [ "$PG_DB_USER" != "user" ] || [ "$PG_DB_PASSWORD" != "password" ] || [ "$PG_DB_HOST" != "localhost" ] || [ "$PG_DB_PORT" != "5432" ] || [ "$PG_DB_NAME" != "" ]; then + echo "Test failed: test_extract_postgres_db_params_empty_dbname did not extract parameters correctly" + echo_params + exit 1 + fi + + echo "Test passed: test_extract_postgres_db_params_empty_dbname" +} + +test_extract_postgres_db_params_with_spaces() { + local conn_string="postgresql://user:p a s s w o r d@localhost:5432/db_name" + extract_postgres_db_params "$conn_string" + + if [ "$PG_DB_USER" != "user" ] || [ "$PG_DB_PASSWORD" != "p%20a%20s%20s%20w%20o%20r%20d" ] || [ "$PG_DB_HOST" != "localhost" ] || [ "$PG_DB_PORT" != "5432" ] || [ "$PG_DB_NAME" != "db_name" ]; then + echo "Test failed: test_extract_postgres_db_params_with_spaces did not extract parameters correctly" + echo_params + exit 1 + fi + + echo "Test passed: test_extract_postgres_db_params_with_spaces" +} + +echo_params() { + echo "PG_DB_USER: $PG_DB_USER" + echo "PG_DB_PASSWORD: $PG_DB_PASSWORD" + echo "PG_DB_HOST: $PG_DB_HOST" + echo "PG_DB_PORT: $PG_DB_PORT" + echo "PG_DB_NAME: $PG_DB_NAME" +} + +# Run tests +test_extract_postgres_db_params_valid_db_string +test_extract_postgres_db_params_empty_dbname +test_extract_postgres_db_params_with_spaces + +echo "All Tests Pass!" \ No newline at end of file