|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Licensed to the Apache Software Foundation (ASF) under one or more |
| 3 | +# contributor license agreements. See the NOTICE file distributed with |
| 4 | +# this work for additional information regarding copyright ownership. |
| 5 | +# The ASF licenses this file to You under the Apache License, Version 2.0 |
| 6 | +# (the "License"); you may not use this file except in compliance with |
| 7 | +# the License. You may obtain a copy of the License at |
| 8 | +# |
| 9 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +# |
| 11 | +# Unless required by applicable law or agreed to in writing, software |
| 12 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | +# See the License for the specific language governing permissions and |
| 15 | +# limitations under the License. |
| 16 | + |
| 17 | +set -euo pipefail |
| 18 | + |
| 19 | +IMAGE="tinkerpop/gremlin-server:3.7.4" |
| 20 | +CONTAINER_NAME="cmdb-gremlin" |
| 21 | +HOST="localhost" |
| 22 | +PORT="8182" |
| 23 | + |
| 24 | +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
| 25 | +# Dataset lives at docs/upgrade/spec/cmdb-data.gremlin relative to this script |
| 26 | +DATASET_PATH="${SCRIPT_DIR}/../../spec/cmdb-data.gremlin" |
| 27 | + |
| 28 | +usage() { |
| 29 | + cat <<EOF |
| 30 | +Usage: $(basename "$0") <command> |
| 31 | +
|
| 32 | +Commands: |
| 33 | + up Start Gremlin Server Docker container (${IMAGE}) on port ${PORT} |
| 34 | + wait Wait until Gremlin Server is ready to accept requests |
| 35 | + seed Load dataset from ${DATASET_PATH} into the running server |
| 36 | + reset Recreate container (down + up) and seed dataset |
| 37 | + down Stop and remove the Gremlin Server container |
| 38 | + logs Tail container logs |
| 39 | + status Show container status |
| 40 | +
|
| 41 | +Environment overrides: |
| 42 | + IMAGE Docker image (default: ${IMAGE}) |
| 43 | + CONTAINER_NAME Container name (default: ${CONTAINER_NAME}) |
| 44 | + HOST Hostname for HTTP (default: ${HOST}) |
| 45 | + PORT Host port for HTTP (default: ${PORT}) |
| 46 | +EOF |
| 47 | +} |
| 48 | + |
| 49 | +container_exists() { docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; } |
| 50 | +container_running() { docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; } |
| 51 | + |
| 52 | +cmd_up() { |
| 53 | + if container_running; then |
| 54 | + echo "[INFO] Container ${CONTAINER_NAME} already running" |
| 55 | + return 0 |
| 56 | + fi |
| 57 | + if container_exists; then |
| 58 | + echo "[INFO] Starting existing container ${CONTAINER_NAME}" |
| 59 | + docker start "${CONTAINER_NAME}" >/dev/null |
| 60 | + else |
| 61 | + echo "[INFO] Pulling image ${IMAGE} (if needed)" |
| 62 | + docker pull "${IMAGE}" >/dev/null || true |
| 63 | + echo "[INFO] Creating and starting container ${CONTAINER_NAME}" |
| 64 | + docker run -d --name "${CONTAINER_NAME}" -p "${PORT}:8182" "${IMAGE}" conf/gremlin-server.yaml >/dev/null |
| 65 | + fi |
| 66 | + echo "[INFO] Container is starting..." |
| 67 | +} |
| 68 | + |
| 69 | +cmd_wait() { |
| 70 | + local timeout=90 |
| 71 | + local start_ts |
| 72 | + start_ts=$(date +%s) |
| 73 | + echo "[INFO] Waiting for Gremlin Server at http://${HOST}:${PORT}/gremlin (timeout ${timeout}s)" |
| 74 | + while true; do |
| 75 | + # Require explicit HTTP 200 from the /gremlin endpoint |
| 76 | + local resp |
| 77 | + resp=$(curl -sS -w "%{http_code}" -H 'Content-Type: application/json' \ |
| 78 | + -X POST "http://${HOST}:${PORT}/gremlin" \ |
| 79 | + --data '{"gremlin":"g.inject(1).count()"}') |
| 80 | + local http_code |
| 81 | + http_code="${resp: -3}" |
| 82 | + if [[ "$http_code" == "200" ]]; then |
| 83 | + break |
| 84 | + fi |
| 85 | + sleep 2 |
| 86 | + local now |
| 87 | + now=$(date +%s) |
| 88 | + if (( now - start_ts > timeout )); then |
| 89 | + echo "[ERROR] Timed out waiting for Gremlin Server to become ready (last HTTP code: ${http_code})" >&2 |
| 90 | + exit 1 |
| 91 | + fi |
| 92 | + done |
| 93 | + echo "[INFO] Gremlin Server is ready" |
| 94 | +} |
| 95 | + |
| 96 | +cmd_seed() { |
| 97 | + if ! container_running; then |
| 98 | + echo "[ERROR] Container ${CONTAINER_NAME} is not running; start it with: $0 up && $0 wait" >&2 |
| 99 | + exit 1 |
| 100 | + fi |
| 101 | + if [[ ! -f "${DATASET_PATH}" ]]; then |
| 102 | + echo "[ERROR] Dataset not found at ${DATASET_PATH}" >&2 |
| 103 | + exit 1 |
| 104 | + fi |
| 105 | + echo "[INFO] Loading dataset from ${DATASET_PATH}" |
| 106 | + |
| 107 | + # Clear existing graph first to ensure a clean, deterministic state |
| 108 | + clear_payload=$(printf '%s' "g.V().drop().iterate()" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g') |
| 109 | + clear_resp=$(curl -sS -w "%{http_code}" -H 'Content-Type: application/json' \ |
| 110 | + -X POST "http://${HOST}:${PORT}/gremlin" \ |
| 111 | + --data "{\"gremlin\":\"${clear_payload}\"}") |
| 112 | + clear_code="${clear_resp: -3}" |
| 113 | + clear_body="${clear_resp::-3}" |
| 114 | + if [[ "$clear_code" != "200" ]]; then |
| 115 | + echo "[ERROR] Failed to clear graph with g.V().drop().iterate()" >&2 |
| 116 | + echo "[ERROR] HTTP ${clear_code} Response: ${clear_body}" >&2 |
| 117 | + exit 1 |
| 118 | + fi |
| 119 | + |
| 120 | + # Post each non-empty, non-comment line to the /gremlin endpoint |
| 121 | + local line_no=0 |
| 122 | + while IFS= read -r raw_line || [[ -n "$raw_line" ]]; do |
| 123 | + line_no=$((line_no + 1)) |
| 124 | + # Trim leading/trailing whitespace |
| 125 | + line="${raw_line%%[[:space:]]*}" |
| 126 | + line="${raw_line%$'\r'}" # strip CR if present |
| 127 | + trimmed="$(echo "$raw_line" | sed -e 's/^\s\+//' -e 's/\s\+$//')" |
| 128 | + |
| 129 | + # Skip empty lines and comments (// ... or # ...) |
| 130 | + if [[ -z "${trimmed}" ]] || [[ "${trimmed}" =~ ^// ]] || [[ "${trimmed}" =~ ^# ]]; then |
| 131 | + continue |
| 132 | + fi |
| 133 | + |
| 134 | + # Escape JSON special chars in the Gremlin line: backslash and quotes |
| 135 | + esc_line=$(printf '%s' "$trimmed" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g') |
| 136 | + |
| 137 | + # Submit |
| 138 | + resp=$(curl -sS -w "%{http_code}" -H 'Content-Type: application/json' \ |
| 139 | + -X POST "http://${HOST}:${PORT}/gremlin" \ |
| 140 | + --data "{\"gremlin\":\"${esc_line}\"}") |
| 141 | + |
| 142 | + http_code="${resp: -3}" |
| 143 | + body="${resp::-3}" |
| 144 | + |
| 145 | + if [[ "$http_code" != "200" ]]; then |
| 146 | + echo "[ERROR] Failed at line ${line_no}: ${trimmed}" >&2 |
| 147 | + echo "[ERROR] HTTP ${http_code} Response: ${body}" >&2 |
| 148 | + exit 1 |
| 149 | + fi |
| 150 | + done < "${DATASET_PATH}" |
| 151 | + |
| 152 | + echo "[INFO] Dataset load complete" |
| 153 | +} |
| 154 | + |
| 155 | +cmd_reset() { |
| 156 | + cmd_down || true |
| 157 | + cmd_up |
| 158 | + cmd_wait |
| 159 | + cmd_seed |
| 160 | +} |
| 161 | + |
| 162 | +cmd_down() { |
| 163 | + if container_exists; then |
| 164 | + echo "[INFO] Stopping and removing ${CONTAINER_NAME}" |
| 165 | + docker rm -f "${CONTAINER_NAME}" >/dev/null || true |
| 166 | + else |
| 167 | + echo "[INFO] No existing container named ${CONTAINER_NAME}" |
| 168 | + fi |
| 169 | +} |
| 170 | + |
| 171 | +cmd_logs() { |
| 172 | + if container_exists; then |
| 173 | + docker logs -f "${CONTAINER_NAME}" |
| 174 | + else |
| 175 | + echo "[INFO] No existing container named ${CONTAINER_NAME}" |
| 176 | + fi |
| 177 | +} |
| 178 | + |
| 179 | +cmd_status() { |
| 180 | + if container_running; then |
| 181 | + echo "running" |
| 182 | + elif container_exists; then |
| 183 | + echo "stopped" |
| 184 | + else |
| 185 | + echo "absent" |
| 186 | + fi |
| 187 | +} |
| 188 | + |
| 189 | +main() { |
| 190 | + local cmd="${1:-}" || true |
| 191 | + case "${cmd}" in |
| 192 | + up) cmd_up ;; |
| 193 | + wait) cmd_wait ;; |
| 194 | + seed) cmd_seed ;; |
| 195 | + reset) cmd_reset ;; |
| 196 | + down) cmd_down ;; |
| 197 | + logs) cmd_logs ;; |
| 198 | + status) cmd_status ;; |
| 199 | + -h|--help|help|"") usage ;; |
| 200 | + *) echo "Unknown command: ${cmd}"; usage; exit 1 ;; |
| 201 | + esac |
| 202 | +} |
| 203 | + |
| 204 | +main "$@" |
0 commit comments