Skip to content

Commit 994ce43

Browse files
committed
feat(lab-03): Odoo Advanced Features -- multi-worker, gevent longpolling :8331, resource limits
1 parent a3e0e26 commit 994ce43

File tree

3 files changed

+280
-57
lines changed

3 files changed

+280
-57
lines changed

.github/workflows/ci.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,33 @@ jobs:
126126
- name: Cleanup
127127
if: always()
128128
run: docker compose -f docker/docker-compose.lan.yml down -v
129+
130+
lab-03-smoke:
131+
name: Lab 03 -- Odoo Advanced Features (multi-worker + gevent longpolling)
132+
runs-on: ubuntu-latest
133+
needs: validate
134+
continue-on-error: true
135+
steps:
136+
- uses: actions/checkout@v4
137+
- name: Install tools
138+
run: sudo apt-get install -y curl postgresql-client redis-tools
139+
- name: Validate advanced compose
140+
run: docker compose -f docker/docker-compose.advanced.yml config -q && echo "Advanced compose valid"
141+
- name: Start advanced stack
142+
run: docker compose -f docker/docker-compose.advanced.yml up -d
143+
- name: Wait for PostgreSQL
144+
run: timeout 60 bash -c 'until docker exec odoo-a03-db pg_isready -U odoo -d odoo_lab03; do sleep 5; done'
145+
- name: Wait for Redis
146+
run: timeout 30 bash -c 'until docker exec odoo-a03-redis redis-cli ping | grep -q PONG; do sleep 3; done'
147+
- name: Wait for Mailhog
148+
run: timeout 60 bash -c 'until curl -sf http://localhost:8630/api/v2/messages; do sleep 5; done'
149+
- name: Wait for Odoo web
150+
run: timeout 180 bash -c 'until curl -sf http://localhost:8330/web/health | grep -q ok; do sleep 10; done'
151+
- name: Run Lab 13-03 test script
152+
run: bash tests/labs/test-lab-13-03.sh --no-cleanup
153+
- name: Collect logs on failure
154+
if: failure()
155+
run: docker compose -f docker/docker-compose.advanced.yml logs
156+
- name: Cleanup
157+
if: always()
158+
run: docker compose -f docker/docker-compose.advanced.yml down -v

docker/docker-compose.advanced.yml

