#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" export SOC_SHARED_NETWORK="${SOC_SHARED_NETWORK:-soc_shared}" export IRIS_HTTPS_PORT="${IRIS_HTTPS_PORT:-8443}" export INTERFACE_HTTPS_PORT="${IRIS_HTTPS_PORT}" export SHUFFLE_OPENSEARCH_PORT="${SHUFFLE_OPENSEARCH_PORT:-9201}" export PAGERDUTY_STUB_PORT="${PAGERDUTY_STUB_PORT:-18080}" export SOC_INTEGRATOR_PORT="${SOC_INTEGRATOR_PORT:-8088}" if [[ $# -eq 0 ]]; then COMMAND="up" ARGS=(-d) else COMMAND="$1" shift ARGS=("$@") fi if [[ "${COMMAND}" == "help" || "${COMMAND}" == "--help" || "${COMMAND}" == "-h" ]]; then cat <<'EOF' Usage: ./run-combined-stack.sh [command] [target] [options] Commands: up Start services (default: up -d when no args) recreate Force-recreate containers (picks up bind-mount inode changes) dedup Remove duplicate OpenSearch index patterns in Wazuh dashboard down Stop services (requires explicit target) logs View logs status Show container and endpoint status cleanup Remove unused Docker resources (containers/images/cache) help Show this help message Targets: all|--all All stacks (wazuh, iris, shuffle, pagerduty, integrator, flask-openapi-shuffle) wazuh Wazuh single-node stack iris IRIS-web stack shuffle Shuffle stack pagerduty PagerDuty stub integrator SOC integrator stack flask-openapi-shuffle Flask OpenAPI demo stack Examples: ./run-combined-stack.sh ./run-combined-stack.sh up --all -d ./run-combined-stack.sh up iris -d ./run-combined-stack.sh up flask-openapi-shuffle -d ./run-combined-stack.sh recreate wazuh ./run-combined-stack.sh recreate --all ./run-combined-stack.sh dedup ./run-combined-stack.sh down shuffle ./run-combined-stack.sh down --all ./run-combined-stack.sh logs integrator -f ./run-combined-stack.sh logs --all --tail 200 ./run-combined-stack.sh cleanup ./run-combined-stack.sh cleanup --with-volumes ./run-combined-stack.sh status EOF exit 0 fi if [[ "${COMMAND}" == "up" || "${COMMAND}" == "down" || "${COMMAND}" == "logs" ]]; then if ! docker network inspect "${SOC_SHARED_NETWORK}" >/dev/null 2>&1; then docker network create "${SOC_SHARED_NETWORK}" >/dev/null fi fi if [[ "${COMMAND}" == "status" ]]; then exec "${ROOT_DIR}/soc-status.sh" fi dedup_index_patterns() { local dashboard_url="https://localhost:443" local user="kibanaserver" local pass="kibanaserver" local max_wait=60 local waited=0 echo "Waiting for Wazuh dashboard to be ready..." until curl -sk -u "${user}:${pass}" "${dashboard_url}/api/status" -o /dev/null 2>&1; do sleep 3 waited=$((waited + 3)) if [[ ${waited} -ge ${max_wait} ]]; then echo "Dashboard not ready after ${max_wait}s — skipping dedup." return 1 fi done echo "Scanning for duplicate index patterns..." python3 - <" echo " ./run-combined-stack.sh down --all" exit 1 fi case "${TARGET}" in wazuh|iris|shuffle|pagerduty|integrator|flask-openapi-shuffle) ARGS=("${ARGS[@]:1}") run_target "${TARGET}" ;; all|--all) ARGS=("${ARGS[@]:1}") run_all "down" ;; *) run_all "down" ;; esac elif [[ "${COMMAND}" == "logs" ]]; then LOGS_TARGET="${1:-all}" case "${LOGS_TARGET}" in wazuh|iris|shuffle|pagerduty|integrator|flask-openapi-shuffle) ARGS=("${ARGS[@]:1}") run_logs_for_target "${LOGS_TARGET}" ;; all|--all) ARGS=("${ARGS[@]:1}") run_logs_for_target "all" ;; *) for arg in ${ARGS[@]+"${ARGS[@]}"}; do if [[ "${arg}" == "-f" || "${arg}" == "--follow" ]]; then echo "For follow mode, specify one target:" echo "./run-combined-stack.sh logs -f" exit 1 fi done run_logs_for_target "all" ;; esac elif [[ "${COMMAND}" == "up" ]]; then TARGET="${1:-all}" case "${TARGET}" in wazuh|iris|shuffle|pagerduty|integrator|flask-openapi-shuffle) ARGS=("${ARGS[@]:1}") run_target "${TARGET}" ;; all|--all) ARGS=("${ARGS[@]:1}") HAS_DETACH="false" for arg in ${ARGS[@]+"${ARGS[@]}"}; do if [[ "${arg}" == "-d" || "${arg}" == "--detach" ]]; then HAS_DETACH="true" break fi done if [[ "${HAS_DETACH}" == "true" ]]; then run_all "up" else ARGS+=("-d") run_all "up" follow_all_logs fi ;; *) run_all "up" ;; esac elif [[ "${COMMAND}" == "recreate" ]]; then TARGET="${1:-all}" COMMAND="up" case "${TARGET}" in wazuh|iris|shuffle|pagerduty|integrator|flask-openapi-shuffle) ARGS=("--force-recreate" "-d") run_target "${TARGET}" ;; all|--all|*) ARGS=("--force-recreate" "-d") run_all "up" ;; esac elif [[ "${COMMAND}" == "cleanup" ]]; then WITH_VOLUMES="false" for arg in ${ARGS[@]+"${ARGS[@]}"}; do case "${arg}" in --with-volumes|-v) WITH_VOLUMES="true" ;; *) ;; esac done run_cleanup "${WITH_VOLUMES}" elif [[ "${COMMAND}" == "dedup" ]]; then dedup_index_patterns else run_all "up" fi