Mirror of https://github.com/roostorg/osprey
github.com/roostorg/osprey
1#!/bin/bash
2
3# Osprey Demo Script
4# This script starts all services, generates test data, and opens the UI
5#
6# Usage:
7# From repo: ./demo.sh
8# Standalone: curl -sSL https://raw.githubusercontent.com/roostorg/osprey/main/demo.sh | bash
9
10set -e
11
12REPO_URL="https://github.com/roostorg/osprey.git"
13DEMO_DIR="osprey-demo"
14COMPOSE_FILE="docker-compose.yaml"
15
16# Colors for output
17RED='\033[0;31m'
18GREEN='\033[0;32m'
19YELLOW='\033[1;33m'
20BLUE='\033[0;34m'
21NC='\033[0m' # No Color
22
23print_banner() {
24 echo -e "${BLUE}========================================${NC}"
25 echo -e "${BLUE} Osprey Demo Setup Script ${NC}"
26 echo -e "${BLUE}========================================${NC}"
27 echo ""
28}
29
30check_prerequisites() {
31 echo -e "${YELLOW}Checking prerequisites...${NC}"
32
33 # Check for Docker
34 if ! command -v docker &> /dev/null; then
35 echo -e "${RED}✗ Docker is not installed. Please install Docker first.${NC}"
36 echo " Visit: https://docs.docker.com/get-docker/"
37 exit 1
38 fi
39 echo -e "${GREEN}✓ Docker found${NC}"
40
41 # Check for Docker Compose v2
42 if ! docker compose version &> /dev/null; then
43 echo -e "${RED}✗ Docker Compose v2 is not available.${NC}"
44 echo " Please update Docker Desktop or install docker-compose-plugin"
45 exit 1
46 fi
47 echo -e "${GREEN}✓ Docker Compose found${NC}"
48
49 # Check if Docker daemon is running
50 if ! docker info &> /dev/null; then
51 echo -e "${RED}✗ Docker daemon is not running. Please start Docker.${NC}"
52 exit 1
53 fi
54 echo -e "${GREEN}✓ Docker daemon is running${NC}"
55 echo ""
56}
57
58check_port_available() {
59 local port=$1
60 local service=$2
61 if lsof -i :"$port" -sTCP:LISTEN >/dev/null 2>&1; then
62 echo -e "${RED}✗ Port $port is already in use (needed for $service)${NC}"
63 echo -e " Run: lsof -i :$port to see what's using it"
64 return 1
65 fi
66 return 0
67}
68
69check_existing_services() {
70 echo -e "${YELLOW}Checking for existing Osprey services...${NC}"
71
72 # Check for running containers from this compose project
73 local running_containers=$(docker ps --filter "name=osprey\|kafka\|postgres\|minio\|druid\|zookeeper\|snowflake" --format "{{.Names}}" 2>/dev/null | head -10)
74
75 if [ -n "$running_containers" ]; then
76 echo -e "${YELLOW}Found existing services that may conflict:${NC}"
77 echo "$running_containers" | while read -r container; do
78 echo -e " • $container"
79 done
80 echo ""
81 echo -e "${YELLOW}Starting the demo will stop these services and remove their volumes (data will be lost).${NC}"
82 echo -n -e "${BLUE}Do you want to continue? [y/N]: ${NC}"
83 read -r response
84 if [[ ! "$response" =~ ^[Yy]$ ]]; then
85 echo -e "${RED}Aborted.${NC}"
86 exit 0
87 fi
88 echo ""
89 else
90 echo -e "${GREEN}✓ No conflicting services found${NC}"
91 fi
92 echo ""
93}
94
95check_required_ports() {
96 echo -e "${YELLOW}Checking required ports...${NC}"
97
98 local failed=0
99
100 # Infrastructure ports
101 check_port_available 9092 "Kafka" || failed=1
102 check_port_available 9000 "MinIO API" || failed=1
103 check_port_available 9001 "MinIO Console" || failed=1
104 check_port_available 5432 "PostgreSQL" || failed=1
105 check_port_available 8088 "Snowflake ID Worker" || failed=1
106
107 # Druid ports
108 check_port_available 2181 "Zookeeper" || failed=1
109 check_port_available 8081 "Druid Coordinator" || failed=1
110 check_port_available 8082 "Druid Broker" || failed=1
111 check_port_available 8083 "Druid Historical" || failed=1
112 check_port_available 8091 "Druid MiddleManager" || failed=1
113 check_port_available 8888 "Druid Router" || failed=1
114
115 # Druid MiddleManager task ports
116 for port in 8100 8101 8102 8103 8104 8105; do
117 check_port_available $port "Druid MiddleManager Tasks" || failed=1
118 done
119
120 # Osprey service ports
121 check_port_available 5001 "Osprey Worker" || failed=1
122 check_port_available 5002 "Osprey UI" || failed=1
123 check_port_available 5004 "Osprey UI API" || failed=1
124
125 if [ $failed -eq 1 ]; then
126 echo ""
127 echo -e "${RED}✗ Some required ports are in use. Please free them before running the demo.${NC}"
128 exit 1
129 fi
130
131 echo -e "${GREEN}✓ All required ports are available${NC}"
132 echo ""
133}
134
135setup_repo() {
136 # Check if we're already in the osprey repo
137 if [ -f "docker-compose.yaml" ] && grep -q "osprey-worker" "docker-compose.yaml" 2>/dev/null; then
138 echo -e "${GREEN}✓ Running from Osprey repository${NC}"
139 return 0
140 fi
141
142 # Check if we need to clone
143 echo -e "${YELLOW}Osprey repository not found. Setting up...${NC}"
144
145 if ! command -v git &> /dev/null; then
146 echo -e "${RED}✗ Git is not installed. Please install Git first.${NC}"
147 exit 1
148 fi
149
150 if [ -d "$DEMO_DIR" ]; then
151 echo -e "${YELLOW}Directory '$DEMO_DIR' already exists, using it${NC}"
152 cd "$DEMO_DIR"
153 echo -e "${YELLOW}Pulling latest changes...${NC}"
154 git pull
155 echo -e "${GREEN}✓ Repository updated${NC}"
156 echo ""
157 return 0
158 else
159 echo -e "${YELLOW}Cloning Osprey repository...${NC}"
160 git clone --depth 1 "$REPO_URL" "$DEMO_DIR"
161 fi
162
163 cd "$DEMO_DIR"
164 echo -e "${GREEN}✓ Repository ready${NC}"
165 echo ""
166}
167
168# Cleanup function for signal handling
169cleanup() {
170 echo ""
171 echo -e "${YELLOW}Caught interrupt signal. Cleaning up...${NC}"
172 DOCKER_CLI_HINTS=false docker compose -f "$COMPOSE_FILE" --progress=quiet --profile test_data down -v 2>/dev/null || true
173 echo -e "${GREEN}✓ Services stopped and volumes removed${NC}"
174 exit 1
175}
176
177# Run the functions we defined above
178print_banner
179check_prerequisites
180check_existing_services
181setup_repo
182
183# Set up signal trap after we're in the repo (services may be started after this point)
184trap cleanup INT TERM
185
186# Function to check if a service is healthy via HTTP
187wait_for_http_service() {
188 local service=$1
189 local url=$2
190 local max_attempts=${3:-60}
191 local attempt=1
192
193 echo -e "${YELLOW}Waiting for $service...${NC}"
194 while [ $attempt -le $max_attempts ]; do
195 if curl -s "$url" > /dev/null 2>&1; then
196 echo -e "${GREEN}✓ $service is ready${NC}"
197 return 0
198 fi
199 sleep 2
200 attempt=$((attempt + 1))
201 done
202 echo -e "${RED}✗ $service failed to start${NC}"
203 return 1
204}
205
206# Function to check if a Docker container is healthy
207wait_for_container() {
208 local container=$1
209 local max_attempts=${2:-60}
210 local attempt=1
211
212 echo -e "${YELLOW}Waiting for $container...${NC}"
213 while [ $attempt -le $max_attempts ]; do
214 local status=$(docker inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null || echo "not_found")
215 if [ "$status" = "healthy" ]; then
216 echo -e "${GREEN}✓ $container is ready${NC}"
217 return 0
218 elif [ "$status" = "not_found" ]; then
219 # Container doesn't have health check, check if running
220 local running=$(docker inspect --format='{{.State.Running}}' "$container" 2>/dev/null || echo "false")
221 if [ "$running" = "true" ]; then
222 echo -e "${GREEN}✓ $container is running${NC}"
223 return 0
224 fi
225 fi
226 sleep 2
227 attempt=$((attempt + 1))
228 done
229 echo -e "${RED}✗ $container failed to start${NC}"
230 return 1
231}
232
233# Step 1: Stop any existing services and remove volumes for clean state
234echo -e "${YELLOW}Step 1: Cleaning up existing services and volumes...${NC}"
235DOCKER_CLI_HINTS=false docker compose -f "$COMPOSE_FILE" --progress=quiet --profile test_data down -v 2>/dev/null || true
236DOCKER_CLI_HINTS=false docker compose --progress=quiet --profile test_data down -v 2>/dev/null || true # Also clean up old full compose
237echo -e "${GREEN}✓ Cleanup complete (volumes removed for fresh start)${NC}"
238echo ""
239
240# Step 1.5: Check ports are available after cleanup
241check_required_ports
242
243# Step 2: Pull Docker images
244echo -e "${YELLOW}Step 2: Pulling Docker images (this may take a few minutes on first run)...${NC}"
245DOCKER_CLI_HINTS=false docker compose -f "$COMPOSE_FILE" --progress=quiet pull --quiet
246echo -e "${GREEN}✓ Images pulled${NC}"
247echo ""
248
249# Step 3: Start core services (build if needed)
250echo -e "${YELLOW}Step 3: Starting core services (building images if needed)...${NC}"
251DOCKER_CLI_HINTS=false docker compose -f "$COMPOSE_FILE" --progress=quiet up -d --build --quiet-pull
252echo -e "${GREEN}✓ Core services starting${NC}"
253echo ""
254
255# Step 4: Wait for critical services
256echo -e "${YELLOW}Step 4: Waiting for services to be ready...${NC}"
257echo ""
258
259# Wait for infrastructure containers (use Docker health checks)
260wait_for_container "osprey-kafka" 90 || exit 1
261wait_for_container "postgres" 90 || exit 1
262wait_for_container "minio" 90 || exit 1
263
264# Wait for Druid broker (use HTTP check - no Docker health check configured)
265wait_for_http_service "Druid Broker" "http://localhost:8082/status" 180 || exit 1
266
267# Wait for osprey services (HTTP check)
268wait_for_http_service "Osprey UI API" "http://localhost:5004/config" 120 || exit 1
269wait_for_http_service "Osprey UI" "http://localhost:5002" 90 || exit 1
270
271echo ""
272echo -e "${GREEN}✓ All services are ready${NC}"
273echo ""
274
275# Step 5: Start test data generator
276echo -e "${YELLOW}Step 5: Starting test data generator...${NC}"
277DOCKER_CLI_HINTS=false docker compose -f "$COMPOSE_FILE" --progress=quiet --profile test_data up -d osprey-kafka-test-data-producer
278echo -e "${GREEN}✓ Test data generator started (1 event/second)${NC}"
279echo ""
280
281# Step 6: Wait for some events to be processed
282echo -e "${YELLOW}Step 6: Waiting for events to be processed...${NC}"
283sleep 15
284echo -e "${GREEN}✓ Events should now be in Druid${NC}"
285echo ""
286
287# Generate UI URL with date range
288START_DATE=$(date -u -v-1d +%Y-%m-%dT00:00:00.000Z 2>/dev/null || date -u -d "yesterday" +%Y-%m-%dT00:00:00.000Z)
289END_DATE=$(date -u +%Y-%m-%dT23:59:59.999Z 2>/dev/null || date -u +%Y-%m-%dT23:59:59.999Z)
290UI_URL="http://localhost:5002/?start=${START_DATE}&end=${END_DATE}&interval=day&queryFilter=&topn=UserId"
291
292echo -e "${BLUE}========================================${NC}"
293echo -e "${GREEN} Demo Ready! ${NC}"
294echo -e "${BLUE}========================================${NC}"
295echo ""
296echo -e "${BLUE}Available Interfaces:${NC}"
297echo -e " • Osprey UI: ${GREEN}${UI_URL}${NC}"
298echo -e " • Druid Console: ${GREEN}http://localhost:8888${NC}"
299echo -e " • Osprey API: ${GREEN}http://localhost:5004${NC}"
300echo ""
301echo -e "${BLUE}Demo Rules Active:${NC}"
302echo -e " • ContainsHello - Bans users who say 'hello'"
303echo -e " • LazyPostRule - Labels posts with 'lazy' as low_effort"
304echo -e " • QuickPostRule - Labels posts with 'quick' as potential_bot"
305echo -e " • FoxPostRule - Bans users who say 'fox' (spam pattern)"
306echo ""
307echo -e "${BLUE}What to Demo:${NC}"
308echo -e " 1. Event Stream - See processed events with rule matches"
309echo -e " 2. TopN Panel - See users grouped by labels/bans"
310echo -e " 3. Timeseries - See event volume over time"
311echo -e " 4. Query Filter - Try: LazyPostRule == True"
312echo -e " 5. Rules Viz - View the rule dependency graph"
313echo ""
314echo -e "${YELLOW}Opening Osprey UI in browser...${NC}"
315
316# Open browser (works on macOS, Linux with xdg-open, or WSL)
317if command -v open &> /dev/null; then
318 open "$UI_URL"
319elif command -v xdg-open &> /dev/null; then
320 xdg-open "$UI_URL"
321elif command -v wslview &> /dev/null; then
322 wslview "$UI_URL"
323else
324 echo -e "${YELLOW}Please open this URL manually: ${UI_URL}${NC}"
325fi
326
327echo ""
328echo -e "${BLUE}To stop the demo:${NC}"
329echo -e " cd $(pwd) && docker compose --profile test_data down -v"
330echo ""
331echo -e "${GREEN}Demo is running! Press Ctrl+C to exit this script (services will keep running)${NC}"