Lines changed: 109 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,124 @@
1-
# Lab 03 — Advanced Features: odoo with TLS, resource limits, logging
2-
---
1+
# Lab 03 — Advanced Features
2+
# Module 13: Odoo ERP
3+
# External PostgreSQL + Redis + Mailhog + multi-worker (gevent longpolling)
4+
# Web UI: http://localhost:8330
5+
# Longpolling: http://localhost:8331 (gevent)
6+
# Mailhog: http://localhost:8630
7+
8+
name: it-stack-odoo-lab03
9+
310
services:
4-
odoo:
11+
# ── External Database ─────────────────────────────────────────────────────────
12+
odoo-a03-db:
13+
image: postgres:15-alpine
14+
container_name: odoo-a03-db
15+
restart: unless-stopped
16+
environment:
17+
POSTGRES_DB: odoo_lab03
18+
POSTGRES_USER: odoo
19+
POSTGRES_PASSWORD: OdooLab03!
20+
volumes:
21+
- odoo-a03-db-data:/var/lib/postgresql/data
22+
healthcheck:
23+
test: ["CMD-SHELL", "pg_isready -U odoo -d odoo_lab03"]
24+
interval: 10s
25+
timeout: 5s
26+
retries: 10
27+
start_period: 20s
28+
deploy:
29+
resources:
30+
limits:
31+
cpus: "0.5"
32+
memory: 512M
33+
networks:
34+
- odoo-a03-net
35+
36+
# ── Redis ─────────────────────────────────────────────────────────────────────
37+
odoo-a03-redis:
38+
image: redis:7-alpine
39+
container_name: odoo-a03-redis
40+
restart: unless-stopped
41+
command: redis-server --save 60 1 --loglevel warning --maxmemory 256mb --maxmemory-policy allkeys-lru
42+
volumes:
43+
- odoo-a03-redis-data:/data
44+
healthcheck:
45+
test: ["CMD", "redis-cli", "ping"]
46+
interval: 10s
47+
timeout: 3s
48+
retries: 5
49+
start_period: 10s
50+
deploy:
51+
resources:
52+
limits:
53+
cpus: "0.25"
54+
memory: 256M
55+
networks:
56+
- odoo-a03-net
57+
58+
# ── SMTP Relay ────────────────────────────────────────────────────────────────
59+
odoo-a03-mail:
60+
image: mailhog/mailhog:latest
61+
container_name: odoo-a03-mail
62+
restart: unless-stopped
63+
ports:
64+
- "8630:8025"
65+
networks:
66+
- odoo-a03-net
67+
68+
# ── Odoo Application (multi-worker + gevent longpoll) ─────────────────────────
69+
odoo-a03-app:
570
image: odoo:17
6-
container_name: it-stack-odoo
71+
container_name: odoo-a03-app
772
restart: unless-stopped
73+
depends_on:
74+
odoo-a03-db:
75+
condition: service_healthy
76+
odoo-a03-redis:
77+
condition: service_healthy
78+
odoo-a03-mail:
79+
condition: service_started
880
ports:
9-
- "8069:$firstPort"
81+
- "8330:8069"
82+
- "8331:8072"
1083
environment:
11-
- IT_STACK_ENV=lab-03-advanced
12-
- TLS_ENABLED=true
84+
HOST: odoo-a03-db
85+
PORT: "5432"
86+
USER: odoo
87+
PASSWORD: OdooLab03!
88+
command:
89+
- --db-filter=odoo_lab03
90+
- --workers=2
91+
- --max-cron-threads=1
92+
- --longpolling-port=8072
93+
- --limit-time-cpu=600
94+
- --limit-time-real=1200
95+
- --limit-memory-hard=1677721600
96+
- --limit-memory-soft=1342177280
97+
- --smtp-server=odoo-a03-mail
98+
- --smtp-port=1025
1399
volumes:
14-
- odoo_data:/var/lib/odoo
15-
- ./certs:/etc/ssl/certs:ro
100+
- odoo-a03-data:/var/lib/odoo
101+
- odoo-a03-addons:/mnt/extra-addons
102+
healthcheck:
103+
test: ["CMD-SHELL", "curl -sf http://localhost:8069/web/health | grep -q 'ok\\|pass'"]
104+
interval: 30s
105+
timeout: 10s
106+
retries: 10
107+
start_period: 60s
16108
deploy:
17109
resources:
18110
limits:
19-
cpus: "2.0"
20-
memory: G
21-
logging:
22-
driver: json-file
23-
options:
24-
max-size: "100m"
25-
max-file: "5"
111+
cpus: "1.5"
112+
memory: 2G
26113
networks:
27-
- it-stack-net
114+
- odoo-a03-net
28115

29116
networks:
30-
it-stack-net:
117+
odoo-a03-net:
31118
driver: bridge
32119

33120
volumes:
34-
odoo_data:
121+
odoo-a03-db-data:
122+
odoo-a03-redis-data:
123+
odoo-a03-data:
124+
odoo-a03-addons:

tests/labs/test-lab-13-03.sh

Lines changed: 141 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env bash
22
# test-lab-13-03.sh — Lab 13-03: Advanced Features
3-
# Module 13: Odoo ERP and business management
4-
# odoo with TLS, resource limits, and production-grade configuration
3+
# Module 13: Odoo ERP
4+
# Tests: multi-worker + gevent longpolling port + Redis + resource limits
55
set -euo pipefail
66

77
LAB_ID="13-03"
@@ -11,61 +11,164 @@ COMPOSE_FILE="docker/docker-compose.advanced.yml"
1111
PASS=0
1212
FAIL=0
1313

14-
# ── Colors ────────────────────────────────────────────────────────────────────
1514
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
1615
CYAN='\033[0;36m'; NC='\033[0m'
1716

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"; }
17+
pass() { echo -e "${GREEN}[PASS]${NC} $1"; ((PASS++)); }
18+
fail() { echo -e "${RED}[FAIL]${NC} $1"; ((FAIL++)); }
19+
section() { echo -e "\n${CYAN}── $1 ──${NC}"; }
20+
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
2221

