Skip to content

Commit 0a7e386

Browse files
committed
feat(lab-01): SuiteCRM Standalone -- MariaDB + Bitnami SuiteCRM, real compose and test script
1 parent 202fd4a commit 0a7e386

3 files changed

Lines changed: 134 additions & 103 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 -- SuiteCRM Standalone (MariaDB + CRM)
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 suitecrm-s01-db mysqladmin ping -h localhost -u root -pRootLab01! > /dev/null 2>&1; do sleep 5; done'
89+
- name: Wait for SuiteCRM
90+
run: timeout 300 bash -c 'until curl -sf http://localhost:8302/index.php | grep -qi suitecrm; do sleep 10; done'
91+
- name: Run Lab 12-01 test script
92+
run: bash tests/labs/test-lab-12-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: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,64 @@
1-
# Lab 01 — Standalone: Complete SuiteCRM customer relationship management in isolation
2-
# No external dependencies required.
1+
# Lab 01 — Standalone: SuiteCRM CRM in complete isolation
2+
# Module 12 | No external dependencies required
33
---
44
services:
5-
suitecrm:
6-
image: bitnami/suitecrm:latest
7-
container_name: it-stack-suitecrm
5+
suitecrm-s01-db:
6+
image: mariadb:10.11
7+
container_name: suitecrm-s01-db
88
restart: unless-stopped
9-
ports:
10-
- "80:$firstPort"
119
environment:
12-
- IT_STACK_ENV=lab-01-standalone
10+
MYSQL_ROOT_PASSWORD: RootLab01!
11+
MYSQL_DATABASE: suitecrm
12+
MYSQL_USER: suitecrm
13+
MYSQL_PASSWORD: SuiteLab01!
1314
volumes:
14-
- suitecrm_data:/var/lib/suitecrm
15-
healthcheck:
16-
test: ["CMD-SHELL", "curl -sf http://localhost/health || exit 1"]
17-
interval: 30s
18-
timeout: 10s
19-
retries: 5
20-
start_period: 60s
15+
- suitecrm-s01-db-data:/var/lib/mysql
2116
networks:
22-
- it-stack-net
17+
- suitecrm-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
2324

24-
networks:
25-
it-stack-net:
26-
driver: bridge
25+
suitecrm-s01-app:
26+
image: bitnami/suitecrm:8
27+
container_name: suitecrm-s01-app
28+
restart: unless-stopped
29+
depends_on:
30+
suitecrm-s01-db:
31+
condition: service_healthy
32+
environment:
33+
SUITECRM_DATABASE_HOST: suitecrm-s01-db
34+
SUITECRM_DATABASE_PORT_NUMBER: "3306"
35+
SUITECRM_DATABASE_NAME: suitecrm
36+
SUITECRM_DATABASE_USER: suitecrm
37+
SUITECRM_DATABASE_PASSWORD: SuiteLab01!
38+
SUITECRM_USERNAME: admin
39+
SUITECRM_PASSWORD: Admin01!
40+
SUITECRM_EMAIL: admin@lab.local
41+
SUITECRM_HOST: localhost
42+
SUITECRM_SKIP_BOOTSTRAP: "no"
43+
ALLOW_EMPTY_PASSWORD: "no"
44+
SUITECRM_VALIDATE_CONFIGURATION: "yes"
45+
ports:
46+
- "8302:8080"
47+
volumes:
48+
- suitecrm-s01-data:/bitnami/suitecrm
49+
networks:
50+
- suitecrm-s01-net
51+
healthcheck:
52+
test: ["CMD-SHELL", "curl -sf http://localhost:8080/index.php | grep -qi 'suitecrm\\|SuiteCRM\\|login\\|username' || exit 1"]
53+
interval: 30s
54+
timeout: 15s
55+
retries: 10
56+
start_period: 120s
2757

2858
volumes:
29-
suitecrm_data:
59+
suitecrm-s01-db-data:
60+
suitecrm-s01-data:
61+
62+
networks:
63+
suitecrm-s01-net:
64+
driver: bridge

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

