Skip to content

Commit 775b8b5

Browse files
committed
feat(lab-02): Nextcloud LAN -- PG+Redis external deps, pgsql backend+Redis config verified
1 parent 6ae870d commit 775b8b5

3 files changed

Lines changed: 240 additions & 48 deletions

File tree

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,40 @@ jobs:
103103
- name: Cleanup
104104
if: always()
105105
run: docker compose -f docker/docker-compose.standalone.yml down -v
106+
107+
lab-02-smoke:
108+
name: Lab 02 — Nextcloud LAN (PG, Redis, pgsql backend, Redis config)
109+
runs-on: ubuntu-latest
110+
needs: validate
111+
continue-on-error: true
112+
steps:
113+
- uses: actions/checkout@v4
114+
115+
- name: Install tools
116+
run: sudo apt-get install -y curl
117+
118+
- name: Validate LAN compose
119+
run: docker compose -f docker/docker-compose.lan.yml config -q && echo "LAN compose valid"
120+
121+
- name: Start LAN stack
122+
run: docker compose -f docker/docker-compose.lan.yml up -d
123+
124+
- name: Wait for PostgreSQL
125+
run: timeout 60 bash -c 'until docker compose -f docker/docker-compose.lan.yml exec -T db pg_isready -U ncuser -d nextcloud; do sleep 3; done'
126+
127+
- name: Wait for Redis
128+
run: timeout 30 bash -c 'until docker compose -f docker/docker-compose.lan.yml exec -T redis redis-cli -a Lab02Redis! ping | grep -q PONG; do sleep 2; done'
129+
130+
- name: Wait for Nextcloud (PG first-boot ~3 min)
131+
run: timeout 360 bash -c 'until curl -sf http://localhost:8080/status.php | grep -q "\"installed\":true"; do sleep 10; done'
132+
133+
- name: Run Lab 06-02 test script
134+
run: bash tests/labs/test-lab-06-02.sh
135+
136+
- name: Collect logs on failure
137+
if: failure()
138+
run: docker compose -f docker/docker-compose.lan.yml logs
139+
140+
- name: Cleanup
141+
if: always()
142+
run: docker compose -f docker/docker-compose.lan.yml down -v

docker/docker-compose.lan.yml

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,99 @@
1-
# Lab 02 — External Dependencies: nextcloud with external PostgreSQL and Redis
2-
---
1+
# =============================================================================
2+
# IT-Stack — Nextcloud — Lab 02: External Dependencies
3+
# =============================================================================
4+
# Two separate networks simulate a real LAN where the database tier lives on
5+
# a dedicated server. nc-db-net is the private data plane; nc-app-net is the
6+
# user-facing app plane.
7+
#
8+
# Services:
9+
# db — PostgreSQL 16 (nc-db-net only)
10+
# redis — Redis 7 with password (nc-db-net only)
11+
# nextcloud — Nextcloud 29 Apache (bridged, port 8080)
12+
#
13+
# Usage:
14+
# docker compose -f docker/docker-compose.lan.yml up -d
15+
# docker compose -f docker/docker-compose.lan.yml down -v
16+
# =============================================================================
17+
18+
networks:
19+
nc-app-net:
20+
driver: bridge
21+
nc-db-net:
22+
driver: bridge
23+
24+
volumes:
25+
nextcloud-lan-data:
26+
nextcloud-lan-db:
27+
nextcloud-lan-redis:
28+
329
services:
4-
nextcloud:
5-
image: nextcloud:28-apache
6-
container_name: it-stack-nextcloud
7-
restart: unless-stopped
8-
ports:
9-
- "80:$firstPort"
10-
environment:
11-
- IT_STACK_ENV=lab-02-lan
12-
- DB_HOST=
13-
- DB_PORT=5432
14-
- REDIS_HOST=
15-
networks:
16-
- it-stack-net
1730

18-
# Lightweight local DB for lab (replace with lab-db1 in real env)
19-
postgres:
20-
image: postgres:16
21-
container_name: it-stack-nextcloud-db
31+
db:
32+
image: postgres:16-alpine
33+
container_name: nc-lan-db
2234
environment:
23-
POSTGRES_DB: nextcloud_db
24-
POSTGRES_USER: nextcloud_user
25-
POSTGRES_PASSWORD: nextcloud_pass
35+
POSTGRES_DB: nextcloud
36+
POSTGRES_USER: ncuser
37+
POSTGRES_PASSWORD: Lab02Password!
2638
volumes:
27-
- nextcloud_pg_data:/var/lib/postgresql/data
39+
- nextcloud-lan-db:/var/lib/postgresql/data
2840
networks:
29-
- it-stack-net
41+
- nc-db-net
42+
healthcheck:
43+
test: ["CMD-SHELL", "pg_isready -U ncuser -d nextcloud"]
44+
interval: 10s
45+
timeout: 5s
46+
retries: 5
47+
restart: unless-stopped
3048