23-
echo -e "${CYAN}======================================${NC}"
22+
CLEANUP=true
23+
[[ "${1:-}" == "--no-cleanup" ]] && CLEANUP=false
24+
25+
cleanup() {
26+
if [[ "${CLEANUP}" == "true" ]]; then
27+
info "Cleaning up..."
28+
docker compose -f "${COMPOSE_FILE}" down -v --remove-orphans 2>/dev/null || true
29+
fi
30+
}
31+
trap cleanup EXIT
32+
33+
echo -e "${CYAN}========================================${NC}"
2434
echo -e "${CYAN} Lab ${LAB_ID}: ${LAB_NAME}${NC}"
2535
echo -e "${CYAN} Module: ${MODULE}${NC}"
26-
echo -e "${CYAN}======================================${NC}"
27-
echo ""
36+
echo -e "${CYAN}========================================${NC}"
2837

2938
# ── PHASE 1: Setup ────────────────────────────────────────────────────────────
30-
info "Phase 1: Setup"
39+
section "Phase 1: Setup"
3140
docker compose -f "${COMPOSE_FILE}" up -d
32-
info "Waiting 30s for ${MODULE} to initialize..."
33-
sleep 30
3441

3542
# ── PHASE 2: Health Checks ────────────────────────────────────────────────────
36-
info "Phase 2: Health Checks"
43+
section "Phase 2: Health Checks"
44+
45+
info "Waiting for PostgreSQL (up to 60s)..."
46+
for i in $(seq 1 12); do
47+
if docker exec odoo-a03-db pg_isready -U odoo -d odoo_lab03 > /dev/null 2>&1; then
48+
pass "PostgreSQL healthy"; break
49+
fi
50+
[[ $i -eq 12 ]] && fail "PostgreSQL timed out"
51+
sleep 5
52+
done
3753

38-
if docker compose -f "${COMPOSE_FILE}" ps | grep -q "running\|Up"; then
39-
pass "Container is running"
54+
info "Waiting for Redis (up to 30s)..."
55+
for i in $(seq 1 6); do
56+
if docker exec odoo-a03-redis redis-cli ping 2>/dev/null | grep -q PONG; then
57+
pass "Redis healthy (PONG)"; break
58+
fi
59+
[[ $i -eq 6 ]] && fail "Redis not responding"
60+
sleep 5
61+
done
62+
63+
info "Waiting for Mailhog (up to 60s)..."
64+
for i in $(seq 1 12); do
65+
if curl -sf http://localhost:8630/api/v2/messages > /dev/null 2>&1; then
66+
pass "Mailhog reachable on :8630"; break
67+
fi
68+
[[ $i -eq 12 ]] && fail "Mailhog not reachable on :8630"
69+
sleep 5
70+
done
71+
72+
info "Waiting for Odoo web (up to 2 min)..."
73+
for i in $(seq 1 24); do
74+
if curl -sf http://localhost:8330/web/health 2>/dev/null | grep -q 'ok\|pass'; then
75+
pass "Odoo /web/health OK on :8330"; break
76+
fi
77+
[[ $i -eq 24 ]] && fail "Odoo not reachable on :8330"
78+
sleep 5
79+
done
80+
81+
# ── PHASE 3: Functional Tests ─────────────────────────────────────────────────
82+
section "Phase 3: Functional Tests"
83+
84+
# Containers
85+
for cname in odoo-a03-db odoo-a03-redis odoo-a03-mail odoo-a03-app; do
86+
if docker inspect "${cname}" --format '{{.State.Status}}' 2>/dev/null | grep -q running; then
87+
pass "${cname} running"
88+
else
89+
fail "${cname} not running"
90+
fi
91+
done
92+
93+
# Gevent longpolling port
94+
info "Waiting for Odoo gevent longpoll on :8331 (up to 60s)..."
95+
for i in $(seq 1 12); do
96+
if curl -sf http://localhost:8331/ > /dev/null 2>&1; then
97+
pass "Odoo gevent longpolling reachable on :8331"; break
98+
fi
99+
[[ $i -eq 12 ]] && fail "Odoo gevent longpolling not reachable on :8331"
100+
sleep 5
101+
done
102+
103+
# Multi-worker: multiple odoo processes
104+
WORKER_COUNT=$(docker exec odoo-a03-app pgrep -c 'python\|odoo' 2>/dev/null || echo 0)
105+
if [[ "${WORKER_COUNT:-0}" -ge 2 ]]; then
106+
pass "Odoo running ${WORKER_COUNT} worker processes"
40107
else
41-
fail "Container is not running"
108+
fail "Odoo has only ${WORKER_COUNT:-0} processes (expected ≥2 with --workers=2)"
42109
fi
43110

