mirror of
https://github.com/open-metadata/OpenMetadata
synced 2026-05-24 09:39:11 +00:00
* RDF, cleanup relations and remove unnecessary bindings, add distributed mode for RDF reindex * Update generated TypeScript types * Address comments from copilot * Update generated TypeScript types * fix test issues * Fix minor UI bugs * Add the missing filters * Fix RDF export API error * Add export functionality * Fix ui-checkstyle * Fix java checkstyle * Fix unit tests * Fix and increase the coverage for KnowledgeGraph.spec.ts * Fix tests * Remove rdf as default in playwright and local docker * fix ui-checkstyle * Address comments * Potential fix for pull request finding 'CodeQL / Artifact poisoning' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Address copilot comments * Address copilot comments * FIx tests * FIx docker * Update openmetadata-service/src/main/java/org/openmetadata/service/apps/bundles/rdf/distributed/DistributedRdfIndexCoordinator.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address copilot review comments: license headers, JSON escaping, type safety, border-color, stop semantics Agent-Logs-Url: https://github.com/open-metadata/OpenMetadata/sessions/c026e52e-162b-4c9a-9874-43791d4aaac1 Co-authored-by: harshach <38649+harshach@users.noreply.github.com> * Show error toast for unsupported export format in KnowledgeGraph Agent-Logs-Url: https://github.com/open-metadata/OpenMetadata/sessions/c026e52e-162b-4c9a-9874-43791d4aaac1 Co-authored-by: harshach <38649+harshach@users.noreply.github.com> * Fix docker * Fix docker for playwright * Fix docker for playwright * Fix tests * Fix tests * Fix docker * Fix docker * Fix glossary and pagination spec flakiness * update the missing translations * Fix docker * Fix docker * Fix integration test * Fix fuseki not starting * Fixed the run local docker script * worked on comments * Fix flakiness in knowledge graph tests * Fix checkstyle --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Aniket Katkar <aniketkatkar97@gmail.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: harshach <38649+harshach@users.noreply.github.com>
614 lines
22 KiB
Bash
Executable file
614 lines
22 KiB
Bash
Executable file
#!/bin/bash
|
|
# Copyright 2021 Collate
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
RED='\033[0;31m'
|
|
RUN_LOCAL_DOCKER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
authorizationToken="eyJraWQiOiJHYjM4OWEtOWY3Ni1nZGpzLWE5MmotMDI0MmJrOTQzNTYiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImlzQm90IjpmYWxzZSwiaXNzIjoib3Blbi1tZXRhZGF0YS5vcmciLCJpYXQiOjE2NjM5Mzg0NjIsImVtYWlsIjoiYWRtaW5Ab3Blbm1ldGFkYXRhLm9yZyJ9.tS8um_5DKu7HgzGBzS1VTA5uUjKWOCU0B_j08WXBiEC0mr0zNREkqVfwFDD-d24HlNEbrqioLsBuFRiwIWKc1m_ZlVQbG7P36RUxhuv2vbSp80FKyNM-Tj93FDzq91jsyNmsQhyNv_fNr3TXfzzSPjHt8Go0FMMP66weoKMgW2PbXlhVKwEuXUHyakLLzewm9UMeQaEiRzhiTMU3UkLXcKbYEJJvfNFcLwSl9W8JCO_l0Yj3ud-qt_nQYEZwqW6u5nfdQllN133iikV4fM5QZsMCnm8Rq1mvLR0y9bmJiD7fwM1tmJ791TUWqmKaTnP49U493VanKpUAfzIiOiIbhg"
|
|
|
|
helpFunction() {
|
|
echo ""
|
|
echo "Usage: $0 -m mode -d database"
|
|
echo "\t-m Running mode: [ui, no-ui]. Default [ui]\n"
|
|
echo "\t-d Database: [mysql, postgresql]. Default [mysql]\n"
|
|
echo "\t-s Skip maven build: [true, false]. Default [false]\n"
|
|
echo "\t-i Include ingestion: [true, false]. Default [true]\n"
|
|
echo "\t-x Open JVM debug port on 5005: [true, false]. Default [false]\n"
|
|
echo "\t-h For usage help\n"
|
|
echo "\t-r For Cleaning DB Volumes. [true, false]. Default [true]\n"
|
|
exit 1
|
|
}
|
|
|
|
current_time_ms() {
|
|
python3 -c "import time; print(int(time.time() * 1000))"
|
|
}
|
|
|
|
run_with_timeout() {
|
|
local secs=$1
|
|
shift
|
|
if command -v timeout &>/dev/null; then
|
|
timeout "$secs" "$@"
|
|
elif command -v gtimeout &>/dev/null; then
|
|
gtimeout "$secs" "$@"
|
|
else
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
parse_app_run_status_line() {
|
|
local payload=$1
|
|
local payload_shape=$2
|
|
local threshold_ms=$3
|
|
local tolerance_ms=$4
|
|
|
|
APP_RUN_BODY="$payload" python3 - "$payload_shape" "$threshold_ms" "$tolerance_ms" <<'PY'
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
payload_shape = sys.argv[1]
|
|
threshold = int(sys.argv[2])
|
|
tolerance = int(sys.argv[3])
|
|
|
|
try:
|
|
payload = json.loads(os.environ["APP_RUN_BODY"])
|
|
except json.JSONDecodeError:
|
|
print("invalid")
|
|
sys.exit(0)
|
|
|
|
if payload_shape == "latest":
|
|
record = payload if isinstance(payload, dict) else None
|
|
else:
|
|
records = payload.get("data") if isinstance(payload, dict) else None
|
|
record = records[0] if records else None
|
|
|
|
if not isinstance(record, dict) or not record:
|
|
print("missing")
|
|
sys.exit(0)
|
|
|
|
timestamp = int(record.get("timestamp") or 0)
|
|
start_time = int(record.get("startTime") or 0)
|
|
status = str(record.get("status") or "").lower()
|
|
execution_time = record.get("executionTime")
|
|
|
|
marker = start_time if start_time > 0 else timestamp
|
|
success_statuses = {"completed", "success"}
|
|
failure_statuses = {"activeerror", "failed", "stopped"}
|
|
active_statuses = {"running", "started", "pending", "active", "stopinprogress"}
|
|
has_execution_time = execution_time not in (None, "", 0, "0")
|
|
|
|
if marker + tolerance < threshold:
|
|
print(f"stale:{status}:{marker}")
|
|
elif status in success_statuses:
|
|
print(f"success:{status}:{marker}")
|
|
elif status in failure_statuses or (has_execution_time and status not in active_statuses):
|
|
print(f"failure:{status}:{marker}")
|
|
elif status in active_statuses:
|
|
print(f"active:{status}:{marker}")
|
|
else:
|
|
print(f"seen:{status}:{marker}")
|
|
PY
|
|
}
|
|
|
|
wait_for_app_availability() {
|
|
local app_name=$1
|
|
local timeout_seconds=${2:-120}
|
|
local deadline=$((SECONDS + timeout_seconds))
|
|
|
|
while [ $SECONDS -lt $deadline ]; do
|
|
local http_code
|
|
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
--header "Authorization: Bearer $authorizationToken" \
|
|
"http://localhost:8585/api/v1/apps/name/${app_name}")
|
|
|
|
if [ "$http_code" = "200" ]; then
|
|
echo "✓ App ${app_name} is available"
|
|
return 0
|
|
fi
|
|
|
|
echo "Waiting for app ${app_name} to become available..."
|
|
sleep 5
|
|
done
|
|
|
|
echo "✗ App ${app_name} did not become available within ${timeout_seconds}s"
|
|
return 1
|
|
}
|
|
|
|
ensure_app_installed() {
|
|
local app_name=$1
|
|
local response
|
|
local http_code
|
|
local body
|
|
|
|
response=$(curl -s -w "\n%{http_code}" \
|
|
--header "Authorization: Bearer $authorizationToken" \
|
|
"http://localhost:8585/api/v1/apps/name/${app_name}")
|
|
http_code=$(echo "$response" | tail -n1)
|
|
body=$(echo "$response" | sed '$d')
|
|
|
|
if [ "$http_code" = "200" ]; then
|
|
echo "✓ App ${app_name} is already installed"
|
|
return 0
|
|
fi
|
|
|
|
if [ "$http_code" != "404" ]; then
|
|
echo "✗ Failed to inspect app ${app_name} (HTTP ${http_code})"
|
|
echo " Response: ${body}"
|
|
return 1
|
|
fi
|
|
|
|
echo "App ${app_name} is not installed. Installing it from the marketplace definition..."
|
|
local marketplace_response
|
|
marketplace_response=$(curl -s -f \
|
|
--header "Authorization: Bearer $authorizationToken" \
|
|
"http://localhost:8585/api/v1/apps/marketplace/name/${app_name}") || {
|
|
echo "✗ Could not fetch marketplace definition for ${app_name}"
|
|
return 1
|
|
}
|
|
|
|
local create_payload
|
|
create_payload=$(MARKETPLACE_RESPONSE="$marketplace_response" python3 - <<'PY'
|
|
import json
|
|
import os
|
|
|
|
definition = json.loads(os.environ["MARKETPLACE_RESPONSE"])
|
|
payload = {
|
|
"name": definition["name"],
|
|
"displayName": definition.get("displayName"),
|
|
"description": definition.get("description"),
|
|
"appConfiguration": definition.get("appConfiguration", {}),
|
|
"appSchedule": {"scheduleTimeline": "None"},
|
|
"supportsInterrupt": definition.get("supportsInterrupt", False),
|
|
}
|
|
print(json.dumps(payload))
|
|
PY
|
|
)
|
|
|
|
response=$(curl -s -w "\n%{http_code}" --location --request POST 'http://localhost:8585/api/v1/apps' \
|
|
--header "Authorization: Bearer $authorizationToken" \
|
|
--header 'Content-Type: application/json' \
|
|
--data-raw "$create_payload")
|
|
http_code=$(echo "$response" | tail -n1)
|
|
body=$(echo "$response" | sed '$d')
|
|
|
|
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ] || [ "$http_code" = "409" ]; then
|
|
wait_for_app_availability "$app_name" 120
|
|
return $?
|
|
fi
|
|
|
|
echo "✗ Failed to install app ${app_name} (HTTP ${http_code})"
|
|
echo " Response: ${body}"
|
|
return 1
|
|
}
|
|
|
|
wait_for_app_run_completion() {
|
|
local app_name=$1
|
|
local trigger_timestamp_ms=$2
|
|
local timeout_seconds=${3:-${APP_RUN_WAIT_TIMEOUT_SECONDS:-300}}
|
|
local freshness_tolerance_ms=${APP_RUN_FRESHNESS_TOLERANCE_MS:-5000}
|
|
local deadline=$((SECONDS + timeout_seconds))
|
|
|
|
while [ $SECONDS -lt $deadline ]; do
|
|
local latest_run_response
|
|
local fallback_status_response
|
|
local http_code
|
|
local fallback_http_code
|
|
local body
|
|
local fallback_body
|
|
local status_line
|
|
local fallback_status_line
|
|
|
|
latest_run_response=$(curl -s -w "\n%{http_code}" \
|
|
--header "Authorization: Bearer $authorizationToken" \
|
|
"http://localhost:8585/api/v1/apps/name/${app_name}/runs/latest")
|
|
http_code=$(printf "%s" "$latest_run_response" | tail -n1)
|
|
body=$(printf "%s" "$latest_run_response" | sed '$d')
|
|
|
|
case "$http_code" in
|
|
200)
|
|
status_line=$(parse_app_run_status_line "$body" "latest" "$trigger_timestamp_ms" "$freshness_tolerance_ms")
|
|
;;
|
|
204)
|
|
status_line="missing"
|
|
;;
|
|
*)
|
|
status_line="endpoint_error:${http_code}"
|
|
;;
|
|
esac
|
|
|
|
if [[ "$status_line" == stale:* || "$status_line" == "missing" || "$status_line" == "invalid" || "$status_line" == endpoint_error:* ]]; then
|
|
fallback_status_response=$(curl -s -w "\n%{http_code}" \
|
|
--header "Authorization: Bearer $authorizationToken" \
|
|
"http://localhost:8585/api/v1/apps/name/${app_name}/status?offset=0&limit=1")
|
|
fallback_http_code=$(printf "%s" "$fallback_status_response" | tail -n1)
|
|
fallback_body=$(printf "%s" "$fallback_status_response" | sed '$d')
|
|
|
|
case "$fallback_http_code" in
|
|
200)
|
|
fallback_status_line=$(parse_app_run_status_line "$fallback_body" "list" "$trigger_timestamp_ms" "$freshness_tolerance_ms")
|
|
;;
|
|
204)
|
|
fallback_status_line="missing"
|
|
;;
|
|
*)
|
|
fallback_status_line="endpoint_error:${fallback_http_code}"
|
|
;;
|
|
esac
|
|
|
|
if [[ "$fallback_status_line" != stale:* && "$fallback_status_line" != "missing" && "$fallback_status_line" != "invalid" && "$fallback_status_line" != endpoint_error:* ]]; then
|
|
status_line="$fallback_status_line"
|
|
body="$fallback_body"
|
|
elif [[ "$status_line" == endpoint_error:* && "$fallback_status_line" == endpoint_error:* ]]; then
|
|
echo "✗ Failed to read run status for ${app_name} from both app run endpoints"
|
|
echo " runs/latest response: ${body}"
|
|
echo " status response: ${fallback_body}"
|
|
return 1
|
|
elif [[ "$status_line" == "missing" || "$status_line" == "invalid" || "$status_line" == endpoint_error:* ]]; then
|
|
status_line="$fallback_status_line"
|
|
body="$fallback_body"
|
|
fi
|
|
fi
|
|
|
|
case "$status_line" in
|
|
success:completed:*|success:success:*)
|
|
echo "✓ ${app_name} completed successfully"
|
|
return 0
|
|
;;
|
|
failure:*)
|
|
echo "✗ ${app_name} finished with status ${status_line#failure:}"
|
|
echo " Response: ${body}"
|
|
return 1
|
|
;;
|
|
active:*)
|
|
echo "Waiting for ${app_name} to finish (${status_line#active:})..."
|
|
;;
|
|
seen:*)
|
|
echo "Waiting for ${app_name}; latest status was ${status_line#seen:}"
|
|
;;
|
|
stale:*|missing|invalid)
|
|
echo "Waiting for a fresh run record for ${app_name}..."
|
|
;;
|
|
*)
|
|
echo "Waiting for ${app_name}; latest status was ${status_line}"
|
|
;;
|
|
esac
|
|
|
|
sleep 5
|
|
done
|
|
|
|
echo "✗ Timed out waiting for ${app_name} to finish within ${timeout_seconds}s"
|
|
return 1
|
|
}
|
|
|
|
trigger_app_and_wait() {
|
|
local app_name=$1
|
|
local payload=${2:-}
|
|
local timeout_seconds=${3:-${APP_RUN_WAIT_TIMEOUT_SECONDS:-300}}
|
|
local trigger_timestamp_ms
|
|
local response
|
|
local http_code
|
|
local body
|
|
|
|
trigger_timestamp_ms=$(current_time_ms)
|
|
if [ -n "$payload" ]; then
|
|
response=$(curl -s -w "\n%{http_code}" --location --request POST "http://localhost:8585/api/v1/apps/trigger/${app_name}" \
|
|
--header "Authorization: Bearer $authorizationToken" \
|
|
--header 'Content-Type: application/json' \
|
|
--data-raw "$payload")
|
|
else
|
|
response=$(curl -s -w "\n%{http_code}" --location --request POST "http://localhost:8585/api/v1/apps/trigger/${app_name}" \
|
|
--header "Authorization: Bearer $authorizationToken")
|
|
fi
|
|
|
|
http_code=$(echo "$response" | tail -n1)
|
|
body=$(echo "$response" | sed '$d')
|
|
|
|
if [ "$http_code" != "200" ] && [ "$http_code" != "201" ] && [ "$http_code" != "202" ]; then
|
|
echo "✗ Failed to trigger ${app_name} (HTTP ${http_code})"
|
|
echo " Response: ${body}"
|
|
return 1
|
|
fi
|
|
|
|
wait_for_app_run_completion "$app_name" "$trigger_timestamp_ms" "$timeout_seconds"
|
|
}
|
|
|
|
run_local_docker_main() {
|
|
local mode database skipMaven includeIngestion debugOM cleanDbVolumes
|
|
local COMPOSE_FILE DB_SERVICE SEARCH_SERVICE
|
|
local response http_code body
|
|
local validation_timeout_seconds sample_data_validation_failed
|
|
local AIRFLOW_ACCESS_TOKEN IMPORT_ERRORS LOGICAL_DATE
|
|
local extra_compose_file
|
|
local -a COMPOSE_ARGS additional_up_services
|
|
|
|
OPTIND=1
|
|
while getopts "m:d:s:i:x:r:h" opt; do
|
|
case "$opt" in
|
|
m ) mode="$OPTARG" ;;
|
|
d ) database="$OPTARG" ;;
|
|
s ) skipMaven="$OPTARG" ;;
|
|
i ) includeIngestion="$OPTARG" ;;
|
|
x ) debugOM="$OPTARG" ;;
|
|
r ) cleanDbVolumes="$OPTARG" ;;
|
|
h ) helpFunction ;;
|
|
? ) helpFunction ;;
|
|
esac
|
|
done
|
|
|
|
mode="${mode:=ui}"
|
|
database="${database:=mysql}"
|
|
skipMaven="${skipMaven:=false}"
|
|
includeIngestion="${includeIngestion:=true}"
|
|
debugOM="${debugOM:=false}"
|
|
cleanDbVolumes="${cleanDbVolumes:=true}"
|
|
export APP_RUN_WAIT_TIMEOUT_SECONDS="${APP_RUN_WAIT_TIMEOUT_SECONDS:-300}"
|
|
|
|
echo "Running local docker using mode [$mode] database [$database] and skipping maven build [$skipMaven] with cleanDB as [$cleanDbVolumes] and including ingestion [$includeIngestion]"
|
|
|
|
cd "$RUN_LOCAL_DOCKER_DIR/.." || exit 1
|
|
|
|
echo "Stopping any previous Local Docker Containers"
|
|
docker compose -f docker/development/docker-compose-postgres.yml down --remove-orphans
|
|
docker compose -f docker/development/docker-compose.yml down --remove-orphans
|
|
if [[ -n "${OM_EXTRA_COMPOSE_FILES:-}" ]]; then
|
|
for extra_compose_file in $OM_EXTRA_COMPOSE_FILES; do
|
|
docker compose -f docker/development/docker-compose-postgres.yml -f "$extra_compose_file" down --remove-orphans
|
|
docker compose -f docker/development/docker-compose.yml -f "$extra_compose_file" down --remove-orphans
|
|
done
|
|
fi
|
|
|
|
if [[ $skipMaven == "false" ]]; then
|
|
if [[ $mode == "no-ui" ]]; then
|
|
echo "Maven Build - Skipping Tests and UI"
|
|
mvn -DskipTests -DonlyBackend clean package -pl !openmetadata-ui
|
|
else
|
|
echo "Maven Build - Skipping Tests"
|
|
mvn -DskipTests clean package
|
|
fi
|
|
else
|
|
echo "Skipping Maven Build"
|
|
fi
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to run Maven build!"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $debugOM == "true" ]]; then
|
|
export OPENMETADATA_DEBUG=true
|
|
fi
|
|
|
|
if [[ $cleanDbVolumes == "true" ]]; then
|
|
if [[ -d "$PWD/docker/development/docker-volume/" ]]; then
|
|
if ! rm -rf "$PWD/docker/development/docker-volume"; then
|
|
if command -v sudo >/dev/null 2>&1 && sudo -n true >/dev/null 2>&1; then
|
|
sudo rm -rf "$PWD/docker/development/docker-volume"
|
|
else
|
|
echo "Warning: failed to remove $PWD/docker/development/docker-volume; continuing may reuse stale database state"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $includeIngestion == "true" ]]; then
|
|
if [[ $VIRTUAL_ENV == "" ]]; then
|
|
echo "Please Use Virtual Environment and make sure to generate Pydantic Models"
|
|
else
|
|
echo "Generating Pydantic Models"
|
|
make install_dev generate
|
|
fi
|
|
else
|
|
echo "Skipping Pydantic Models generation (ingestion disabled)"
|
|
fi
|
|
|
|
echo "Starting Local Docker Containers"
|
|
|
|
if [[ $database == "postgresql" ]]; then
|
|
COMPOSE_FILE="docker/development/docker-compose-postgres.yml"
|
|
DB_SERVICE="postgresql"
|
|
SEARCH_SERVICE="opensearch"
|
|
elif [[ $database == "mysql" ]]; then
|
|
COMPOSE_FILE="docker/development/docker-compose.yml"
|
|
DB_SERVICE="mysql"
|
|
SEARCH_SERVICE="elasticsearch"
|
|
else
|
|
echo "Invalid database type: $database"
|
|
exit 1
|
|
fi
|
|
|
|
COMPOSE_ARGS=(-f "$COMPOSE_FILE")
|
|
if [[ -n "${OM_EXTRA_COMPOSE_FILES:-}" ]]; then
|
|
for extra_compose_file in $OM_EXTRA_COMPOSE_FILES; do
|
|
COMPOSE_ARGS+=(-f "$extra_compose_file")
|
|
done
|
|
fi
|
|
|
|
if [[ $includeIngestion == "true" ]]; then
|
|
echo "Building all services including ingestion (dependency: ${INGESTION_DEPENDENCY:-all})"
|
|
docker compose "${COMPOSE_ARGS[@]}" build --build-arg INGESTION_DEPENDENCY="${INGESTION_DEPENDENCY:-all}" && \
|
|
docker compose "${COMPOSE_ARGS[@]}" up -d
|
|
else
|
|
echo "Building services without ingestion"
|
|
docker compose "${COMPOSE_ARGS[@]}" build $SEARCH_SERVICE $DB_SERVICE execute-migrate-all openmetadata-server || exit 1
|
|
if [[ -n "${OM_ADDITIONAL_UP_SERVICES:-}" ]]; then
|
|
for extra_compose_file in $OM_ADDITIONAL_UP_SERVICES; do
|
|
additional_up_services+=("$extra_compose_file")
|
|
done
|
|
fi
|
|
docker compose "${COMPOSE_ARGS[@]}" up -d "${additional_up_services[@]}" $SEARCH_SERVICE $DB_SERVICE execute-migrate-all openmetadata-server
|
|
fi
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to start Docker instances!"
|
|
exit 1
|
|
fi
|
|
|
|
until curl -s -f "http://localhost:9200/_cat/indices/openmetadata_team_search_index"; do
|
|
echo 'Checking if Elastic Search instance is up...\n'
|
|
sleep 5
|
|
done
|
|
|
|
if [[ $includeIngestion == "true" ]]; then
|
|
get_airflow_token() {
|
|
local token_response
|
|
local access_token
|
|
|
|
token_response=$(curl -s -X POST 'http://localhost:8080/auth/token' \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"username": "admin", "password": "admin"}')
|
|
|
|
access_token=$(echo "$token_response" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('access_token', ''))" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$access_token" ]; then
|
|
echo "✗ Failed to get access token" >&2
|
|
echo " Response: ${token_response}" >&2
|
|
return 1
|
|
fi
|
|
|
|
echo "$access_token"
|
|
}
|
|
|
|
echo "Waiting for Airflow API to be ready..."
|
|
until AIRFLOW_ACCESS_TOKEN=$(get_airflow_token) 2>/dev/null && [ -n "$AIRFLOW_ACCESS_TOKEN" ]; do
|
|
echo 'Checking if Airflow API is reachable...'
|
|
sleep 5
|
|
done
|
|
echo "✓ Airflow API is ready, token obtained"
|
|
|
|
echo "Checking if Sample Data DAG is available..."
|
|
until curl -s -f -H "Authorization: Bearer $AIRFLOW_ACCESS_TOKEN" "http://localhost:8080/api/v2/dags/sample_data" >/dev/null 2>&1; do
|
|
IMPORT_ERRORS=$(curl -s -H "Authorization: Bearer $AIRFLOW_ACCESS_TOKEN" "http://localhost:8080/api/v2/importErrors" 2>/dev/null)
|
|
if [ -n "$IMPORT_ERRORS" ]; then
|
|
echo "$IMPORT_ERRORS" | grep "/airflow_sample_data.py" > /dev/null 2>&1
|
|
if [ "$?" == "0" ]; then
|
|
echo -e "${RED}Airflow found an error importing \`sample_data\` DAG"
|
|
echo "$IMPORT_ERRORS" | python3 -c "import sys, json; data=json.load(sys.stdin); [print(json.dumps(e, indent=2)) for e in data.get('import_errors', []) if e.get('filename', '').endswith('airflow_sample_data.py')]" 2>/dev/null || echo "$IMPORT_ERRORS"
|
|
exit 1
|
|
fi
|
|
fi
|
|
echo 'Checking if Sample Data DAG is reachable...'
|
|
sleep 5
|
|
AIRFLOW_ACCESS_TOKEN=$(get_airflow_token) 2>/dev/null
|
|
done
|
|
echo "✓ Sample Data DAG is available"
|
|
fi
|
|
|
|
until curl -s -f --header "Authorization: Bearer $authorizationToken" "http://localhost:8585/api/v1/tables"; do
|
|
echo 'Checking if OM Server is reachable...\n'
|
|
sleep 5
|
|
done
|
|
|
|
if [[ $includeIngestion == "true" ]]; then
|
|
unpause_dag() {
|
|
local dag_id=$1
|
|
|
|
echo "Unpausing DAG: ${dag_id}"
|
|
if [ -z "$AIRFLOW_ACCESS_TOKEN" ]; then
|
|
AIRFLOW_ACCESS_TOKEN=$(get_airflow_token)
|
|
if [ -z "$AIRFLOW_ACCESS_TOKEN" ]; then
|
|
return 1
|
|
fi
|
|
echo "✓ OAuth token obtained"
|
|
fi
|
|
|
|
response=$(curl -s -w "\n%{http_code}" --location --request PATCH "http://localhost:8080/api/v2/dags/${dag_id}" \
|
|
--header "Authorization: Bearer $AIRFLOW_ACCESS_TOKEN" \
|
|
--header 'Content-Type: application/json' \
|
|
--data-raw '{"is_paused": false}')
|
|
|
|
http_code=$(echo "$response" | tail -n1)
|
|
body=$(echo "$response" | sed '$d')
|
|
|
|
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
|
echo "✓ Successfully unpaused ${dag_id}"
|
|
else
|
|
echo "✗ Failed to unpause ${dag_id} (HTTP ${http_code})"
|
|
echo " Response: ${body}"
|
|
if [ "$http_code" = "401" ]; then
|
|
echo " Refreshing token and retrying..."
|
|
AIRFLOW_ACCESS_TOKEN=$(get_airflow_token)
|
|
if [ -n "$AIRFLOW_ACCESS_TOKEN" ]; then
|
|
response=$(curl -s -w "\n%{http_code}" --location --request PATCH "http://localhost:8080/api/v2/dags/${dag_id}" \
|
|
--header "Authorization: Bearer $AIRFLOW_ACCESS_TOKEN" \
|
|
--header 'Content-Type: application/json' \
|
|
--data-raw '{"is_paused": false}')
|
|
http_code=$(echo "$response" | tail -n1)
|
|
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
|
echo "✓ Successfully unpaused ${dag_id} after retry"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
unpause_dag "sample_data"
|
|
unpause_dag "extended_sample_data"
|
|
|
|
echo "Triggering sample_data DAG..."
|
|
LOGICAL_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
response=$(curl -s -w "\n%{http_code}" -X POST "http://localhost:8080/api/v2/dags/sample_data/dagRuns" \
|
|
--header "Authorization: Bearer $AIRFLOW_ACCESS_TOKEN" \
|
|
--header 'Content-Type: application/json' \
|
|
--data-raw "{\"logical_date\": \"$LOGICAL_DATE\"}")
|
|
|
|
http_code=$(echo "$response" | tail -n1)
|
|
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
|
echo "✓ Successfully triggered sample_data DAG"
|
|
else
|
|
echo "⚠ Could not trigger sample_data DAG (HTTP ${http_code})"
|
|
echo " Response: $(echo "$response" | sed '$d')"
|
|
echo " Note: DAG may run automatically on schedule"
|
|
fi
|
|
|
|
echo 'Validate sample data DAG...'
|
|
sleep 5
|
|
make install
|
|
|
|
echo "Running DAG validation (this may take a few minutes)..."
|
|
sample_data_validation_failed=false
|
|
validation_timeout_seconds="${VALIDATION_TIMEOUT_SECONDS:-300}"
|
|
|
|
run_with_timeout "$validation_timeout_seconds" python docker/validate_compose.py || {
|
|
local exit_code=$?
|
|
sample_data_validation_failed=true
|
|
if [ $exit_code -eq 124 ]; then
|
|
echo "⚠ Warning: DAG validation timed out after ${validation_timeout_seconds} seconds"
|
|
echo " The DAG may still be running. Check Airflow UI at http://localhost:8080"
|
|
else
|
|
echo "⚠ Warning: DAG validation failed with exit code $exit_code"
|
|
fi
|
|
echo " Continuing with remaining setup..."
|
|
}
|
|
|
|
if [[ "${STRICT_DAG_VALIDATION:-false}" == "true" && "$sample_data_validation_failed" == "true" ]]; then
|
|
echo "✗ Startup requires sample data ingestion to complete before continuing."
|
|
exit 1
|
|
fi
|
|
|
|
sleep 5
|
|
unpause_dag "sample_usage"
|
|
sleep 5
|
|
unpause_dag "index_metadata"
|
|
sleep 2
|
|
unpause_dag "sample_lineage"
|
|
else
|
|
echo "Skipping Airflow DAG setup (ingestion disabled)"
|
|
fi
|
|
|
|
echo "✔running reindexing"
|
|
ensure_app_installed "SearchIndexingApplication"
|
|
if ! trigger_app_and_wait "SearchIndexingApplication" "" "$APP_RUN_WAIT_TIMEOUT_SECONDS"; then
|
|
exit 1
|
|
fi
|
|
|
|
tput setaf 2
|
|
echo "✔ OpenMetadata is up and running"
|
|
}
|