377 lines
No EOL
12 KiB
Markdown
377 lines
No EOL
12 KiB
Markdown
# Docker Compose Updater
|
|
|
|
A robust, production-ready Rust service that automatically updates Docker Compose image versions whilst preserving comments, formatting, and maintaining operational stability. The service operates on a configurable schedule and uses intelligent update strategies to ensure safe, conservative updates.
|
|
|
|
## Key Features
|
|
|
|
- 🔄 **Intelligent Updates**: Automatically updates Docker Compose files using configurable update strategies
|
|
- 📝 **Format Preservation**: Maintains all comments, whitespace, and YAML formatting perfectly
|
|
- 🎯 **Conservative Strategies**: Defaults to stable, tested versions rather than bleeding edge
|
|
- 🏥 **Health Monitoring**: Built-in health endpoints for monitoring and observability
|
|
- 🔐 **Multi-Registry Support**: Works with Docker Hub, GitHub Container Registry, GitLab, and custom registries
|
|
- 🔍 **Flexible Filtering**: Configurable patterns to ignore specific images or registries
|
|
- 🛡️ **Robust Error Handling**: Comprehensive error management with no silent failures
|
|
- 📋 **Prefix/Suffix Support**: Handles complex versioning schemes like `v1.2.3-alpine`, `release-1.0.0-slim`
|
|
- 🚀 **Container-Ready**: Designed for containerised environments with proper security
|
|
- 📊 **Comprehensive Testing**: Extensively tested with 44+ test cases covering edge cases
|
|
|
|
## Quick Start
|
|
|
|
### Using Docker Compose (Recommended)
|
|
|
|
1. **Create a configuration file** (`config.yaml`):
|
|
```yaml
|
|
# Paths to scan for Docker Compose files
|
|
compose_paths:
|
|
- "./compose-files"
|
|
- "./docker-compose.yml"
|
|
- "./services"
|
|
|
|
# Cron expression for update schedule
|
|
schedule: "0 0 2 * * *" # Daily at 2 AM UTC
|
|
|
|
# Update strategy (conservative by default)
|
|
update_strategy: "LatestPatchOfPreviousMinor"
|
|
|
|
# Runtime behaviour
|
|
dry_run: false # Set to true for testing
|
|
|
|
# Images to ignore during updates (substring matching)
|
|
ignore_images:
|
|
- "localhost"
|
|
- "127.0.0.1"
|
|
- "internal.company.com"
|
|
|
|
# Registry configurations
|
|
registries:
|
|
"docker.io":
|
|
url: "https://registry-1.docker.io"
|
|
|
|
"ghcr.io":
|
|
url: "https://ghcr.io"
|
|
auth_token: "${GITHUB_TOKEN}"
|
|
```
|
|
|
|
2. **Deploy with Docker Compose**:
|
|
```yaml
|
|
version: '3.8'
|
|
services:
|
|
docker-compose-updater:
|
|
image: ghcr.io/your-username/docker-compose-updater:latest
|
|
container_name: docker-compose-updater
|
|
restart: unless-stopped
|
|
environment:
|
|
- GITHUB_TOKEN=${GITHUB_TOKEN}
|
|
- RUST_LOG=info
|
|
volumes:
|
|
# Mount Docker socket (read-only for security)
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
# Mount compose files directory
|
|
- ./compose-files:/app/compose-files:rw
|
|
# Mount configuration
|
|
- ./config.yaml:/app/config/config.yaml:ro
|
|
ports:
|
|
- "8080:8080" # Health monitoring port
|
|
user: "1000:1000" # Run as non-root user
|
|
```
|
|
|
|
3. **Start the service**:
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
### Command Line Usage
|
|
|
|
```bash
|
|
# Install or build the binary
|
|
cargo build --release
|
|
|
|
# Run a one-time update
|
|
./target/release/docker-compose-updater --config ./config.yaml update
|
|
|
|
# Start the scheduled service with health monitoring
|
|
./target/release/docker-compose-updater --config ./config.yaml start
|
|
```
|
|
|
|
## Update Strategies
|
|
|
|
The service supports three intelligent update strategies, defaulting to the most conservative:
|
|
|
|
|
|
### `LatestPatchOfPreviousMinor`
|
|
- **Predictable**: Always updates to the latest patch of the previous minor version
|
|
- **Consistent Behaviour**: Never updates to the current latest minor
|
|
- **Example**: `1.5.3` → `1.4.8`
|
|
|
|
### `Latest`
|
|
- **Aggressive**: Updates to the absolute latest available version
|
|
- **Cutting Edge**: Useful for development environments
|
|
- **Example**: `1.5.3` → `2.1.0`
|
|
|
|
## Advanced Version Handling
|
|
|
|
The service handles complex versioning schemes intelligently:
|
|
|
|
### Supported Version Formats
|
|
- **Standard Semantic Versions**: `1.2.3`, `2.0.1`
|
|
- **Prefixed Versions**: `v1.2.3`, `release-1.0.0`, `build123-2.1.0`
|
|
- **Suffixed Versions**: `1.2.3-alpine`, `2.0.1-slim`, `1.5.0-ubuntu20.04`
|
|
- **Combined**: `v1.2.3-alpine-slim`, `release-2.0.1-final`
|
|
|
|
### Version Matching Logic
|
|
The service ensures that prefix and suffix combinations remain consistent:
|
|
- `v1.2.3-alpine` will only be updated to other `v*.*.*-alpine` versions
|
|
- `release-1.0.0` will only be updated to other `release-*.*.*` versions
|
|
- This prevents incompatible image variants from being selected
|
|
|
|
## Comprehensive Registry Support
|
|
|
|
### Supported Registries
|
|
- **Docker Hub**: `docker.io` (default registry)
|
|
- **GitHub Container Registry**: `ghcr.io`
|
|
- **GitLab Container Registry**: `registry.gitlab.com`
|
|
- **Google Container Registry**: `gcr.io`
|
|
- **Amazon ECR**: `*.dkr.ecr.*.amazonaws.com`
|
|
- **Azure Container Registry**: `*.azurecr.io`
|
|
- **Custom/Private Registries**: Any registry following Docker Registry API v2
|
|
|
|
### Registry Configuration
|
|
```yaml
|
|
registries:
|
|
"docker.io":
|
|
url: "https://registry-1.docker.io"
|
|
# Docker Hub typically doesn't require auth for public images
|
|
|
|
"ghcr.io":
|
|
url: "https://ghcr.io"
|
|
auth_token: "${GITHUB_TOKEN}"
|
|
|
|
"registry.gitlab.com":
|
|
url: "https://registry.gitlab.com"
|
|
auth_token: "${GITLAB_TOKEN}"
|
|
|
|
"your-registry.company.com":
|
|
url: "https://your-registry.company.com"
|
|
auth_token: "${COMPANY_REGISTRY_TOKEN}"
|
|
```
|
|
|
|
## Supported Image Formats
|
|
|
|
The updater handles various image naming conventions:
|
|
|
|
```yaml
|
|
# Standard Docker Hub images
|
|
nginx:1.21.0 → nginx:1.20.6
|
|
ubuntu:20.04 → ubuntu:18.04
|
|
|
|
# Namespaced images
|
|
bitnami/nginx:1.21.0 → bitnami/nginx:1.20.6
|
|
library/postgres:13.7 → library/postgres:12.11
|
|
|
|
# Custom registries
|
|
ghcr.io/user/app:v1.0.0 → ghcr.io/user/app:v0.9.5
|
|
registry.example.com/team/service:2.1.3 → registry.example.com/team/service:2.0.9
|
|
|
|
# Complex versioning
|
|
nginx:1.21.0-alpine → nginx:1.20.6-alpine
|
|
postgres:13.7-bullseye → postgres:12.11-bullseye
|
|
redis:v7.0.0-alpine3.16 → redis:v6.2.7-alpine3.16
|
|
|
|
# Local development (typically ignored)
|
|
localhost:5000/myapp:latest → [ignored by default]
|
|
127.0.0.1:8080/service:dev → [ignored by default]
|
|
```
|
|
|
|
## Health Monitoring & Observability
|
|
|
|
The service provides comprehensive monitoring capabilities:
|
|
|
|
### Health Endpoints
|
|
- **`GET /`**: Basic health check
|
|
- Returns `{"status":"healthy"}` when operational
|
|
- Returns `{"status":"unhealthy"}` during failures
|
|
- Includes basic service information
|
|
|
|
### Logging
|
|
```bash
|
|
# Configure logging level via environment variable
|
|
RUST_LOG=debug # For detailed debugging
|
|
RUST_LOG=info # For operational information (default)
|
|
RUST_LOG=warn # For warnings and errors only
|
|
RUST_LOG=error # For errors only
|
|
```
|
|
|
|
### Service Monitoring
|
|
The service reports:
|
|
- Successful update cycles with file counts
|
|
- Failed updates with detailed error information
|
|
- Registry connectivity status
|
|
- Configuration validation results
|
|
|
|
## Security Features
|
|
|
|
- **Non-Root Execution**: Designed to run as non-root user in containers
|
|
- **Minimal Attack Surface**: Alpine Linux base image with minimal packages
|
|
- **Read-Only Docker Socket**: Only requires read access to Docker socket
|
|
- **Secret Management**: All tokens provided via environment variables
|
|
- **Input Validation**: Comprehensive validation of all configuration inputs
|
|
- **Error Sanitisation**: No sensitive information leaked in error messages
|
|
|
|
## Development
|
|
|
|
### Building from Source
|
|
|
|
```bash
|
|
# Clone the repository
|
|
git clone https://github.com/your-username/docker-compose-updater.git
|
|
cd docker-compose-updater
|
|
|
|
# Build in release mode
|
|
cargo build --release
|
|
|
|
# Run comprehensive test suite
|
|
cargo test
|
|
|
|
# Run with custom configuration
|
|
./target/release/docker-compose-updater --config ./config.example.yaml start
|
|
```
|
|
|
|
### Testing
|
|
|
|
The project includes extensive testing:
|
|
|
|
```bash
|
|
# Run all tests (44+ test cases)
|
|
cargo test
|
|
|
|
# Run specific test suites
|
|
cargo test --test integration_tests # Integration tests
|
|
cargo test --test test_error_handling # Error handling tests
|
|
cargo test --test test_config_validation # Configuration tests
|
|
cargo test --test test_compose_operations # Compose file operations
|
|
|
|
# Run with output for debugging
|
|
cargo test -- --nocapture
|
|
|
|
# Generate coverage report (requires cargo-llvm-cov)
|
|
cargo llvm-cov --html
|
|
```
|
|
|
|
### Test Coverage
|
|
- **Error Handling**: Invalid configurations, malformed files, network failures
|
|
- **Config Validation**: All configuration formats, edge cases, defaults
|
|
- **Compose Operations**: Complex file formats, image parsing, version handling
|
|
- **Registry Integration**: Multiple registry types, authentication, failures
|
|
- **Version Logic**: All update strategies, complex versioning schemes
|
|
|
|
## Configuration Reference
|
|
|
|
### Complete Configuration Example
|
|
```yaml
|
|
# File/directory paths to scan for Docker Compose files
|
|
compose_paths:
|
|
- "/app/compose-files" # Directory to scan recursively
|
|
- "./docker-compose.yml" # Specific file
|
|
- "./services/" # Another directory
|
|
|
|
# Cron expression for update schedule (UTC timezone)
|
|
schedule: "0 0 2 * * *" # Daily at 2 AM
|
|
|
|
# Update strategy selection
|
|
update_strategy: "LatestPatchOfPreviousMinor"
|
|
|
|
# Prevent actual file modifications (for testing)
|
|
dry_run: false
|
|
|
|
# Images to ignore during updates (substring matching)
|
|
ignore_images:
|
|
- "localhost" # Local development images
|
|
- "127.0.0.1" # Local registry
|
|
- "internal.company.com" # Private internal registry
|
|
- "snapshot" # Snapshot versions
|
|
|
|
# Registry configurations
|
|
registries:
|
|
"docker.io":
|
|
url: "https://registry-1.docker.io"
|
|
# auth_token not typically needed for public images
|
|
|
|
"ghcr.io":
|
|
url: "https://ghcr.io"
|
|
auth_token: "${GITHUB_TOKEN}"
|
|
|
|
"registry.gitlab.com":
|
|
url: "https://registry.gitlab.com"
|
|
auth_token: "${GITLAB_TOKEN}"
|
|
```
|
|
|
|
### Environment Variables
|
|
- **`GITHUB_TOKEN`**: Personal access token for GitHub Container Registry
|
|
- **`GITLAB_TOKEN`**: Access token for GitLab Container Registry
|
|
- **`RUST_LOG`**: Logging level (`debug`, `info`, `warn`, `error`)
|
|
- **Registry-specific tokens**: For private registries
|
|
|
|
## Operational Considerations
|
|
|
|
### Backup Strategy
|
|
- Always backup your Docker Compose files before running updates
|
|
- Use version control (Git) to track changes
|
|
- Test with `dry_run: true` before enabling actual updates
|
|
|
|
### Monitoring
|
|
- Monitor the health endpoint for service availability
|
|
- Set up alerts for failed update cycles
|
|
- Review logs regularly for registry connectivity issues
|
|
|
|
### Resource Requirements
|
|
- **Memory**: ~10-50MB during operation
|
|
- **CPU**: Minimal during dormant periods, brief spikes during updates
|
|
- **Network**: Requires outbound HTTPS access to configured registries
|
|
- **Storage**: Minimal, only for configuration and temporary files
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
**Service fails to start**
|
|
- Check configuration file syntax with `yamllint`
|
|
- Verify file paths exist and are accessible
|
|
- Ensure cron expression is valid
|
|
|
|
**Registry authentication failures**
|
|
- Verify environment variables are set correctly
|
|
- Check token permissions for the target registry
|
|
- Test registry connectivity manually
|
|
|
|
**Updates not applied**
|
|
- Check if `dry_run` is enabled
|
|
- Verify images are not in the ignore list
|
|
- Ensure semantic versioning is used in image tags
|
|
|
|
## Contributing
|
|
|
|
1. Fork the repository
|
|
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
3. Write tests for your changes
|
|
4. Ensure all tests pass (`cargo test`)
|
|
5. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
6. Push to the branch (`git push origin feature/amazing-feature`)
|
|
7. Open a Pull Request
|
|
|
|
## Licence
|
|
|
|
This project is licensed under the MIT Licence - see the [LICENSE](LICENSE) file for details.
|
|
|
|
## Changelog
|
|
|
|
### v0.1.0
|
|
- Initial release with comprehensive functionality
|
|
- Multi-registry support (Docker Hub, GHCR, GitLab, custom)
|
|
- Intelligent update strategies with conservative defaults
|
|
- Comment and formatting preservation
|
|
- Comprehensive error handling (no silent failures)
|
|
- Health monitoring and observability
|
|
- Extensive test coverage (44+ test cases)
|
|
- Prefix/suffix version handling
|
|
- Robust configuration validation
|
|
- Production-ready security features |