feat: kompose hooks

This commit is contained in:
2025-10-11 10:24:25 +02:00
parent bb9e8ac603
commit 2efc30d8f9
5 changed files with 1095 additions and 50 deletions

View File

@@ -3,9 +3,11 @@ title: Hooks System
description: Extend Kompose with custom hooks
---
Extend Kompose functionality with custom hooks for each stack.
Extend Kompose functionality with custom hooks for each stack. Hooks allow you to run custom logic before and after Docker Compose commands and database operations.
### Available Hooks
## Hook Types
### Database Hooks
| Hook | Timing | Arguments | Use Case |
|------|--------|-----------|----------|
@@ -14,76 +16,431 @@ Extend Kompose functionality with custom hooks for each stack.
| `hook_pre_db_import` | Before DB import | `$1` = dump file path | Prepare environment, schema setup |
| `hook_post_db_import` | After DB import | `$1` = dump file path | Restart services, clear caches |
### Creating Hooks
### Docker Compose Command Hooks
Create `<stack>/hooks.sh`:
All Docker Compose commands support pre and post hooks. The hooks receive the full command arguments.
| Hook | Timing | Arguments | Use Case |
|------|--------|-----------|----------|
| `hook_pre_up` | Before `docker compose up` | `$@` = command args | Pre-flight checks, setup |
| `hook_post_up` | After `docker compose up` | `$@` = command args | Health checks, initialization |
| `hook_pre_down` | Before `docker compose down` | `$@` = command args | Graceful shutdown, backups |
| `hook_post_down` | After `docker compose down` | `$@` = command args | Cleanup, notifications |
| `hook_pre_start` | Before `docker compose start` | `$@` = command args | Validate state |
| `hook_post_start` | After `docker compose start` | `$@` = command args | Verify services |
| `hook_pre_stop` | Before `docker compose stop` | `$@` = command args | Save state |
| `hook_post_stop` | After `docker compose stop` | `$@` = command args | Cleanup |
| `hook_pre_restart` | Before `docker compose restart` | `$@` = command args | Prepare for restart |
| `hook_post_restart` | After `docker compose restart` | `$@` = command args | Verify restart |
| `hook_pre_build` | Before `docker compose build` | `$@` = command args | Code generation |
| `hook_post_build` | After `docker compose build` | `$@` = command args | Tag images, push to registry |
| `hook_pre_pull` | Before `docker compose pull` | `$@` = command args | Check registry availability |
| `hook_post_pull` | After `docker compose pull` | `$@` = command args | Verify images |
| `hook_pre_logs` | Before `docker compose logs` | `$@` = command args | Setup log filters |
| `hook_post_logs` | After `docker compose logs` | `$@` = command args | Process logs |
**Also supported:** `ps`, `exec`, `run`, `create`, `kill`, `pause`, `unpause`, `port`, `top`
::alert{type="info"}
**Note:** Post-command hooks only execute if the Docker Compose command succeeds (exit code 0).
::
## Creating Hooks
Create `<stack>/hooks.sh` in your stack directory:
```bash
#!/usr/bin/env bash
# Export schema before database export
hook_pre_db_export() {
echo " Exporting application schema..."
docker exec sexy_api npx directus schema snapshot --yes ./schema.yaml
# Hook naming convention:
# - hook_pre_<command> - runs before the command
# - hook_post_<command> - runs after the command (only on success)
# Example: Setup before starting containers
hook_pre_up() {
local -a args=("$@")
echo " Running pre-flight checks..."
# Check if required directories exist
if [[ ! -d "./uploads" ]]; then
echo " Creating uploads directory..."
mkdir -p ./uploads
fi
return 0 # 0 = success, 1 = failure
}
# Apply schema before database import
hook_pre_db_import() {
local dump_file="$1"
echo " Applying schema snapshot..."
docker exec sexy_api npx directus schema apply --yes ./schema.yaml
# Example: Verify services after startup
hook_post_up() {
local -a args=("$@")
echo " Waiting for services to be healthy..."
# Wait for health check
sleep 5
if docker compose ps | grep -q "healthy"; then
echo " ✓ Services are healthy"
return 0
else
echo " ⚠ Warning: Some services may not be healthy"
return 0 # Non-critical, don't fail
fi
}
# Example: Backup before shutdown
hook_pre_down() {
echo " Creating backup before shutdown..."
./backup.sh
return 0
}
# Restart service after import
hook_post_db_import() {
local dump_file="$1"
echo " Restarting application..."
docker restart sexy_api
# Example: Restart dependent services
hook_post_restart() {
echo " Restarting dependent services..."
# Restart a related service that depends on this stack
return 0
}
```
### Real-World Example: Directus (sexy stack)
## Real-World Examples
The `sexy` stack uses hooks for Directus schema management:
### Example 1: Directus Schema Management (sexy stack)
**Export Flow:**
1. `pre_db_export`: Export Directus schema snapshot
2. Database export creates SQL dump
3. Result: Both database dump + schema snapshot
**Import Flow:**
1. `pre_db_import`: Apply Directus schema from snapshot
2. Database import loads SQL dump
3. `post_db_import`: Restart Directus container
4. Result: Fully synchronized schema + data
### Testing Hooks
The `sexy` stack uses hooks for Directus schema synchronization:
```bash
# Preview hook execution
#!/usr/bin/env bash
# Export Directus schema before database export
hook_pre_db_export() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local snapshot_file="directus_schema_${timestamp}.yaml"
echo " Exporting Directus schema snapshot..."
if docker exec sexy_api npx directus schema snapshot "$snapshot_file" > /dev/null 2>&1; then
echo " Schema snapshot saved: $snapshot_file"
return 0
else
echo " Warning: Could not export schema snapshot"
return 0 # Don't fail the entire export
fi
}
# Import Directus schema before database import
hook_pre_db_import() {
local dump_file="$1"
# Find most recent schema snapshot
local snapshot_file=$(ls -t directus_schema_*.yaml 2>/dev/null | head -1)
if [[ -z "$snapshot_file" ]]; then
echo " No schema snapshot found, skipping"
return 0
fi
echo " Applying Directus schema from: $snapshot_file"
if docker exec sexy_api npx directus schema apply "$snapshot_file" > /dev/null 2>&1; then
echo " Schema applied successfully"
return 0
else
echo " Warning: Could not apply schema snapshot"
return 0
fi
}
# Reload Directus after import
hook_post_db_import() {
echo " Restarting Directus to apply changes..."
docker restart sexy_api > /dev/null 2>&1
return 0
}
```
### Example 2: Health Monitoring
```bash
#!/usr/bin/env bash
# Send notification when services start
hook_post_up() {
local -a args=("$@")
echo " Notifying monitoring system..."
# Example: Send webhook
curl -X POST https://monitoring.example.com/webhook \
-H "Content-Type: application/json" \
-d "{\"stack\": \"${stack}\", \"status\": \"up\"}" \
2>/dev/null
return 0
}
# Alert on shutdown
hook_post_down() {
echo " Sending shutdown notification..."
curl -X POST https://monitoring.example.com/webhook \
-H "Content-Type: application/json" \
-d "{\"stack\": \"${stack}\", \"status\": \"down\"}" \
2>/dev/null
return 0
}
```
### Example 3: Automated Backups
```bash
#!/usr/bin/env bash
# Backup volumes before rebuild
hook_pre_build() {
echo " Backing up volumes before rebuild..."
local backup_dir="./backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup_dir"
# Backup volumes
docker run --rm \
-v app_data:/data \
-v "$backup_dir:/backup" \
alpine tar czf /backup/data.tar.gz -C /data .
echo " Backup saved to: $backup_dir"
return 0
}
# Tag and push after successful build
hook_post_build() {
echo " Tagging and pushing image..."
local image="${COMPOSE_PROJECT_NAME}_app"
local tag="$(date +%Y%m%d-%H%M%S)"
docker tag "$image:latest" "$image:$tag"
docker push "$image:$tag"
return 0
}
```
### Example 4: Cache Warming
```bash
#!/usr/bin/env bash
# Warm caches after starting
hook_post_up() {
echo " Warming application caches..."
# Wait for app to be ready
sleep 10
# Hit cache endpoints
curl -s http://localhost:3000/api/cache/warm > /dev/null
return 0
}
# Clear caches before restart
hook_pre_restart() {
echo " Clearing application caches..."
docker exec app_container redis-cli FLUSHALL
return 0
}
```
## Hook Execution Flow
### Database Export Flow
```
1. hook_pre_db_export
2. pg_dump (database export)
3. hook_post_db_export (receives dump file path)
```
### Database Import Flow
```
1. hook_pre_db_import (receives dump file path)
2. Database drop & recreate
3. psql (database import)
4. hook_post_db_import (receives dump file path)
```
### Docker Compose Command Flow
```
1. hook_pre_<command> (receives command args)
2. docker compose <command>
3. hook_post_<command> (only if command succeeds, receives command args)
```
## Testing Hooks
```bash
# Preview hook execution with dry-run
./kompose.sh sexy db:export --dry-run
./kompose.sh sexy up -d --dry-run
# Execute with hooks
./kompose.sh sexy db:export
./kompose.sh sexy up -d
# Import with hooks
./kompose.sh sexy db:import
# Test specific commands with hooks
./kompose.sh news restart
./kompose.sh blog build
./kompose.sh "*" down
```
### Hook Best Practices
## Accessing Command Arguments
**DO:**
- Return 0 for success, 1 for failure
- Use indented output: `echo " Message"`
- Make non-critical operations return 0
- Check container status before `docker exec`
- Test in dry-run mode first
Hooks receive the full command and its arguments:
**DON'T:**
- Assume containers are running
- Use blocking operations without timeouts
- Forget error handling
- Hardcode paths or credentials
```bash
hook_pre_up() {
local -a args=("$@")
# Access specific arguments
echo " Command: ${args[0]}" # "up"
echo " All args: ${args[*]}" # "up -d --build"
# Check for specific flags
if [[ " ${args[*]} " =~ " --build " ]]; then
echo " Build flag detected, running pre-build tasks..."
fi
return 0
}
```
## Hook Best Practices
### ✅ DO:
- **Return proper exit codes**: `0` for success, `1` for failure
- **Use indented output**: `echo " Message"` for better readability
- **Make non-critical operations non-blocking**: Return `0` even on minor failures
- **Check container status** before using `docker exec`
- **Test in dry-run mode** first: `./kompose.sh stack command --dry-run`
- **Use environment variables** available from `.env` files
- **Add timeouts** for long-running operations
- **Document your hooks** with comments
### ❌ DON'T:
- **Assume containers are running** before executing commands
- **Use blocking operations** without timeouts
- **Forget error handling** for external commands
- **Hardcode paths, credentials, or hostnames**
- **Perform destructive operations** in `pre_*` hooks without confirmation
- **Make hooks too complex** - consider separate scripts if needed
- **Ignore exit codes** from important operations
## Available Environment Variables
Hooks have access to all environment variables from:
- Root `.env` file
- Stack-specific `.env` file
- CLI overrides (`-e KEY=VALUE`)
Common variables:
```bash
$SCRIPT_DIR # Root kompose directory
$stack # Current stack name
$COMPOSE_PROJECT_NAME
$DB_NAME
$DB_HOST
$DB_PORT
$DB_USER
$DB_PASSWORD
# ... all variables from .env files
```
## Troubleshooting
### Hook not executing
1. Verify `hooks.sh` has execute permissions: `chmod +x <stack>/hooks.sh`
2. Check function naming: `hook_pre_<command>` or `hook_post_<command>`
3. Test with dry-run mode to see if hooks are detected
4. Check for bash syntax errors: `bash -n <stack>/hooks.sh`
### Hook failing
1. Add `set -x` at the top of your hook for debugging
2. Check return codes: `return 0` for success
3. Verify container names and states
4. Check environment variables are loaded
5. Look at Kompose output for error messages
### Best debugging approach
```bash
# Enable dry-run to see what would execute
./kompose.sh stack command --dry-run
# Add debug output in your hook
hook_pre_up() {
echo " [DEBUG] Stack: $stack"
echo " [DEBUG] Args: $*"
echo " [DEBUG] Container: $(docker ps --filter name=$stack)"
# ... your hook logic
return 0
}
```
## Advanced Patterns
### Conditional Execution
```bash
hook_pre_up() {
# Only run in production
if [[ "${ENVIRONMENT}" == "production" ]]; then
echo " Running production pre-flight checks..."
./production-checks.sh
fi
return 0
}
```
### Parallel Hook Execution
```bash
hook_post_up() {
echo " Running parallel initialization tasks..."
# Background jobs
(cache_warm) &
(index_rebuild) &
# Wait for all background jobs
wait
return 0
}
```
### Hook Chaining
```bash
hook_post_restart() {
# Call another stack's command
echo " Restarting dependent services..."
"${SCRIPT_DIR}/kompose.sh" dependent-stack restart
return 0
}
```
## Security Considerations
- Never log sensitive information (passwords, tokens)
- Validate user input in hooks that accept parameters
- Use quotes around variables to prevent injection
- Restrict file permissions on `hooks.sh`: `chmod 750 hooks.sh`
- Avoid executing user-supplied data without validation
- Use absolute paths or `$SCRIPT_DIR` for file references
## Related
- [Database Operations](/guide/database) - Learn about database import/export
- [Stack Management](/guide/stack-management) - Managing multiple stacks
- [Configuration](/guide/configuration) - Environment variables and settings