Skip to content

Commit 475829c

Browse files
committed
feat(lab-01): FreePBX Standalone -- MariaDB + Asterisk/FreePBX web UI, real compose and test script
1 parent d5137da commit 475829c

3 files changed

Lines changed: 137 additions & 105 deletions

File tree

.github/workflows/ci.yml

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -72,42 +72,27 @@ jobs:
7272
sarif_file: trivy-results.sarif
7373

7474
lab-01-smoke:
75-
name: Lab 01 — Smoke Test
75+
name: Lab 01 -- FreePBX Standalone (MariaDB + Asterisk)
7676
runs-on: ubuntu-latest
7777
needs: validate
78-
continue-on-error: true # scaffold stubs; full lab runs on real VMs
78+
continue-on-error: true
7979
steps:
8080
- uses: actions/checkout@v4
81-
82-
- name: Generate CI env file
83-
run: |
84-
# Copy example env and inject CI-safe defaults for any unset port vars
85-
if [ -f .env.example ]; then cp .env.example .env; fi
86-
# Set port placeholder vars used in scaffold compose files
87-
echo "firstPort=389" >> .env
88-
echo "secondPort=9090" >> .env
89-
90-
- name: Validate standalone compose can start
91-
run: |
92-
docker compose -f docker/docker-compose.standalone.yml config --no-interpolate -q
93-
echo "Standalone compose structure is valid"
94-
81+
- name: Install tools
82+
run: sudo apt-get install -y curl
83+
- name: Validate standalone compose
84+
run: docker compose -f docker/docker-compose.standalone.yml config -q && echo "Standalone compose valid"
9585
- name: Start standalone stack
9686
run: docker compose -f docker/docker-compose.standalone.yml up -d
97-
98-
- name: Wait for health
99-
run: |
100-
echo "Waiting for services..."
101-
sleep 30
102-
docker compose -f docker/docker-compose.standalone.yml ps
103-
104-
- name: Run Lab 01 test script
105-
run: bash tests/labs/test-lab-01.sh
106-
87+
- name: Wait for MariaDB
88+
run: timeout 120 bash -c 'until docker exec freepbx-s01-db mysqladmin ping -h localhost -u root -pRootLab01! > /dev/null 2>&1; do sleep 5; done'
89+
- name: Wait for FreePBX web
90+
run: timeout 300 bash -c 'until curl -sf http://localhost:8301/admin/config.php; do sleep 10; done'
91+
- name: Run Lab 10-01 test script
92+
run: bash tests/labs/test-lab-10-01.sh --no-cleanup
10793
- name: Collect logs on failure
10894
if: failure()
10995
run: docker compose -f docker/docker-compose.standalone.yml logs
110-
11196
- name: Cleanup
11297
if: always()
11398
run: docker compose -f docker/docker-compose.standalone.yml down -v
Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,66 @@
1-
# Lab 01 — Standalone: Complete FreePBX/Asterisk VoIP PBX in isolation
2-
# No external dependencies required.
1+
# Lab 01 — Standalone: FreePBX/Asterisk VoIP PBX in complete isolation
2+
# Module 10 | No external dependencies required
33
---
44
services:
5-
freepbx:
5+
freepbx-s01-db:
6+
image: mariadb:10.11
7+
container_name: freepbx-s01-db
8+
restart: unless-stopped
9+
environment:
10+
MYSQL_ROOT_PASSWORD: RootLab01!
11+
MYSQL_DATABASE: asterisk
12+
MYSQL_USER: asterisk
13+
MYSQL_PASSWORD: AsteriskLab01!
14+
volumes:
15+
- freepbx-s01-db-data:/var/lib/mysql
16+
networks:
17+
- freepbx-s01-net
18+
healthcheck:
19+
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-pRootLab01!"]
20+
interval: 10s
21+
timeout: 5s
22+
retries: 10
23+
start_period: 30s
24+
25+
freepbx-s01-app:
626
image: tiredofit/freepbx:16
7-
container_name: it-stack-freepbx
27+
container_name: freepbx-s01-app
828
restart: unless-stopped
9-
ports:
10-
- "5060:$firstPort"
29+
depends_on:
30+
freepbx-s01-db:
31+
condition: service_healthy
1132
environment:
12-
- IT_STACK_ENV=lab-01-standalone
33+
DB_HOST: freepbx-s01-db
34+
DB_NAME: asterisk
35+
DB_USER: asterisk
36+
DB_PASS: AsteriskLab01!
37+
DB_ADMIN_PASSWORD: RootLab01!
38+
ADMIN_PASSWORD: Admin01!
39+
WEBROOT: /var/www/html
40+
RTP_START: "18000"
41+
RTP_FINISH: "18100"
42+
TZ: UTC
43+
ports:
44+
- "8301:80"
45+
- "5160:5060/udp"
46+
- "5160:5060/tcp"
1347
volumes:
14-
- freepbx_data:/var/lib/freepbx
48+
- freepbx-s01-data:/var/lib/asterisk
49+
- freepbx-s01-logs:/var/log/asterisk
50+
networks:
51+
- freepbx-s01-net
1552
healthcheck:
16-
test: ["CMD-SHELL", "curl -sf http://localhost/health || exit 1"]
53+
test: ["CMD-SHELL", "curl -sf http://localhost/admin/config.php | grep -qi 'freepbx\\|asterisk\\|login\\|password' || exit 1"]
1754
interval: 30s
18-
timeout: 10s
19-
retries: 5
20-
start_period: 60s
21-
networks:
22-
- it-stack-net
23-
24-
networks:
25-
it-stack-net:
26-
driver: bridge
55+
timeout: 15s
56+
retries: 10
57+
start_period: 180s
2758

