370 lines
9.7 KiB
Markdown
370 lines
9.7 KiB
Markdown
# Quick Reference: Packs Volume Architecture
|
|
|
|
## TL;DR
|
|
|
|
**Packs are NOT copied into Docker images. They are mounted as volumes.**
|
|
|
|
```bash
|
|
# Build pack binaries (one-time or when updated)
|
|
./scripts/build-pack-binaries.sh
|
|
|
|
# Start services - init-packs copies packs to volume
|
|
docker compose up -d
|
|
|
|
# Update pack files - no image rebuild needed!
|
|
vim packs/core/actions/my_action.yaml
|
|
docker compose restart
|
|
```
|
|
|
|
## Architecture Overview
|
|
|
|
```
|
|
Host Filesystem Docker Volumes Service Containers
|
|
───────────────── ─────────────── ──────────────────
|
|
|
|
./packs/
|
|
├── core/
|
|
│ ├── actions/
|
|
│ ├── sensors/
|
|
│ └── pack.yaml
|
|
│
|
|
│ ┌─────────────┐
|
|
│ (copy during │ packs_data │──────────> /opt/attune/packs (api)
|
|
│ init-packs) │ volume │
|
|
│ └────────────>│ │──────────> /opt/attune/packs (executor)
|
|
│ │ │
|
|
│ │ │──────────> /opt/attune/packs (worker)
|
|
│ │ │
|
|
│ │ │──────────> /opt/attune/packs (sensor)
|
|
│ └─────────────┘
|
|
│
|
|
./packs.dev/
|
|
└── custom-pack/ ┌────────────────────────> /opt/attune/packs.dev (all)
|
|
(bind mount) │ (read-write for dev)
|
|
│
|
|
└─ (mounted directly)
|
|
```
|
|
|
|
## Why Volumes Instead of COPY?
|
|
|
|
| Aspect | COPY into Image | Volume Mount |
|
|
|--------|----------------|--------------|
|
|
| **Update packs** | Rebuild image (~5 min) | Restart service (~5 sec) |
|
|
| **Image size** | Larger (+packs) | Smaller (no packs) |
|
|
| **Development** | Slow iteration | Fast iteration |
|
|
| **Consistency** | Each service separate | All services share |
|
|
| **Pack binaries** | Baked into image | Updateable |
|
|
|
|
## docker-compose.yaml Configuration
|
|
|
|
```yaml
|
|
volumes:
|
|
packs_data:
|
|
driver: local
|
|
|
|
services:
|
|
# Step 1: init-packs runs once to populate packs_data volume
|
|
init-packs:
|
|
image: python:3.11-alpine
|
|
volumes:
|
|
- ./packs:/source/packs:ro # Host packs (read-only)
|
|
- packs_data:/opt/attune/packs # Target volume
|
|
command: ["/bin/sh", "/init-packs.sh"]
|
|
restart: on-failure
|
|
|
|
# Step 2: Services mount packs_data as read-only
|
|
api:
|
|
volumes:
|
|
- packs_data:/opt/attune/packs:ro # Production packs (RO)
|
|
- ./packs.dev:/opt/attune/packs.dev:rw # Dev packs (RW)
|
|
depends_on:
|
|
init-packs:
|
|
condition: service_completed_successfully
|
|
|
|
worker-shell:
|
|
volumes:
|
|
- packs_data:/opt/attune/packs:ro # Same volume
|
|
- ./packs.dev:/opt/attune/packs.dev:rw
|
|
|
|
# ... all services follow same pattern
|
|
```
|
|
|
|
## Pack Binaries (Native Code)
|
|
|
|
Some packs contain compiled binaries (e.g., sensors written in Rust).
|
|
|
|
### Building Pack Binaries
|
|
|
|
**Option 1: Use the script (recommended)**
|
|
```bash
|
|
./scripts/build-pack-binaries.sh
|
|
```
|
|
|
|
**Option 2: Manual build**
|
|
```bash
|
|
# Build in Docker with GLIBC compatibility
|
|
docker build -f docker/Dockerfile.pack-binaries -t attune-pack-builder .
|
|
|
|
# Extract binaries
|
|
docker create --name pack-tmp attune-pack-builder
|
|
docker cp pack-tmp:/pack-binaries/. ./packs/
|
|
docker rm pack-tmp
|
|
```
|
|
|
|
**Option 3: Native build (if GLIBC matches)**
|
|
```bash
|
|
cargo build --release --bin attune-core-timer-sensor
|
|
cp target/release/attune-core-timer-sensor packs/core/sensors/
|
|
```
|
|
|
|
### When to Rebuild Pack Binaries
|
|
|
|
- ✅ After `git pull` that updates pack binary source
|
|
- ✅ After modifying sensor source code (e.g., `crates/core-timer-sensor`)
|
|
- ✅ When setting up development environment for first time
|
|
- ❌ NOT needed for YAML/script changes in packs
|
|
|
|
## Development Workflow
|
|
|
|
### Editing Pack YAML Files
|
|
|
|
```bash
|
|
# 1. Edit pack files
|
|
vim packs/core/actions/echo.yaml
|
|
|
|
# 2. Restart services (no rebuild!)
|
|
docker compose restart
|
|
|
|
# 3. Test changes
|
|
curl -X POST http://localhost:8080/api/v1/executions \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-d '{"action_ref": "core.echo", "parameters": {"message": "hello"}}'
|
|
```
|
|
|
|
**Time**: ~5 seconds
|
|
|
|
### Editing Pack Scripts (Python/Shell)
|
|
|
|
```bash
|
|
# 1. Edit script
|
|
vim packs/core/actions/http_request.py
|
|
|
|
# 2. Restart services
|
|
docker compose restart worker-python
|
|
|
|
# 3. Test
|
|
# (run execution)
|
|
```
|
|
|
|
**Time**: ~5 seconds
|
|
|
|
### Editing Pack Binaries (Native Sensors)
|
|
|
|
```bash
|
|
# 1. Edit source
|
|
vim crates/core-timer-sensor/src/main.rs
|
|
|
|
# 2. Rebuild binary
|
|
./scripts/build-pack-binaries.sh
|
|
|
|
# 3. Restart services
|
|
docker compose restart sensor
|
|
|
|
# 4. Test
|
|
# (check sensor registration)
|
|
```
|
|
|
|
**Time**: ~2 minutes (compile + restart)
|
|
|
|
## Development Packs (packs.dev)
|
|
|
|
For rapid development, use the `packs.dev` directory:
|
|
|
|
```bash
|
|
# Create a dev pack
|
|
mkdir -p packs.dev/mypack/actions
|
|
|
|
# Create action
|
|
cat > packs.dev/mypack/actions/test.yaml <<EOF
|
|
name: test
|
|
description: Test action
|
|
runner_type: Shell
|
|
entry_point: echo.sh
|
|
parameters:
|
|
message:
|
|
type: string
|
|
required: true
|
|
EOF
|
|
|
|
cat > packs.dev/mypack/actions/echo.sh <<'EOF'
|
|
#!/bin/bash
|
|
echo "Message: $ATTUNE_MESSAGE"
|
|
EOF
|
|
|
|
chmod +x packs.dev/mypack/actions/echo.sh
|
|
|
|
# Restart to pick up changes
|
|
docker compose restart
|
|
|
|
# Test immediately - no rebuild needed!
|
|
```
|
|
|
|
**Benefits of packs.dev**:
|
|
- ✅ Direct bind mount (changes visible immediately)
|
|
- ✅ Read-write access (can modify from container)
|
|
- ✅ No init-packs step needed
|
|
- ✅ Perfect for iteration
|
|
|
|
## Optimized Dockerfiles and Packs
|
|
|
|
The optimized Dockerfiles (`docker/Dockerfile.optimized`) do NOT copy packs:
|
|
|
|
```dockerfile
|
|
# ❌ OLD: Packs copied into image
|
|
COPY packs/ ./packs/
|
|
|
|
# ✅ NEW: Only create mount point
|
|
RUN mkdir -p /opt/attune/packs /opt/attune/logs
|
|
|
|
# Packs mounted at runtime from packs_data volume
|
|
```
|
|
|
|
**Result**:
|
|
- Service images contain only binaries + configs
|
|
- Packs updated independently
|
|
- Faster builds (no pack layer invalidation)
|
|
|
|
## Troubleshooting
|
|
|
|
### "Pack not found" errors
|
|
|
|
**Symptom**: API returns 404 for pack/action
|
|
**Cause**: Packs not loaded into volume
|
|
|
|
**Fix**:
|
|
```bash
|
|
# Check if packs exist in volume
|
|
docker compose exec api ls -la /opt/attune/packs/
|
|
|
|
# If empty, restart init-packs
|
|
docker compose restart init-packs
|
|
docker compose logs init-packs
|
|
```
|
|
|
|
### Pack changes not visible
|
|
|
|
**Symptom**: Updated pack.yaml but changes not reflected
|
|
**Cause**: Changes made to host `./packs/` after init-packs ran
|
|
|
|
**Fix**:
|
|
```bash
|
|
# Option 1: Use packs.dev for development
|
|
mv packs/mypack packs.dev/mypack
|
|
docker compose restart
|
|
|
|
# Option 2: Recreate packs_data volume
|
|
docker compose down
|
|
docker volume rm attune_packs_data
|
|
docker compose up -d
|
|
```
|
|
|
|
### Pack binary "exec format error"
|
|
|
|
**Symptom**: Sensor binary fails with exec format error
|
|
**Cause**: Binary compiled for wrong architecture or GLIBC version
|
|
|
|
**Fix**:
|
|
```bash
|
|
# Rebuild with Docker (ensures compatibility)
|
|
./scripts/build-pack-binaries.sh
|
|
|
|
# Restart sensor service
|
|
docker compose restart sensor
|
|
```
|
|
|
|
### Pack binary "permission denied"
|
|
|
|
**Symptom**: Binary exists but can't execute
|
|
**Cause**: Binary not executable
|
|
|
|
**Fix**:
|
|
```bash
|
|
chmod +x packs/core/sensors/attune-core-timer-sensor
|
|
docker compose restart init-packs sensor
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### DO:
|
|
- ✅ Use `./scripts/build-pack-binaries.sh` for pack binaries
|
|
- ✅ Put development packs in `packs.dev/`
|
|
- ✅ Keep production packs in `packs/`
|
|
- ✅ Commit pack YAML/scripts to git
|
|
- ✅ Use `.gitignore` for compiled pack binaries
|
|
- ✅ Restart services after pack changes
|
|
- ✅ Use `init-packs` logs to debug loading issues
|
|
|
|
### DON'T:
|
|
- ❌ Don't copy packs into Dockerfiles
|
|
- ❌ Don't edit packs inside running containers
|
|
- ❌ Don't commit compiled pack binaries to git
|
|
- ❌ Don't expect instant updates to `packs/` (need restart)
|
|
- ❌ Don't rebuild service images for pack changes
|
|
- ❌ Don't modify packs_data volume directly
|
|
|
|
## Migration from Old Dockerfiles
|
|
|
|
If your old Dockerfiles copied packs:
|
|
|
|
```dockerfile
|
|
# OLD Dockerfile
|
|
COPY packs/ ./packs/
|
|
COPY --from=pack-builder /build/pack-binaries/ ./packs/
|
|
```
|
|
|
|
**Migration steps**:
|
|
|
|
1. **Build pack binaries separately**:
|
|
```bash
|
|
./scripts/build-pack-binaries.sh
|
|
```
|
|
|
|
2. **Update to optimized Dockerfile**:
|
|
```yaml
|
|
# docker-compose.yaml
|
|
api:
|
|
build:
|
|
dockerfile: docker/Dockerfile.optimized
|
|
```
|
|
|
|
3. **Rebuild service images**:
|
|
```bash
|
|
docker compose build --no-cache
|
|
```
|
|
|
|
4. **Start services** (init-packs will populate volume):
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
## Summary
|
|
|
|
**Architecture**: Packs → Volume → Services
|
|
- Host `./packs/` copied to `packs_data` volume by `init-packs`
|
|
- Services mount `packs_data` as read-only
|
|
- Dev packs in `packs.dev/` bind-mounted directly
|
|
|
|
**Benefits**:
|
|
- 90% faster pack updates (restart vs rebuild)
|
|
- Smaller service images
|
|
- Consistent packs across all services
|
|
- Clear separation: services = code, packs = content
|
|
|
|
**Key Commands**:
|
|
```bash
|
|
./scripts/build-pack-binaries.sh # Build native pack binaries
|
|
docker compose restart # Pick up pack changes
|
|
docker compose logs init-packs # Debug pack loading
|
|
```
|
|
|
|
**Remember**: Packs are content, not code. Treat them as configuration, not part of the service image. |