44-
# ── PHASE 3: Functional Tests ─────────────────────────────────────────────────
45-
info "Phase 3: Functional Tests (Lab 03 — Advanced Features)"
111+
# Redis connectivity from app
112+
if docker exec odoo-a03-app redis-cli -h odoo-a03-redis ping 2>/dev/null | grep -q PONG; then
113+
pass "App connects to Redis"
114+
else
115+
fail "App cannot reach Redis"
116+
fi
46117

47-
# TODO: Add module-specific functional tests here
48-
# Example:
49-
# if curl -sf http://localhost:8069/health > /dev/null 2>&1; then
50-
# pass "Health endpoint responds"
51-
# else
52-
# fail "Health endpoint not reachable"
53-
# fi
118+
# /web/health
119+
if curl -sf http://localhost:8330/web/health | grep -q 'ok\|pass'; then
120+
pass "Odoo /web/health endpoint OK"
121+
else
122+
fail "Odoo /web/health endpoint failed"
123+
fi
124+
125+
# JSON-RPC
126+
DB_LIST=$(curl -sf -X POST http://localhost:8330/web/database/get_list \
127+
-H 'Content-Type: application/json' \
128+
-d '{"jsonrpc":"2.0","method":"call","params":{}}' 2>/dev/null || echo "{}")
129+
if echo "${DB_LIST}" | grep -q 'result'; then
130+
pass "Odoo JSON-RPC /web/database/get_list OK"
131+
else
132+
fail "Odoo JSON-RPC not responding"
133+
fi
134+
135+
# Resource limits
136+
MEM_LIMIT=$(docker inspect odoo-a03-app --format '{{.HostConfig.Memory}}' 2>/dev/null || echo 0)
137+
if [[ "${MEM_LIMIT:-0}" -gt 0 ]]; then
138+
pass "Memory limit set on odoo-a03-app (${MEM_LIMIT} bytes)"
139+
else
140+
fail "No memory limit on odoo-a03-app"
141+
fi
54142

55-
warn "Functional tests for Lab 13-03 pending implementation"
143+
# CPU limit
144+
CPU_LIMIT=$(docker inspect odoo-a03-app --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo 0)
145+
if [[ "${CPU_LIMIT:-0}" -gt 0 ]]; then
146+
pass "CPU limit set on odoo-a03-app"
147+
else
148+
fail "No CPU limit on odoo-a03-app"
149+
fi
56150

57-
# ── PHASE 4: Cleanup ──────────────────────────────────────────────────────────
58-
info "Phase 4: Cleanup"
59-
docker compose -f "${COMPOSE_FILE}" down -v --remove-orphans
60-
info "Cleanup complete"
151+
# Mailhog API
152+
if curl -sf http://localhost:8630/api/v2/messages | grep -q 'total\|items'; then
153+
pass "Mailhog API valid"
154+
else
155+
fail "Mailhog API invalid"
156+
fi
157+
158+
# Volumes
159+
for vol in odoo-a03-db-data odoo-a03-redis-data odoo-a03-data odoo-a03-addons; do
160+
if docker volume ls --format '{{.Name}}' | grep -q "${vol}"; then
161+
pass "Volume ${vol} exists"
162+
else
163+
fail "Volume ${vol} missing"
164+
fi
165+
done
61166

62167
# ── Results ───────────────────────────────────────────────────────────────────
63168
echo ""
64-
echo -e "${CYAN}======================================${NC}"
65-
echo -e " Lab ${LAB_ID} Complete"
169+
echo -e "${CYAN}========================================${NC}"
170+
echo " Lab ${LAB_ID} Results"
66171
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
172+
echo -e "${CYAN}========================================${NC}"
173+
[[ "${FAIL}" -gt 0 ]] && exit 1
174+
exit 0

0 commit comments

Comments
 (0)