2859
volumes:
29-
freepbx_data:
60+
freepbx-s01-db-data:
61+
freepbx-s01-data:
62+
freepbx-s01-logs:
63+
64+
networks:
65+
freepbx-s01-net:
66+
driver: bridge

tests/labs/test-lab-10-01.sh

Lines changed: 69 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,81 @@
11
#!/usr/bin/env bash
2-
# test-lab-10-01.sh — Lab 10-01: Standalone
3-
# Module 10: FreePBX/Asterisk VoIP PBX
4-
# Basic freepbx functionality in complete isolation
2+
# test-lab-10-01.sh — FreePBX Lab 01: Standalone
3+
# Module 10 | Lab 01 | Tests: basic FreePBX/Asterisk functionality in isolation
54
set -euo pipefail
65

7-
LAB_ID="10-01"
8-
LAB_NAME="Standalone"
9-
MODULE="freepbx"
10-
COMPOSE_FILE="docker/docker-compose.standalone.yml"
11-
PASS=0
12-
FAIL=0
13-
14-
# ── Colors ────────────────────────────────────────────────────────────────────
15-
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
16-
CYAN='\033[0;36m'; NC='\033[0m'
17-
18-
pass() { echo -e "${GREEN}[PASS]${NC} $1"; ((PASS++)); }
19-
fail() { echo -e "${RED}[FAIL]${NC} $1"; ((FAIL++)); }
20-
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
21-
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
22-
23-
echo -e "${CYAN}======================================${NC}"
24-
echo -e "${CYAN} Lab ${LAB_ID}: ${LAB_NAME}${NC}"
25-
echo -e "${CYAN} Module: ${MODULE}${NC}"
26-
echo -e "${CYAN}======================================${NC}"
27-
echo ""
6+
COMPOSE_FILE="$(dirname "$0")/../docker/docker-compose.standalone.yml"
7+
CLEANUP=true
8+
for arg in "$@"; do [[ "$arg" == "--no-cleanup" ]] && CLEANUP=false; done
9+
10+
WEB_PORT=8301
11+
DB_PASS="RootLab01!"
12+
ADMIN_PASS="Admin01!"
13+
14+
PASS=0; FAIL=0
15+
pass() { echo "[PASS] $1"; ((PASS++)) || true; }
16+
fail() { echo "[FAIL] $1"; ((FAIL++)) || true; }
17+
section() { echo ""; echo "=== $1 ==="; }
18+
19+
cleanup() {
20+
if [[ "$CLEANUP" == "true" ]]; then
21+
echo "Cleaning up..."
22+
docker compose -f "$COMPOSE_FILE" down -v --remove-orphans 2>/dev/null || true
23+
fi
24+
}
25+
trap cleanup EXIT
26+
27+
section "Starting Lab 01 Standalone Stack"
28+
docker compose -f "$COMPOSE_FILE" up -d
29+
echo "Waiting for MariaDB and FreePBX to initialize (this may take 2-3 minutes)..."
30+
31+
section "MariaDB Health Check"
32+
for i in $(seq 1 30); do
33+
status=$(docker inspect freepbx-s01-db --format '{{.State.Health.Status}}' 2>/dev/null || echo "waiting")
34+
[[ "$status" == "healthy" ]] && break; sleep 5
35+
done
36+
[[ "$(docker inspect freepbx-s01-db --format '{{.State.Health.Status}}')" == "healthy" ]] && pass "MariaDB healthy" || fail "MariaDB not healthy"
37+
38+
section "FreePBX Web Health Check"
39+
for i in $(seq 1 60); do
40+
status=$(docker inspect freepbx-s01-app --format '{{.State.Health.Status}}' 2>/dev/null || echo "waiting")
41+
[[ "$status" == "healthy" ]] && break
42+
echo " Waiting for FreePBX ($i/60)..."
43+
sleep 10
44+
done
45+
[[ "$(docker inspect freepbx-s01-app --format '{{.State.Health.Status}}')" == "healthy" ]] && pass "FreePBX app healthy" || fail "FreePBX app not healthy"
46+
47+
section "FreePBX Web UI Check"
48+
http_code=$(curl -so /dev/null -w "%{http_code}" "http://localhost:${WEB_PORT}/admin/config.php" 2>/dev/null || echo "000")
49+
[[ "$http_code" =~ ^(200|302|401)$ ]] && pass "FreePBX admin page accessible (HTTP $http_code)" || fail "FreePBX admin page returned HTTP $http_code"
2850