Lines changed: 67 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,82 @@
11
#!/usr/bin/env bash
2-
# test-lab-12-01.sh — Lab 12-01: Standalone
3-
# Module 12: SuiteCRM customer relationship management
4-
# Basic suitecrm functionality in complete isolation
2+
# test-lab-12-01.sh — SuiteCRM Lab 01: Standalone
3+
# Module 12 | Lab 01 | Tests: basic SuiteCRM CRM functionality in isolation
54
set -euo pipefail
65

7-
LAB_ID="12-01"
8-
LAB_NAME="Standalone"
9-
MODULE="suitecrm"
10-
COMPOSE_FILE="docker/docker-compose.standalone.yml"
11-
PASS=0
12-
FAIL=0
6+
COMPOSE_FILE="$(dirname "$0")/../docker/docker-compose.standalone.yml"
7+
CLEANUP=true
8+
for arg in "$@"; do [[ "$arg" == "--no-cleanup" ]] && CLEANUP=false; done
139

14-
# ── Colors ────────────────────────────────────────────────────────────────────
15-
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
16-
CYAN='\033[0;36m'; NC='\033[0m'
10+
WEB_PORT=8302
11+
DB_USER="suitecrm"
12+
DB_PASS="SuiteLab01!"
13+
ADMIN_USER="admin"
14+
ADMIN_PASS="Admin01!"
1715

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"; }
16+
PASS=0; FAIL=0
17+
pass() { echo "[PASS] $1"; ((PASS++)) || true; }
18+
fail() { echo "[FAIL] $1"; ((FAIL++)) || true; }
19+
section() { echo ""; echo "=== $1 ==="; }
2220

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 ""
21+
cleanup() {
22+
if [[ "$CLEANUP" == "true" ]]; then
23+
echo "Cleaning up..."
24+
docker compose -f "$COMPOSE_FILE" down -v --remove-orphans 2>/dev/null || true
25+
fi
26+
}
27+
trap cleanup EXIT
2828

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
29+
section "Starting Lab 01 Standalone Stack"
30+
docker compose -f "$COMPOSE_FILE" up -d
31+
echo "Waiting for MariaDB and SuiteCRM to initialize (may take 2-3 minutes)..."
3432

35-
# ── PHASE 2: Health Checks ────────────────────────────────────────────────────
36-
info "Phase 2: Health Checks"
33+
section "MariaDB Health Check"
34+
for i in $(seq 1 30); do
35+
status=$(docker inspect suitecrm-s01-db --format '{{.State.Health.Status}}' 2>/dev/null || echo "waiting")
36+
[[ "$status" == "healthy" ]] && break; sleep 5
37+
done
38+
[[ "$(docker inspect suitecrm-s01-db --format '{{.State.Health.Status}}')" == "healthy" ]] && pass "MariaDB healthy" || fail "MariaDB not healthy"
3739

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
40+
section "SuiteCRM App Health Check"
41+
for i in $(seq 1 60); do
42+
status=$(docker inspect suitecrm-s01-app --format '{{.State.Health.Status}}' 2>/dev/null || echo "waiting")
43+
[[ "$status" == "healthy" ]] && break
44+
echo " Waiting for SuiteCRM ($i/60)..."
45+
sleep 10
46+
done
47+
[[ "$(docker inspect suitecrm-s01-app --format '{{.State.Health.Status}}')" == "healthy" ]] && pass "SuiteCRM app healthy" || fail "SuiteCRM app not healthy"
4348

44-
# ── PHASE 3: Functional Tests ─────────────────────────────────────────────────
45-
info "Phase 3: Functional Tests (Lab 01 — Standalone)"
49+
section "SuiteCRM Web UI"
50+
http_code=$(curl -so /dev/null -w "%{http_code}" -L "http://localhost:${WEB_PORT}/index.php" 2>/dev/null || echo "000")
51+
[[ "$http_code" =~ ^(200|302)$ ]] && pass "SuiteCRM login page accessible (HTTP $http_code)" || fail "SuiteCRM login page returned HTTP $http_code"
4652

