blob: a7a3a04a1502658602d505e9eb3cb8fe3afb04fa [file] [log] [blame]
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
set -euo pipefail
IMAGE="tinkerpop/gremlin-server:3.7.4"
CONTAINER_NAME="cmdb-gremlin"
HOST="localhost"
PORT="8182"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Dataset lives at docs/upgrade/spec/cmdb-data.gremlin relative to this script
DATASET_PATH="${SCRIPT_DIR}/../../spec/cmdb-data.gremlin"
usage() {
cat <<EOF
Usage: $(basename "$0") <command>
Commands:
up Start Gremlin Server Docker container (${IMAGE}) on port ${PORT}
wait Wait until Gremlin Server is ready to accept requests
seed Load dataset from ${DATASET_PATH} into the running server
reset Recreate container (down + up) and seed dataset
down Stop and remove the Gremlin Server container
logs Tail container logs
status Show container status
Environment overrides:
IMAGE Docker image (default: ${IMAGE})
CONTAINER_NAME Container name (default: ${CONTAINER_NAME})
HOST Hostname for HTTP (default: ${HOST})
PORT Host port for HTTP (default: ${PORT})
EOF
}
container_exists() { docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; }
container_running() { docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; }
cmd_up() {
if container_running; then
echo "[INFO] Container ${CONTAINER_NAME} already running"
return 0
fi
if container_exists; then
echo "[INFO] Starting existing container ${CONTAINER_NAME}"
docker start "${CONTAINER_NAME}" >/dev/null
else
echo "[INFO] Pulling image ${IMAGE} (if needed)"
docker pull "${IMAGE}" >/dev/null || true
echo "[INFO] Creating and starting container ${CONTAINER_NAME}"
docker run -d --name "${CONTAINER_NAME}" -p "${PORT}:8182" "${IMAGE}" conf/gremlin-server.yaml >/dev/null
fi
echo "[INFO] Container is starting..."
}
cmd_wait() {
local timeout=90
local start_ts
start_ts=$(date +%s)
echo "[INFO] Waiting for Gremlin Server at http://${HOST}:${PORT}/gremlin (timeout ${timeout}s)"
while true; do
# Require explicit HTTP 200 from the /gremlin endpoint
local resp
resp=$(curl -sS -w "%{http_code}" -H 'Content-Type: application/json' \
-X POST "http://${HOST}:${PORT}/gremlin" \
--data '{"gremlin":"g.inject(1).count()"}')
local http_code
http_code="${resp: -3}"
if [[ "$http_code" == "200" ]]; then
break
fi
sleep 2
local now
now=$(date +%s)
if (( now - start_ts > timeout )); then
echo "[ERROR] Timed out waiting for Gremlin Server to become ready (last HTTP code: ${http_code})" >&2
exit 1
fi
done
echo "[INFO] Gremlin Server is ready"
}
cmd_seed() {
if ! container_running; then
echo "[ERROR] Container ${CONTAINER_NAME} is not running; start it with: $0 up && $0 wait" >&2
exit 1
fi
if [[ ! -f "${DATASET_PATH}" ]]; then
echo "[ERROR] Dataset not found at ${DATASET_PATH}" >&2
exit 1
fi
echo "[INFO] Loading dataset from ${DATASET_PATH}"
# Clear existing graph first to ensure a clean, deterministic state
clear_payload=$(printf '%s' "g.V().drop().iterate()" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
clear_resp=$(curl -sS -w "%{http_code}" -H 'Content-Type: application/json' \
-X POST "http://${HOST}:${PORT}/gremlin" \
--data "{\"gremlin\":\"${clear_payload}\"}")
clear_code="${clear_resp: -3}"
clear_body="${clear_resp::-3}"
if [[ "$clear_code" != "200" ]]; then
echo "[ERROR] Failed to clear graph with g.V().drop().iterate()" >&2
echo "[ERROR] HTTP ${clear_code} Response: ${clear_body}" >&2
exit 1
fi
# Post each non-empty, non-comment line to the /gremlin endpoint
local line_no=0
while IFS= read -r raw_line || [[ -n "$raw_line" ]]; do
line_no=$((line_no + 1))
# Trim leading/trailing whitespace
line="${raw_line%%[[:space:]]*}"
line="${raw_line%$'\r'}" # strip CR if present
trimmed="$(echo "$raw_line" | sed -e 's/^\s\+//' -e 's/\s\+$//')"
# Skip empty lines and comments (// ... or # ...)
if [[ -z "${trimmed}" ]] || [[ "${trimmed}" =~ ^// ]] || [[ "${trimmed}" =~ ^# ]]; then
continue
fi
# Escape JSON special chars in the Gremlin line: backslash and quotes
esc_line=$(printf '%s' "$trimmed" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
# Submit
resp=$(curl -sS -w "%{http_code}" -H 'Content-Type: application/json' \
-X POST "http://${HOST}:${PORT}/gremlin" \
--data "{\"gremlin\":\"${esc_line}\"}")
http_code="${resp: -3}"
body="${resp::-3}"
if [[ "$http_code" != "200" ]]; then
echo "[ERROR] Failed at line ${line_no}: ${trimmed}" >&2
echo "[ERROR] HTTP ${http_code} Response: ${body}" >&2
exit 1
fi
done < "${DATASET_PATH}"
echo "[INFO] Dataset load complete"
}
cmd_reset() {
cmd_down || true
cmd_up
cmd_wait
cmd_seed
}
cmd_down() {
if container_exists; then
echo "[INFO] Stopping and removing ${CONTAINER_NAME}"
docker rm -f "${CONTAINER_NAME}" >/dev/null || true
else
echo "[INFO] No existing container named ${CONTAINER_NAME}"
fi
}
cmd_logs() {
if container_exists; then
docker logs -f "${CONTAINER_NAME}"
else
echo "[INFO] No existing container named ${CONTAINER_NAME}"
fi
}
cmd_status() {
if container_running; then
echo "running"
elif container_exists; then
echo "stopped"
else
echo "absent"
fi
}
main() {
local cmd="${1:-}" || true
case "${cmd}" in
up) cmd_up ;;
wait) cmd_wait ;;
seed) cmd_seed ;;
reset) cmd_reset ;;
down) cmd_down ;;
logs) cmd_logs ;;
status) cmd_status ;;
-h|--help|help|"") usage ;;
*) echo "Unknown command: ${cmd}"; usage; exit 1 ;;
esac
}
main "$@"