29-
# ── PHASE 1: Setup ────────────────────────────────────────────────────────────
30-
info "Phase 1: Setup"
31-
docker compose -f "${COMPOSE_FILE}" up -d
32-
info "Waiting 30s for ${MODULE} to initialize..."
33-
sleep 30
51+
section "Database Connectivity"
52+
db_check=$(docker exec freepbx-s01-db mysql -u root -p"${DB_PASS}" -e "SHOW DATABASES;" 2>/dev/null | grep -c "asterisk" || echo 0)
53+
[[ "$db_check" -ge 1 ]] && pass "Asterisk database exists in MariaDB" || fail "Asterisk database not found"
3454

35-
# ── PHASE 2: Health Checks ────────────────────────────────────────────────────
36-
info "Phase 2: Health Checks"
55+
# Check asterisk DB has tables (FreePBX sets up via DB init)
56+
table_check=$(docker exec freepbx-s01-db mysql -u asterisk -p"AsteriskLab01!" asterisk -e "SHOW TABLES;" 2>/dev/null | wc -l || echo 0)
57+
[[ "$table_check" -gt 0 ]] && pass "Asterisk DB has tables (FreePBX initialized)" || fail "Asterisk DB has no tables yet"
3758

38-
if docker compose -f "${COMPOSE_FILE}" ps | grep -q "running\|Up"; then
39-
pass "Container is running"
40-
else
41-
fail "Container is not running"
42-
fi
59+
section "Container Configuration"
60+
restart_policy=$(docker inspect freepbx-s01-app --format '{{.HostConfig.RestartPolicy.Name}}')
61+
[[ "$restart_policy" == "unless-stopped" ]] && pass "Restart policy: unless-stopped" || fail "Unexpected restart policy: $restart_policy"
4362

44-
# ── PHASE 3: Functional Tests ─────────────────────────────────────────────────
45-
info "Phase 3: Functional Tests (Lab 01 — Standalone)"
63+
# Check environment variables
64+
admin_pass_set=$(docker inspect freepbx-s01-app --format '{{range .Config.Env}}{{println .}}{{end}}' | grep "ADMIN_PASSWORD" | cut -d= -f2)
65+
[[ "$admin_pass_set" == "${ADMIN_PASS}" ]] && pass "ADMIN_PASSWORD env var set correctly" || fail "ADMIN_PASSWORD not set correctly"
4666

47-
# TODO: Add module-specific functional tests here
48-
# Example:
49-
# if curl -sf http://localhost:5060/health > /dev/null 2>&1; then
50-
# pass "Health endpoint responds"
51-
# else
52-
# fail "Health endpoint not reachable"
53-
# fi
67+
db_host=$(docker inspect freepbx-s01-app --format '{{range .Config.Env}}{{println .}}{{end}}' | grep "^DB_HOST=" | cut -d= -f2)
68+
[[ "$db_host" == "freepbx-s01-db" ]] && pass "DB_HOST env var set correctly" || fail "DB_HOST not set (got: $db_host)"
5469

55-
warn "Functional tests for Lab 10-01 pending implementation"
70+
section "Named Volumes"
71+
docker volume ls | grep -q "freepbx-s01-db-data" && pass "Volume freepbx-s01-db-data exists" || fail "Volume freepbx-s01-db-data missing"
72+
docker volume ls | grep -q "freepbx-s01-data" && pass "Volume freepbx-s01-data exists" || fail "Volume freepbx-s01-data missing"
5673

57-
# ── PHASE 4: Cleanup ──────────────────────────────────────────────────────────
58-
info "Phase 4: Cleanup"
59-
docker compose -f "${COMPOSE_FILE}" down -v --remove-orphans
60-
info "Cleanup complete"
74+
section "Network"
75+
docker network ls | grep -q "freepbx-s01-net" && pass "Network freepbx-s01-net exists" || fail "Network freepbx-s01-net missing"
6176

62-
# ── Results ───────────────────────────────────────────────────────────────────
6377
echo ""
64-
echo -e "${CYAN}======================================${NC}"
65-
echo -e " Lab ${LAB_ID} Complete"
66-
echo -e " ${GREEN}PASS: ${PASS}${NC} | ${RED}FAIL: ${FAIL}${NC}"
67-
echo -e "${CYAN}======================================${NC}"
68-
69-
if [ "${FAIL}" -gt 0 ]; then
70-
exit 1
71-
fi
78+
echo "================================================"
79+
echo "Lab 01 Results: ${PASS} passed, ${FAIL} failed"
80+
echo "================================================"
81+
[[ $FAIL -eq 0 ]] && exit 0 || exit 1

0 commit comments

Comments
 (0)