47-
# TODO: Add module-specific functional tests here
48-
# Example:
49-
# if curl -sf http://localhost:80/health > /dev/null 2>&1; then
50-
# pass "Health endpoint responds"
51-
# else
52-
# fail "Health endpoint not reachable"
53-
# fi
53+
# Verify login page contains expected content
54+
curl -sf -L "http://localhost:${WEB_PORT}/index.php" 2>/dev/null | grep -qi "suitecrm\|login\|username\|password" && pass "SuiteCRM login page content OK" || fail "SuiteCRM login page content unexpected"
5455

55-
warn "Functional tests for Lab 12-01 pending implementation"
56+
section "SuiteCRM REST API"
57+
login_response=$(curl -sf -c /tmp/suitecrm-cookies.txt -X POST "http://localhost:${WEB_PORT}/index.php?module=Users&action=Authenticate" \
58+
-d "user_name=${ADMIN_USER}&user_password=$(echo -n ${ADMIN_PASS} | md5sum | awk '{print $1}')" \
59+
-w "\n%{http_code}" 2>/dev/null || echo "000")
60+
login_code=$(echo "$login_response" | tail -1)
61+
[[ "$login_code" =~ ^(200|302)$ ]] && pass "SuiteCRM auth endpoint accessible (HTTP $login_code)" || fail "SuiteCRM auth endpoint HTTP $login_code"
5662

57-
# ── PHASE 4: Cleanup ──────────────────────────────────────────────────────────
58-
info "Phase 4: Cleanup"
59-
docker compose -f "${COMPOSE_FILE}" down -v --remove-orphans
60-
info "Cleanup complete"
63+
section "Database Checks"
64+
db_tables=$(docker exec suitecrm-s01-db mysql -u "${DB_USER}" -p"${DB_PASS}" suitecrm -e "SHOW TABLES;" 2>/dev/null | wc -l || echo 0)
65+
[[ "$db_tables" -gt 10 ]] && pass "SuiteCRM DB has tables (count: $db_tables)" || fail "SuiteCRM DB seems empty (count: $db_tables)"
6166

62-
# ── Results ───────────────────────────────────────────────────────────────────
63-
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}"
67+
section "Container Configuration"
68+
restart_policy=$(docker inspect suitecrm-s01-app --format '{{.HostConfig.RestartPolicy.Name}}')
69+
[[ "$restart_policy" == "unless-stopped" ]] && pass "Restart policy: unless-stopped" || fail "Unexpected restart policy: $restart_policy"
70+
71+
db_host=$(docker inspect suitecrm-s01-app --format '{{range .Config.Env}}{{println .}}{{end}}' | grep "^SUITECRM_DATABASE_HOST=" | cut -d= -f2)
72+
[[ "$db_host" == "suitecrm-s01-db" ]] && pass "SUITECRM_DATABASE_HOST env set correctly" || fail "SUITECRM_DATABASE_HOST not set (got: $db_host)"
6873

69-
if [ "${FAIL}" -gt 0 ]; then
70-
exit 1
71-
fi
74+
section "Named Volumes"
75+
docker volume ls | grep -q "suitecrm-s01-db-data" && pass "Volume suitecrm-s01-db-data exists" || fail "Volume suitecrm-s01-db-data missing"
76+
docker volume ls | grep -q "suitecrm-s01-data" && pass "Volume suitecrm-s01-data exists" || fail "Volume suitecrm-s01-data missing"
77+
78+
echo ""
79+
echo "================================================"
80+
echo "Lab 01 Results: ${PASS} passed, ${FAIL} failed"
81+
echo "================================================"
82+
[[ $FAIL -eq 0 ]] && exit 0 || exit 1

0 commit comments

Comments
 (0)