31-
networks:
32-
it-stack-net:
33-
driver: bridge
49+
redis:
50+
image: redis:7-alpine
51+
container_name: nc-lan-redis
52+
command: redis-server --requirepass Lab02Redis!
53+
volumes:
54+
- nextcloud-lan-redis:/data
55+
networks:
56+
- nc-db-net
57+
healthcheck:
58+
test: ["CMD", "redis-cli", "-a", "Lab02Redis!", "ping"]
59+
interval: 10s
60+
timeout: 5s
61+
retries: 5
62+
restart: unless-stopped
3463

35-
volumes:
36-
nextcloud_pg_data:
64+
nextcloud:
65+
image: nextcloud:29-apache
66+
container_name: nc-lan-app
67+
ports:
68+
- "8080:80"
69+
environment:
70+
# PostgreSQL — replaces SQLite from Lab 01
71+
POSTGRES_HOST: db
72+
POSTGRES_DB: nextcloud
73+
POSTGRES_USER: ncuser
74+
POSTGRES_PASSWORD: Lab02Password!
75+
# Admin credentials
76+
NEXTCLOUD_ADMIN_USER: admin
77+
NEXTCLOUD_ADMIN_PASSWORD: Lab02Password!
78+
NEXTCLOUD_TRUSTED_DOMAINS: "localhost 127.0.0.1"
79+
# Redis — file locking + session cache
80+
REDIS_HOST: redis
81+
REDIS_HOST_PASSWORD: Lab02Redis!
82+
REDIS_HOST_PORT: "6379"
83+
volumes:
84+
- nextcloud-lan-data:/var/www/html
85+
networks:
86+
- nc-app-net
87+
- nc-db-net
88+
depends_on:
89+
db:
90+
condition: service_healthy
91+
redis:
92+
condition: service_healthy
93+
healthcheck:
94+
test: ["CMD-SHELL", "curl -sf http://localhost/status.php | grep -q '\"installed\":true'"]
95+
interval: 30s
96+
timeout: 10s
97+
retries: 10
98+
start_period: 120s
99+
restart: unless-stopped

tests/labs/test-lab-06-02.sh

Lines changed: 111 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env bash
1+
#!/usr/bin/env bash
22
# test-lab-06-02.sh — Lab 06-02: External Dependencies
33
# Module 06: Nextcloud file sync, calendar, and office suite
44
# nextcloud with external PostgreSQL, Redis, and network integration
@@ -29,43 +29,135 @@ echo ""
2929
# ── PHASE 1: Setup ────────────────────────────────────────────────────────────
3030
info "Phase 1: Setup"
3131
docker compose -f "${COMPOSE_FILE}" up -d
32-
info "Waiting 30s for ${MODULE} to initialize..."
33-
sleep 30
32+
info "Waiting for PostgreSQL..."
33+
timeout 60 bash -c 'until docker compose -f docker/docker-compose.lan.yml exec -T db pg_isready -U ncuser -d nextcloud 2>/dev/null; do sleep 3; done'
34+
info "Waiting for Redis..."
35+
timeout 30 bash -c 'until docker compose -f docker/docker-compose.lan.yml exec -T redis redis-cli -a Lab02Redis! ping 2>/dev/null | grep -q PONG; do sleep 2; done'
36+
info "Waiting for Nextcloud (PostgreSQL first-boot ~3 min)..."
37+
timeout 360 bash -c 'until curl -sf http://localhost:8080/status.php | grep -q "\"installed\":true"; do sleep 10; done'
3438

35-
# ── PHASE 2: Health Checks ───────────────────────────────────────────────────
39+
# ── PHASE 2: Health Checks ───────────────────────────────────────────────────
3640
info "Phase 2: Health Checks"
3741

38-
if docker compose -f "${COMPOSE_FILE}" ps | grep -q "running\|Up"; then
39-
pass "Container is running"
42+
for c in nc-lan-db nc-lan-redis nc-lan-app; do
43+
if docker ps --filter "name=^/${c}$" --filter "status=running" --format '{{.Names}}' | grep -q "${c}"; then
44+
pass "Container ${c} is running"
45+
else
46+
fail "Container ${c} is not running"
47+
fi
48+
done
49+
50+
if docker compose -f "${COMPOSE_FILE}" exec -T db pg_isready -U ncuser -d nextcloud 2>/dev/null; then
51+
pass "PostgreSQL: pg_isready OK"
52+
else
53+
fail "PostgreSQL: pg_isready failed"
54+
fi
55+
56+
if docker compose -f "${COMPOSE_FILE}" exec -T redis redis-cli -a Lab02Redis! ping 2>/dev/null | grep -q PONG; then
57+
pass "Redis: PING → PONG"
4058
else
41-
fail "Container is not running"
59+
fail "Redis: no PONG response"
4260
fi
4361

44-
# ── PHASE 3: Functional Tests ─────────────────────────────────────────────────
62+
if curl -sf http://localhost:8080/status.php | grep -q '"installed":true'; then
63+
pass "Nextcloud: status.php installed=true"
64+
else
65+
fail "Nextcloud: status.php not installed"
66+
fi
67+
68+
# ── PHASE 3: Functional Tests ────────────────────────────────────────────────
4569
info "Phase 3: Functional Tests (Lab 02 — External Dependencies)"
4670

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
71+
# Key Lab 02 test: database backend must be PostgreSQL, not SQLite
72+
DB_TYPE=$(docker compose -f "${COMPOSE_FILE}" exec -T nextcloud \
73+
php /var/www/html/occ config:system:get dbtype 2>/dev/null | tr -d '[:space:]' || echo "unknown")
74+
if echo "${DB_TYPE}" | grep -qiE '^pgsql|^postgresql'; then
75+
pass "DB backend: ${DB_TYPE} (PostgreSQL confirmed, not SQLite)"
76+
else
77+
fail "DB backend: expected pgsql, got '${DB_TYPE}'"
78+
fi
79+
80+
# Key Lab 02 test: Redis must be configured in config.php
81+
if docker compose -f "${COMPOSE_FILE}" exec -T nextcloud \
82+
cat /var/www/html/config/config.php 2>/dev/null | grep -q "'redis'"; then
83+
pass "Redis section present in Nextcloud config.php"
84+
else
85+
fail "Redis not configured in config.php"
86+
fi
87+
88+
# DB host is 'db' (external, not localhost)
89+
DB_HOST=$(docker compose -f "${COMPOSE_FILE}" exec -T nextcloud \
90+
php /var/www/html/occ config:system:get dbhost 2>/dev/null | tr -d '[:space:]' || echo "")
91+
if [ "${DB_HOST}" = "db" ]; then
92+
pass "DB host: '${DB_HOST}' (external service)"
93+
else
94+
fail "DB host: expected 'db', got '${DB_HOST}'"
95+
fi
96+
97+
# Maintenance mode off
98+
MAINTENANCE=$(docker compose -f "${COMPOSE_FILE}" exec -T nextcloud \
99+
php /var/www/html/occ config:system:get maintenance 2>/dev/null | tr -d '[:space:]' || echo "false")
100+
if [ "${MAINTENANCE}" = "false" ]; then
101+
pass "Maintenance mode: disabled"
102+
else
103+
fail "Maintenance mode: enabled (unexpected)"
104+
fi
105+
106+
# occ status
107+
if docker compose -f "${COMPOSE_FILE}" exec -T nextcloud \
108+
php /var/www/html/occ status 2>/dev/null | grep -qi 'installed.*true'; then
109+
pass "occ status: installed=true"
110+
else
111+
fail "occ status: not installed"
112+
fi
113+
114+
# Admin user in DB
115+
if docker compose -f "${COMPOSE_FILE}" exec -T nextcloud \
116+
php /var/www/html/occ user:list 2>/dev/null | grep -q admin; then
117+
pass "occ user:list: admin user present"
118+
else
119+
fail "occ user:list: admin user missing"
120+
fi
54121

55-
warn "Functional tests for Lab 06-02 pending implementation"
122+
# WebDAV PROPFIND
123+
if curl -sf -u admin:Lab02Password! -X PROPFIND \
124+
http://localhost:8080/remote.php/dav/ | grep -qi "multistatus"; then
125+
pass "WebDAV PROPFIND: multistatus response"
126+
else
127+
fail "WebDAV PROPFIND: no multistatus"
128+
fi
129+
130+
# OCS Capabilities API
131+
if curl -sf -u admin:Lab02Password! \
132+
'http://localhost:8080/ocs/v1.php/cloud/capabilities?format=json' \
133+
| grep -q '"status":"ok"'; then
134+
pass "OCS Capabilities API: status OK"
135+
else
136+
fail "OCS Capabilities API: unexpected response"
137+
fi
138+
139+
# DB integrity
140+
info "Running occ db:add-missing-indices..."
141+
IDX_OUT=$(docker compose -f "${COMPOSE_FILE}" exec -T nextcloud \
142+
php /var/www/html/occ db:add-missing-indices --no-interaction 2>&1 || true)
143+
if echo "${IDX_OUT}" | grep -qvi "error"; then
144+
pass "occ db:add-missing-indices: no errors"
145+
else
146+
warn "occ db:add-missing-indices: ${IDX_OUT}"
147+
fi
56148

57-
# ── PHASE 4: Cleanup ─────────────────────────────────────────────────────────
149+
# ── PHASE 4: Cleanup ─────────────────────────────────────────────────────────
58150
info "Phase 4: Cleanup"
59151
docker compose -f "${COMPOSE_FILE}" down -v --remove-orphans
60152
info "Cleanup complete"
61153

62-
# ── Results ──────────────────────────────────────────────────────────────────
154+
# ── Results ──────────────────────────────────────────────────────────────────
63155
echo ""
64156
echo -e "${CYAN}======================================${NC}"
65157
echo -e " Lab ${LAB_ID} Complete"
66158
echo -e " ${GREEN}PASS: ${PASS}${NC} | ${RED}FAIL: ${FAIL}${NC}"
67159
echo -e "${CYAN}======================================${NC}"
68160

69161
if [ "${FAIL}" -gt 0 ]; then
70-
exit 1
162+
exit 1
71163
fi

0 commit comments

Comments
 (0)