diff --git a/Cargo.toml b/Cargo.toml index 50dde037..04e141c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,5 +68,6 @@ uuid = { version = "1.0", features = [ "v4", "serde" ] } email_address = "0.2.9" [dev-dependencies] +regex = "1.0" rstest = "0.26" tracing-test = "0.2" diff --git a/docs/ai-training/README.md b/docs/ai-training/README.md new file mode 100644 index 00000000..e22832eb --- /dev/null +++ b/docs/ai-training/README.md @@ -0,0 +1,514 @@ +# AI Agent Training Materials + +This directory contains resources to help AI agents guide users through creating Torrust Tracker environment configurations. + +## Contents + +- **[questionnaire.md](questionnaire.md)** - Structured decision tree for gathering user requirements +- **[dataset/environment-configs/](dataset/environment-configs/)** - 15 pre-configured environment examples demonstrating common deployment scenarios +- **[dataset/rendered-templates/](dataset/rendered-templates/)** - Rendered deployment artifacts for all examples (complete input/output pairs) + +## Purpose + +These materials enable AI agents to: + +1. **Guide users through configuration decisions** using the questionnaire's structured approach +2. **Recommend appropriate starting configurations** based on user requirements +3. **Explain configuration tradeoffs** between different deployment options +4. **Help users customize** example configurations for their specific needs + +## Dataset Structure: Input/Output Pairs + +This directory contains a **complete AI training dataset** with both inputs and outputs: + +### Input: Environment Configurations + +- Location: [`dataset/environment-configs/`](dataset/environment-configs/) +- Format: JSON configuration files +- Content: High-level deployment requirements (provider, database, domains, etc.) + +### Output: Rendered Deployment Artifacts + +- Location: [`dataset/rendered-templates/`](dataset/rendered-templates/) +- Format: Rendered templates (Ansible, Docker Compose, configuration files) +- Content: Concrete artifacts ready for deployment + +### The Deployer as a Function + +```text +config.json → [Deployer] → Rendered Templates + (IN) (OUT) +``` + +The deployer transforms: + +- **FROM**: Desired infrastructure state in custom domain language +- **TO**: Executable deployment artifacts + +### Example Mapping + +| Input (Config) | Output (Artifacts) | +| ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| [`dataset/environment-configs/01-minimal-lxd.json`](dataset/environment-configs/01-minimal-lxd.json) | [`dataset/rendered-templates/01-minimal-lxd/`](dataset/rendered-templates/01-minimal-lxd/) | +| SQLite + UDP + HTTP + LXD | Ansible playbooks, tracker.toml, docker-compose, etc. | + +**Benefits for AI Agents:** + +- **Few-shot learning**: Full input/output examples show transformation patterns +- **Pattern recognition**: See how config changes affect rendered templates +- **Diff analysis**: Compare outputs to understand configuration impact +- **Template understanding**: Learn the structure of deployment artifacts + +**Regenerating Outputs**: Run `./scripts/generate-ai-training-outputs.sh` to update artifacts when templates change. + +## Using the Questionnaire + +The [questionnaire.md](questionnaire.md) provides a systematic approach to gather user requirements across 14 key areas: + +1. **Deployment Goal** - Understand use case (production, development, testing) +2. **Provider Selection** - Choose between LXD (local) or Hetzner (cloud) +3. **Database Configuration** - Select SQLite3 or MySQL based on needs +4. **Tracker Protocol** - Configure UDP, HTTP, or both +5. **HTTPS/TLS** - Determine if encryption is needed +6. **Monitoring** - Decide on Prometheus + Grafana integration +7. **Backup Strategy** - Configure automated backups if needed +8. **Advanced Features** - Private tracker mode, custom domains, etc. + +### Questionnaire Workflow + +```mermaid +graph TD + A[Start] --> B[Understand Deployment Goal] + B --> C[Select Provider] + C --> D[Choose Database] + D --> E[Configure Protocols] + E --> F[HTTPS Requirements?] + F --> G[Monitoring Needed?] + G --> H[Backup Strategy?] + H --> I[Review Configuration] + I --> J[Recommend Example] + J --> K[Guide Customization] +``` + +**AI Agent Instructions:** + +1. Work through sections sequentially +2. Ask clarifying questions when user needs are unclear +3. Explain tradeoffs (e.g., SQLite vs MySQL, LXD vs Hetzner) +4. Recommend specific examples based on collected requirements +5. Help users customize the example for their specific needs + +## Example Configurations + +### Core Examples (01-05) + +Fundamental deployment patterns demonstrating basic configurations: + +| Example | Name | Provider | Database | HTTPS | Monitoring | Backups | Use Case | +| ------- | ---------------------------------------------------------------------------- | -------- | -------- | ----- | ---------- | ------- | ------------------------------------------ | +| 01 | [minimal-lxd](dataset/environment-configs/01-minimal-lxd.json) | LXD | SQLite3 | ✗ | ✗ | ✗ | Quick local testing, learning basics | +| 02 | [full-stack-lxd](dataset/environment-configs/02-full-stack-lxd.json) | LXD | MySQL | ✓ | ✓ | ✓ | Complete local setup, testing all features | +| 03 | [minimal-hetzner](dataset/environment-configs/03-minimal-hetzner.json) | Hetzner | SQLite3 | ✗ | ✗ | ✗ | Simple cloud deployment, proof of concept | +| 04 | [full-stack-hetzner](dataset/environment-configs/04-full-stack-hetzner.json) | Hetzner | MySQL | ✓ | ✓ | ✓ | Production-ready cloud deployment | +| 05 | [mysql-development](dataset/environment-configs/05-mysql-development.json) | LXD | MySQL | ✗ | ✗ | ✗ | Database-focused development | + +### Extended Examples (06-15) + +Specialized configurations for specific requirements: + +| Example | Name | Provider | Database | HTTPS | Monitoring | Backups | Use Case | +| ------- | ------------------------------------------------------------------------------------ | -------- | -------- | ----- | ---------- | ------- | ------------------------------------------- | +| 06 | [production-https](dataset/environment-configs/06-production-https.json) | LXD | MySQL | ✓ | ✓ | ✗ | Secure production without backup complexity | +| 07 | [udp-only](dataset/environment-configs/07-udp-only.json) | LXD | SQLite3 | ✗ | ✗ | ✗ | UDP tracker testing, minimal footprint | +| 08 | [http-only-https](dataset/environment-configs/08-http-only-https.json) | LXD | MySQL | ✓ | ✓ | ✗ | HTTP API-focused, secure web interface | +| 09 | [monitoring-stack](dataset/environment-configs/09-monitoring-stack.json) | LXD | MySQL | ✗ | ✓ | ✗ | Monitoring development, dashboard testing | +| 10 | [multi-domain](dataset/environment-configs/10-multi-domain.json) | LXD | MySQL | ✓ | ✓ | ✗ | Multiple domain architecture | +| 11 | [private-tracker](dataset/environment-configs/11-private-tracker.json) | LXD | MySQL | ✓ | ✓ | ✗ | Private tracker with user whitelisting | +| 12 | [high-availability](dataset/environment-configs/12-high-availability.json) | Hetzner | MySQL | ✓ | ✓ | ✓ | Mission-critical production | +| 13 | [backup-focused](dataset/environment-configs/13-backup-focused.json) | LXD | MySQL | ✗ | ✓ | ✓ | Data protection priority (4hr backups) | +| 14 | [lightweight-production](dataset/environment-configs/14-lightweight-production.json) | Hetzner | SQLite3 | ✓ | ✗ | ✗ | Cost-effective production | +| 15 | [sqlite-monitoring](dataset/environment-configs/15-sqlite-monitoring.json) | LXD | SQLite3 | ✗ | ✓ | ✗ | Simple database + full monitoring | + +## Scenario-to-Example Mapping + +Use this guide to recommend examples based on user scenarios: + +### "I want to quickly test the tracker locally" + +→ **Example 01** (minimal-lxd): Fastest setup, minimal dependencies + +### "I need a production tracker in the cloud" + +→ **Example 04** (full-stack-hetzner): Complete production features +→ **Example 12** (high-availability): Mission-critical with backups + +### "I'm developing monitoring dashboards" + +→ **Example 09** (monitoring-stack): Full Prometheus + Grafana without HTTPS complexity + +### "I need HTTPS for my tracker API" + +→ **Example 06** (production-https): HTTPS-focused without backup overhead +→ **Example 08** (http-only-https): HTTP API-only with TLS + +### "I want to test only UDP tracker functionality" + +→ **Example 07** (udp-only): Isolated UDP tracker testing + +### "I need a private tracker with user control" + +→ **Example 11** (private-tracker): Private mode with user whitelisting + +### "I'm on a tight budget but need cloud hosting" + +→ **Example 14** (lightweight-production): Smallest Hetzner server, minimal features + +### "Data backups are my top priority" + +→ **Example 13** (backup-focused): Aggressive backup strategy (every 4 hours, 14-day retention) + +### "I want to learn all features locally" + +→ **Example 02** (full-stack-lxd): Complete feature set on LXD + +### "I need to serve multiple domains" + +→ **Example 10** (multi-domain): Separate domains for tracker and Grafana + +## Configuration Customization Guide + +After selecting an example, guide users through common customizations: + +### 1. Environment Name + +```json +{ + "environment": { + "name": "my-custom-tracker", // Change this + "description": "My production tracker" + } +} +``` + +### 2. SSH Credentials + +```json +{ + "ssh_credentials": { + "private_key_path": "/home/username/.ssh/id_rsa", // User's actual key path + "public_key_path": "/home/username/.ssh/id_rsa.pub" + } +} +``` + +### 3. Provider Configuration + +**For LXD:** + +```json +{ + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-custom" // Customize profile name + } +} +``` + +**For Hetzner:** + +```json +{ + "provider": { + "provider": "hetzner", + "api_token": "$HETZNER_API_TOKEN", // Environment variable or actual token + "server_type": "cx21", // Adjust size: cx11, cx21, cx31, cx41 + "location": "nbg1", // Or: fsn1, hel1, ash + "image": "ubuntu-24.04" + } +} +``` + +### 4. Database Configuration + +**SQLite3:** + +```json +{ + "database": { + "driver": "sqlite3", + "database_name": "tracker.db" // Filename only + } +} +``` + +**MySQL:** + +```json +{ + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "ChangeThisPassword!" // Use strong password + } +} +``` + +### 5. Tracker Protocols + +**Enable/Disable UDP:** + +```json +{ + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" // Standard BitTorrent port + } + ] +} +``` + +**Enable/Disable HTTP:** + +```json +{ + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" // Custom HTTP port + } + ] +} +``` + +### 6. HTTPS/TLS Configuration + +**Enable HTTPS with Caddy:** + +```json +{ + "caddy": { + "domain": "tracker.yourdomain.com", // Your domain + "email": "admin@yourdomain.com", // Let's Encrypt email + "grafana_domain": "grafana.yourdomain.com" // Optional: separate Grafana domain + } +} +``` + +**Requirements:** + +- Domain must point to the server's public IP +- Ports 80 and 443 must be accessible +- Valid email for Let's Encrypt certificate notifications + +### 7. Monitoring Configuration + +**Prometheus:** + +```json +{ + "prometheus": { + "scrape_interval_in_secs": 15 // 15-30 recommended, higher for less load + } +} +``` + +**Grafana:** + +```json +{ + "grafana": { + "admin_user": "admin", + "admin_password": "ChangeThisPassword!" // Use strong password + } +} +``` + +### 8. Backup Configuration + +```json +{ + "backup": { + "schedule": "0 2 * * *", // Cron format: daily at 2 AM + "retention_days": 7 // Keep backups for 7 days + } +} +``` + +**Common schedules:** + +- `"0 */4 * * *"` - Every 4 hours +- `"0 2 * * *"` - Daily at 2 AM +- `"0 3 * * 0"` - Weekly on Sunday at 3 AM + +### 9. Private Tracker Mode + +```json +{ + "tracker": { + "core": { + "private": true // Enable private mode (requires user whitelisting) + } + } +} +``` + +## Validation + +All examples have been validated with the `validate` command: + +```bash +cargo run -- validate --env-file docs/ai-training/dataset/environment-configs/01-minimal-lxd.json +``` + +Expected output: + +```text +✅ Configuration file 'docs/ai-training/dataset/environment-configs/01-minimal-lxd.json' is valid +``` + +## Creating Custom Configurations + +Guide users through creating custom configurations: + +1. **Start with the closest example** from the tables above +2. **Copy and rename** the file (e.g., `my-tracker.json`) +3. **Customize** required fields (name, SSH keys, domain, passwords) +4. **Validate** the configuration: + + ```bash + cargo run -- validate --env-file my-tracker.json + ``` + +5. **Create environment**: + + ```bash + cargo run -- create environment --env-file my-tracker.json + ``` + +## Common Decision Points + +Help users make informed choices: + +### Provider: LXD vs Hetzner + +**Choose LXD when:** + +- Testing locally before cloud deployment +- Learning the system +- No public IP needed +- Free local resources available + +**Choose Hetzner when:** + +- Need public internet access +- Production deployment +- Want managed infrastructure +- Budget allows (~€4-20/month) + +### Database: SQLite3 vs MySQL + +**Choose SQLite3 when:** + +- Single-server deployment +- Low to medium traffic +- Simplicity is priority +- Development/testing + +**Choose MySQL when:** + +- High traffic expected +- Need advanced database features +- Plan to scale horizontally (future) +- Production deployment + +### HTTPS: Yes or No + +**Enable HTTPS when:** + +- Public internet deployment +- Security/privacy requirements +- Using HTTP API in production + +**Skip HTTPS when:** + +- Local testing only +- Behind VPN or private network +- Development environment + +### Monitoring: Yes or No + +**Enable Monitoring when:** + +- Production deployment +- Need performance insights +- Troubleshooting issues +- Learning system behavior + +**Skip Monitoring when:** + +- Minimal testing +- Resource-constrained +- Simple UDP-only tracker + +## Next Steps + +After configuration is created and validated: + +1. **Provision infrastructure**: `cargo run -- provision --env ` +2. **Configure services**: `cargo run -- configure --env ` +3. **Release software**: `cargo run -- release --env ` +4. **Start services**: `cargo run -- run --env ` + +See the [user guide](../user-guide/README.md) for complete workflow documentation. + +## AI Agent Best Practices + +1. **Be conversational** - Ask natural questions, not just read checklist items +2. **Explain tradeoffs** - Help users understand implications of choices +3. **Recommend examples** - Point to specific examples that match requirements +4. **Validate understanding** - Summarize choices before recommending configuration +5. **Offer customization help** - Guide users through modifying example configs +6. **Check prerequisites** - Ensure users have required SSH keys, domain names, API tokens +7. **Suggest validation** - Always recommend validating before deployment + +## Troubleshooting + +Common issues when helping users create configurations: + +### "I don't have SSH keys" + +Guide them to generate keys: + +```bash +ssh-keygen -t rsa -b 4096 -C "your_email@example.com" +``` + +### "What's my Hetzner API token?" + +Guide them to: Hetzner Cloud Console → Security → API Tokens → Generate API Token + +### "My domain isn't working with HTTPS" + +Check: + +- Domain DNS points to server public IP +- Ports 80 and 443 are accessible +- Domain propagation complete (can take hours) + +### "Which server size should I use?" + +Recommend based on expected load: + +- **cx11** (2GB RAM): Testing, low traffic (<100 peers) +- **cx21** (4GB RAM): Light production, medium traffic +- **cx31** (8GB RAM): Production, high traffic +- **cx41+**: Very high traffic or future scaling + +## Related Documentation + +- [User Guide](../user-guide/README.md) - Complete deployment workflow +- [Commands Reference](../user-guide/commands/) - Detailed command documentation +- [Environment Config Schema](../../schemas/environment-config.json) - JSON schema for validation +- [Quick Start Guide](../user-guide/quick-start/) - Step-by-step deployment tutorial diff --git a/docs/ai-training/dataset/environment-configs/01-minimal-lxd.json b/docs/ai-training/dataset/environment-configs/01-minimal-lxd.json new file mode 100644 index 00000000..a49f921e --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/01-minimal-lxd.json @@ -0,0 +1,40 @@ +{ + "environment": { + "name": "minimal-lxd", + "description": "Minimal development setup with SQLite and UDP/HTTP trackers on LXD. No HTTPS, monitoring, or backups. Ideal for local testing and rapid development iteration." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-minimal-lxd" + }, + "tracker": { + "core": { + "database": { + "driver": "sqlite3", + "database_name": "tracker.db" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "MyAccessToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/02-full-stack-lxd.json b/docs/ai-training/dataset/environment-configs/02-full-stack-lxd.json new file mode 100644 index 00000000..892208af --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/02-full-stack-lxd.json @@ -0,0 +1,75 @@ +{ + "environment": { + "name": "full-stack-lxd", + "description": "Complete full-stack deployment with all features enabled on LXD. Includes MySQL database, multiple UDP/HTTP trackers with HTTPS (staging certificates), Prometheus + Grafana monitoring, and daily backups. Ideal for comprehensive E2E testing and pre-production validation." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-full-stack-lxd" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "secure_password" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + }, + { + "bind_address": "0.0.0.0:6970" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070", + "domain": "tracker1.example.com", + "use_tls_proxy": true + }, + { + "bind_address": "0.0.0.0:7071", + "domain": "tracker2.example.com", + "use_tls_proxy": true + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "MyAccessToken", + "domain": "api.example.com", + "use_tls_proxy": true + }, + "health_check_api": { + "bind_address": "0.0.0.0:1313", + "domain": "health.example.com", + "use_tls_proxy": true + } + }, + "https": { + "admin_email": "admin@example.com", + "use_staging": true + }, + "prometheus": { + "scrape_interval_in_secs": 15 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "admin-password", + "domain": "grafana.example.com", + "use_tls_proxy": true + }, + "backup": { + "schedule": "0 2 * * *", + "retention_days": 7 + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/03-minimal-hetzner.json b/docs/ai-training/dataset/environment-configs/03-minimal-hetzner.json new file mode 100644 index 00000000..d7d97c31 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/03-minimal-hetzner.json @@ -0,0 +1,43 @@ +{ + "environment": { + "name": "minimal-hetzner", + "description": "Minimal cloud deployment on Hetzner with SQLite and basic UDP/HTTP trackers. No HTTPS, monitoring, or backups. Suitable for simple public cloud deployments or initial Hetzner infrastructure testing." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "hetzner", + "api_token": "REPLACE_WITH_HETZNER_API_TOKEN", + "server_type": "cx22", + "location": "nbg1", + "image": "ubuntu-24.04" + }, + "tracker": { + "core": { + "database": { + "driver": "sqlite3", + "database_name": "tracker.db" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "MyAccessToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/04-full-stack-hetzner.json b/docs/ai-training/dataset/environment-configs/04-full-stack-hetzner.json new file mode 100644 index 00000000..767a5a7e --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/04-full-stack-hetzner.json @@ -0,0 +1,77 @@ +{ + "environment": { + "name": "full-stack-hetzner", + "description": "Production-ready deployment on Hetzner cloud with all enterprise features. Includes MySQL for scalability, multiple HTTPS-enabled endpoints with production certificates, comprehensive monitoring (Prometheus + Grafana), and automated daily backups. Designed for high-traffic public trackers requiring professional infrastructure." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "hetzner", + "api_token": "REPLACE_WITH_HETZNER_API_TOKEN", + "server_type": "cx22", + "location": "nbg1", + "image": "ubuntu-24.04" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "secure_production_password" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + }, + { + "bind_address": "0.0.0.0:6970" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070", + "domain": "tracker1.yourdomain.com", + "use_tls_proxy": true + }, + { + "bind_address": "0.0.0.0:7071", + "domain": "tracker2.yourdomain.com", + "use_tls_proxy": true + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "REPLACE_WITH_STRONG_TOKEN", + "domain": "api.yourdomain.com", + "use_tls_proxy": true + }, + "health_check_api": { + "bind_address": "0.0.0.0:1313", + "domain": "health.yourdomain.com", + "use_tls_proxy": true + } + }, + "https": { + "admin_email": "admin@yourdomain.com" + }, + "prometheus": { + "scrape_interval_in_secs": 15 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "REPLACE_WITH_STRONG_PASSWORD", + "domain": "grafana.yourdomain.com", + "use_tls_proxy": true + }, + "backup": { + "schedule": "0 2 * * *", + "retention_days": 7 + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/05-mysql-development.json b/docs/ai-training/dataset/environment-configs/05-mysql-development.json new file mode 100644 index 00000000..171225e2 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/05-mysql-development.json @@ -0,0 +1,44 @@ +{ + "environment": { + "name": "mysql-development", + "description": "Development environment with MySQL database for testing database-intensive features locally on LXD. Includes basic UDP/HTTP trackers without HTTPS or monitoring. Ideal for developing and testing MySQL-specific functionality or migration scripts." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-mysql-development" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "dev_password" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "MyAccessToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/06-production-https.json b/docs/ai-training/dataset/environment-configs/06-production-https.json new file mode 100644 index 00000000..8dabd1a2 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/06-production-https.json @@ -0,0 +1,55 @@ +{ + "environment": { + "name": "production-https", + "description": "Production-ready LXD deployment with HTTPS/TLS via Caddy reverse proxy, MySQL database, and full monitoring stack. Includes automatic certificate management and secure HTTP API access. Ideal for real-world deployments requiring encrypted communications." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/id_rsa", + "public_key_path": "/home/torrust/.ssh/id_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-production-https" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "SecureProductionPassword123!" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "ProductionSecureToken!" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + }, + "caddy": { + "domain": "tracker.example.com", + "email": "admin@example.com" + }, + "prometheus": { + "scrape_interval_in_secs": 30 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "GrafanaSecurePassword123!" + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/07-udp-only.json b/docs/ai-training/dataset/environment-configs/07-udp-only.json new file mode 100644 index 00000000..39dc5054 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/07-udp-only.json @@ -0,0 +1,36 @@ +{ + "environment": { + "name": "udp-only", + "description": "Minimal UDP-only tracker deployment on LXD using SQLite. No HTTP API, monitoring, or backups - just the core BitTorrent UDP tracker protocol. Perfect for testing UDP tracker functionality or running a simple, lightweight tracker without web interface." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-udp-only" + }, + "tracker": { + "core": { + "database": { + "driver": "sqlite3", + "database_name": "tracker.db" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "UdpOnlyToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/08-http-only-https.json b/docs/ai-training/dataset/environment-configs/08-http-only-https.json new file mode 100644 index 00000000..5b68374b --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/08-http-only-https.json @@ -0,0 +1,51 @@ +{ + "environment": { + "name": "http-only-https", + "description": "HTTP API-focused deployment with HTTPS via Caddy for secure web interface access. Disables UDP tracker to isolate HTTP API functionality. Uses MySQL for persistence and includes monitoring stack. Great for testing HTTP announce protocol and secure API administration." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-http-only-https" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "http_only_password" + }, + "private": false + }, + "udp_trackers": [], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "HttpOnlyToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + }, + "caddy": { + "domain": "http-tracker.example.com", + "email": "admin@example.com" + }, + "prometheus": { + "scrape_interval_in_secs": 15 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "admin" + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/09-monitoring-stack.json b/docs/ai-training/dataset/environment-configs/09-monitoring-stack.json new file mode 100644 index 00000000..16545083 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/09-monitoring-stack.json @@ -0,0 +1,51 @@ +{ + "environment": { + "name": "monitoring-stack", + "description": "Full monitoring stack deployment on LXD with Prometheus and Grafana for metrics visualization. Uses MySQL for robust data storage and basic tracker configuration without HTTPS. Perfect for learning the monitoring stack or developing custom Grafana dashboards." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-monitoring-stack" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "monitoring_password" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "MyAccessToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + }, + "prometheus": { + "scrape_interval_in_secs": 15 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "admin" + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/10-multi-domain.json b/docs/ai-training/dataset/environment-configs/10-multi-domain.json new file mode 100644 index 00000000..e12c7ea5 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/10-multi-domain.json @@ -0,0 +1,56 @@ +{ + "environment": { + "name": "multi-domain", + "description": "Production deployment with multiple domain configurations for different endpoints. Tracker and Grafana served on separate subdomains with automatic HTTPS certificates. Uses MySQL for reliability and includes comprehensive monitoring. Demonstrates proper multi-service domain architecture." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/id_rsa", + "public_key_path": "/home/torrust/.ssh/id_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-multi-domain" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "multi_domain_password" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "MultiDomainToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + }, + "caddy": { + "domain": "tracker.example.com", + "email": "admin@example.com", + "grafana_domain": "grafana.example.com" + }, + "prometheus": { + "scrape_interval_in_secs": 30 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "GrafanaMultiDomain123!" + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/11-private-tracker.json b/docs/ai-training/dataset/environment-configs/11-private-tracker.json new file mode 100644 index 00000000..dbd50524 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/11-private-tracker.json @@ -0,0 +1,55 @@ +{ + "environment": { + "name": "private-tracker", + "description": "Private tracker deployment requiring user whitelisting with MySQL for user management. Includes both UDP and HTTP trackers with HTTPS via Caddy, plus monitoring stack. Only whitelisted users can announce and scrape torrents. Suitable for closed communities or testing private tracker behavior." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/id_rsa", + "public_key_path": "/home/torrust/.ssh/id_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-private" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "PrivateTrackerPassword123!" + }, + "private": true + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "PrivateAdminToken!" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + }, + "caddy": { + "domain": "private.tracker.example.com", + "email": "admin@example.com" + }, + "prometheus": { + "scrape_interval_in_secs": 30 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "PrivateGrafana123!" + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/12-high-availability.json b/docs/ai-training/dataset/environment-configs/12-high-availability.json new file mode 100644 index 00000000..fe064014 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/12-high-availability.json @@ -0,0 +1,62 @@ +{ + "environment": { + "name": "high-availability", + "description": "High-availability production setup on Hetzner Cloud with MySQL, HTTPS, monitoring, and automated daily backups. Optimized scrape intervals and production-grade security. Includes comprehensive observability and disaster recovery capabilities. Ready for mission-critical tracker deployments." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/id_rsa", + "public_key_path": "/home/torrust/.ssh/id_rsa.pub" + }, + "provider": { + "provider": "hetzner", + "api_token": "$HETZNER_API_TOKEN", + "server_type": "cx21", + "location": "nbg1", + "image": "ubuntu-24.04" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "HighAvailabilityPassword123!" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "HighAvailabilityToken!" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + }, + "caddy": { + "domain": "ha-tracker.example.com", + "email": "ops@example.com" + }, + "prometheus": { + "scrape_interval_in_secs": 30 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "HAGrafanaPassword123!" + }, + "backup": { + "schedule": "0 2 * * *", + "retention_days": 7 + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/13-backup-focused.json b/docs/ai-training/dataset/environment-configs/13-backup-focused.json new file mode 100644 index 00000000..3439ab4d --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/13-backup-focused.json @@ -0,0 +1,55 @@ +{ + "environment": { + "name": "backup-focused", + "description": "LXD deployment emphasizing data protection with automated backups every 4 hours and extended 14-day retention. Uses MySQL for critical data persistence and includes monitoring for backup health verification. Demonstrates aggressive backup strategy for data-sensitive environments requiring frequent snapshots." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-backup-focused" + }, + "tracker": { + "core": { + "database": { + "driver": "mysql", + "host": "mysql", + "port": 3306, + "database_name": "tracker", + "username": "tracker_user", + "password": "backup_focused_password" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "BackupFocusedToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + }, + "prometheus": { + "scrape_interval_in_secs": 15 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "admin" + }, + "backup": { + "schedule": "0 */4 * * *", + "retention_days": 14 + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/14-lightweight-production.json b/docs/ai-training/dataset/environment-configs/14-lightweight-production.json new file mode 100644 index 00000000..f4b8c989 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/14-lightweight-production.json @@ -0,0 +1,47 @@ +{ + "environment": { + "name": "lightweight-production", + "description": "Cost-effective production deployment on Hetzner's smallest server with SQLite database and HTTPS. Minimal resource footprint while maintaining essential production features like TLS and health checks. No monitoring or backups to reduce overhead. Ideal for personal projects or low-traffic trackers with budget constraints." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/id_rsa", + "public_key_path": "/home/torrust/.ssh/id_rsa.pub" + }, + "provider": { + "provider": "hetzner", + "api_token": "$HETZNER_API_TOKEN", + "server_type": "cx11", + "location": "nbg1", + "image": "ubuntu-24.04" + }, + "tracker": { + "core": { + "database": { + "driver": "sqlite3", + "database_name": "tracker.db" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "LightweightToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + }, + "caddy": { + "domain": "lightweight.tracker.example.com", + "email": "admin@example.com" + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/environment-configs/15-sqlite-monitoring.json b/docs/ai-training/dataset/environment-configs/15-sqlite-monitoring.json new file mode 100644 index 00000000..479b0668 --- /dev/null +++ b/docs/ai-training/dataset/environment-configs/15-sqlite-monitoring.json @@ -0,0 +1,47 @@ +{ + "environment": { + "name": "sqlite-monitoring", + "description": "Development environment combining SQLite's simplicity with full monitoring stack on LXD. Both UDP and HTTP trackers enabled without HTTPS complexity. Perfect for monitoring stack development, dashboard testing, or learning how Prometheus and Grafana integrate with the tracker without MySQL overhead." + }, + "ssh_credentials": { + "private_key_path": "/home/torrust/.ssh/testing_rsa", + "public_key_path": "/home/torrust/.ssh/testing_rsa.pub" + }, + "provider": { + "provider": "lxd", + "profile_name": "torrust-profile-sqlite-monitoring" + }, + "tracker": { + "core": { + "database": { + "driver": "sqlite3", + "database_name": "tracker.db" + }, + "private": false + }, + "udp_trackers": [ + { + "bind_address": "0.0.0.0:6969" + } + ], + "http_trackers": [ + { + "bind_address": "0.0.0.0:7070" + } + ], + "http_api": { + "bind_address": "0.0.0.0:1212", + "admin_token": "SQLiteMonitoringToken" + }, + "health_check_api": { + "bind_address": "127.0.0.1:1313" + } + }, + "prometheus": { + "scrape_interval_in_secs": 15 + }, + "grafana": { + "admin_user": "admin", + "admin_password": "admin" + } +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/inventory.yml new file mode 100644 index 00000000..bc4e1f1c --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/variables.yml new file mode 100644 index 00000000..7e9e036a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/docker-compose/.env new file mode 100644 index 00000000..00ab99b5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/docker-compose/docker-compose.yml new file mode 100644 index 00000000..fb6ed91d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..af9322e0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/prometheus/prometheus.yml new file mode 100644 index 00000000..efd6d4b0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..27f8a0fc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/tracker/tracker.toml new file mode 100644 index 00000000..28d095e7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/01-minimal-lxd/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/inventory.yml new file mode 100644 index 00000000..bc4e1f1c --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/variables.yml new file mode 100644 index 00000000..7e9e036a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/docker-compose/.env new file mode 100644 index 00000000..00ab99b5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/docker-compose/docker-compose.yml new file mode 100644 index 00000000..fb6ed91d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..af9322e0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/prometheus/prometheus.yml new file mode 100644 index 00000000..efd6d4b0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..27f8a0fc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/tracker/tracker.toml new file mode 100644 index 00000000..28d095e7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/02-full-stack-lxd/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/inventory.yml new file mode 100644 index 00000000..bc4e1f1c --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/variables.yml new file mode 100644 index 00000000..7e9e036a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/docker-compose/.env new file mode 100644 index 00000000..00ab99b5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/docker-compose/docker-compose.yml new file mode 100644 index 00000000..fb6ed91d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..af9322e0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/prometheus/prometheus.yml new file mode 100644 index 00000000..efd6d4b0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/tofu/hetzner/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/tofu/hetzner/cloud-init.yml new file mode 100644 index 00000000..27f8a0fc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/tofu/hetzner/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/tofu/hetzner/main.tf b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/tofu/hetzner/main.tf new file mode 100644 index 00000000..b7848696 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/tofu/hetzner/main.tf @@ -0,0 +1,171 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/hetzner/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for Hetzner Cloud provider. +# Defines cloud servers, SSH keys, and cloud-init configuration for Hetzner deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# This is the main OpenTofu configuration for deploying Torrust Tracker +# environments to Hetzner Cloud. +# +# Resources created: +# - SSH key: Imported from local keypair for secure access +# - Server: Hetzner Cloud server running Ubuntu with cloud-init configuration +# +# Dependencies: +# - variables.tfvars: Runtime variables (API token, server settings, SSH config) +# - cloud-init.yml: Server initialization script (rendered from template) + +terraform { + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + version = "~> 1.47" + } + } + required_version = ">= 1.0" +} + +# Configure the Hetzner Cloud provider with the API token from variables +provider "hcloud" { + token = var.hcloud_api_token +} + +# ============================================================================ +# Variables +# ============================================================================ + +variable "hcloud_api_token" { + description = "Hetzner Cloud API token for authentication" + type = string + sensitive = true +} + +variable "ssh_public_key" { + description = "Public SSH key content for server access" + type = string +} + +variable "ssh_key_name" { + description = "Name for the SSH key resource in Hetzner Cloud" + type = string +} + +variable "server_name" { + description = "Name for the server instance" + type = string +} + +variable "server_type" { + description = "Hetzner Cloud server type (e.g., cx22, cx32)" + type = string +} + +variable "server_image" { + description = "Operating system image for the server" + type = string + default = "ubuntu-24.04" +} + +variable "server_location" { + description = "Hetzner Cloud datacenter location (e.g., nbg1, fsn1, hel1)" + type = string +} + +variable "server_labels" { + description = "Labels to apply to the server for organization" + type = map(string) + default = {} +} + +# ============================================================================ +# Resources +# ============================================================================ + +# Create or import the SSH key for server access +# +# PURPOSE: This resource registers the SSH public key in Hetzner's account-level +# registry and enables ROOT SSH access as a fallback/debugging mechanism. +# +# WHY BOTH THIS AND CLOUD-INIT? +# - This SSH key (via ssh_keys on server) → root user access +# - cloud-init ssh_authorized_keys → torrust user access +# +# The root access is intentional: if cloud-init fails (syntax error, network +# issue, script error), the server would be completely inaccessible without it. +# Root SSH provides a recovery/debugging path. +# +# SECURITY NOTE: For production deployments, consider disabling root SSH access +# after verifying deployment succeeded. See docs/security/ssh-root-access-hetzner.md +# +# This key will appear in Hetzner Console → Security → SSH Keys. +resource "hcloud_ssh_key" "torrust" { + name = var.ssh_key_name + public_key = var.ssh_public_key +} + +# Create the Hetzner Cloud server +resource "hcloud_server" "torrust" { + name = var.server_name + image = var.server_image + server_type = var.server_type + location = var.server_location + labels = var.server_labels + + ssh_keys = [ + hcloud_ssh_key.torrust.id + ] + + # Cloud-init configuration for initial server setup + user_data = file("${path.module}/cloud-init.yml") + + # Ensure SSH key is created before the server + depends_on = [hcloud_ssh_key.torrust] +} + +# ============================================================================ +# Outputs +# ============================================================================ + +# IMPORTANT: This output is parsed by src/adapters/tofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created server" + value = { + name = hcloud_server.torrust.name + image = hcloud_server.torrust.image + status = hcloud_server.torrust.status + ip_address = hcloud_server.torrust.ipv4_address + } + depends_on = [hcloud_server.torrust] +} + +output "connection_commands" { + description = "Commands to connect to the server" + value = [ + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address}", + "hcloud server ssh ${var.server_name}" + ] +} + +output "test_commands" { + description = "Commands to test the server functionality" + value = [ + "hcloud server describe ${var.server_name}", + "hcloud server list", + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address} 'cat /etc/os-release'", + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address} 'cloud-init status'" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/tracker/tracker.toml new file mode 100644 index 00000000..28d095e7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/03-minimal-hetzner/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/inventory.yml new file mode 100644 index 00000000..bc4e1f1c --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/variables.yml new file mode 100644 index 00000000..7e9e036a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/docker-compose/.env new file mode 100644 index 00000000..00ab99b5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/docker-compose/docker-compose.yml new file mode 100644 index 00000000..fb6ed91d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..af9322e0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/prometheus/prometheus.yml new file mode 100644 index 00000000..efd6d4b0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/tofu/hetzner/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/tofu/hetzner/cloud-init.yml new file mode 100644 index 00000000..27f8a0fc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/tofu/hetzner/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/tofu/hetzner/main.tf b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/tofu/hetzner/main.tf new file mode 100644 index 00000000..b7848696 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/tofu/hetzner/main.tf @@ -0,0 +1,171 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/hetzner/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for Hetzner Cloud provider. +# Defines cloud servers, SSH keys, and cloud-init configuration for Hetzner deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# This is the main OpenTofu configuration for deploying Torrust Tracker +# environments to Hetzner Cloud. +# +# Resources created: +# - SSH key: Imported from local keypair for secure access +# - Server: Hetzner Cloud server running Ubuntu with cloud-init configuration +# +# Dependencies: +# - variables.tfvars: Runtime variables (API token, server settings, SSH config) +# - cloud-init.yml: Server initialization script (rendered from template) + +terraform { + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + version = "~> 1.47" + } + } + required_version = ">= 1.0" +} + +# Configure the Hetzner Cloud provider with the API token from variables +provider "hcloud" { + token = var.hcloud_api_token +} + +# ============================================================================ +# Variables +# ============================================================================ + +variable "hcloud_api_token" { + description = "Hetzner Cloud API token for authentication" + type = string + sensitive = true +} + +variable "ssh_public_key" { + description = "Public SSH key content for server access" + type = string +} + +variable "ssh_key_name" { + description = "Name for the SSH key resource in Hetzner Cloud" + type = string +} + +variable "server_name" { + description = "Name for the server instance" + type = string +} + +variable "server_type" { + description = "Hetzner Cloud server type (e.g., cx22, cx32)" + type = string +} + +variable "server_image" { + description = "Operating system image for the server" + type = string + default = "ubuntu-24.04" +} + +variable "server_location" { + description = "Hetzner Cloud datacenter location (e.g., nbg1, fsn1, hel1)" + type = string +} + +variable "server_labels" { + description = "Labels to apply to the server for organization" + type = map(string) + default = {} +} + +# ============================================================================ +# Resources +# ============================================================================ + +# Create or import the SSH key for server access +# +# PURPOSE: This resource registers the SSH public key in Hetzner's account-level +# registry and enables ROOT SSH access as a fallback/debugging mechanism. +# +# WHY BOTH THIS AND CLOUD-INIT? +# - This SSH key (via ssh_keys on server) → root user access +# - cloud-init ssh_authorized_keys → torrust user access +# +# The root access is intentional: if cloud-init fails (syntax error, network +# issue, script error), the server would be completely inaccessible without it. +# Root SSH provides a recovery/debugging path. +# +# SECURITY NOTE: For production deployments, consider disabling root SSH access +# after verifying deployment succeeded. See docs/security/ssh-root-access-hetzner.md +# +# This key will appear in Hetzner Console → Security → SSH Keys. +resource "hcloud_ssh_key" "torrust" { + name = var.ssh_key_name + public_key = var.ssh_public_key +} + +# Create the Hetzner Cloud server +resource "hcloud_server" "torrust" { + name = var.server_name + image = var.server_image + server_type = var.server_type + location = var.server_location + labels = var.server_labels + + ssh_keys = [ + hcloud_ssh_key.torrust.id + ] + + # Cloud-init configuration for initial server setup + user_data = file("${path.module}/cloud-init.yml") + + # Ensure SSH key is created before the server + depends_on = [hcloud_ssh_key.torrust] +} + +# ============================================================================ +# Outputs +# ============================================================================ + +# IMPORTANT: This output is parsed by src/adapters/tofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created server" + value = { + name = hcloud_server.torrust.name + image = hcloud_server.torrust.image + status = hcloud_server.torrust.status + ip_address = hcloud_server.torrust.ipv4_address + } + depends_on = [hcloud_server.torrust] +} + +output "connection_commands" { + description = "Commands to connect to the server" + value = [ + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address}", + "hcloud server ssh ${var.server_name}" + ] +} + +output "test_commands" { + description = "Commands to test the server functionality" + value = [ + "hcloud server describe ${var.server_name}", + "hcloud server list", + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address} 'cat /etc/os-release'", + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address} 'cloud-init status'" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/tracker/tracker.toml new file mode 100644 index 00000000..28d095e7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/04-full-stack-hetzner/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:22Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/inventory.yml new file mode 100644 index 00000000..4576468d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/variables.yml new file mode 100644 index 00000000..0f5d67f7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/05-mysql-development/docker-compose/.env new file mode 100644 index 00000000..2ef80f1b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/docker-compose/docker-compose.yml new file mode 100644 index 00000000..5c30824f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..5128bed6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/prometheus/prometheus.yml new file mode 100644 index 00000000..05ab87a3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..77ddeda0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/05-mysql-development/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/05-mysql-development/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/05-mysql-development/tracker/tracker.toml new file mode 100644 index 00000000..ad04b95a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/05-mysql-development/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/inventory.yml new file mode 100644 index 00000000..4576468d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/variables.yml new file mode 100644 index 00000000..0f5d67f7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/06-production-https/docker-compose/.env new file mode 100644 index 00000000..2ef80f1b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/docker-compose/docker-compose.yml new file mode 100644 index 00000000..5c30824f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..5128bed6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/prometheus/prometheus.yml new file mode 100644 index 00000000..05ab87a3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/06-production-https/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..77ddeda0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/06-production-https/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/06-production-https/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/06-production-https/tracker/tracker.toml new file mode 100644 index 00000000..ad04b95a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/06-production-https/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/inventory.yml new file mode 100644 index 00000000..4576468d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/variables.yml new file mode 100644 index 00000000..0f5d67f7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/07-udp-only/docker-compose/.env new file mode 100644 index 00000000..2ef80f1b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/docker-compose/docker-compose.yml new file mode 100644 index 00000000..5c30824f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..5128bed6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/prometheus/prometheus.yml new file mode 100644 index 00000000..05ab87a3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/07-udp-only/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..77ddeda0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/07-udp-only/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/07-udp-only/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/07-udp-only/tracker/tracker.toml new file mode 100644 index 00000000..ad04b95a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/07-udp-only/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/inventory.yml new file mode 100644 index 00000000..4576468d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/variables.yml new file mode 100644 index 00000000..0f5d67f7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/08-http-only-https/docker-compose/.env new file mode 100644 index 00000000..2ef80f1b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/docker-compose/docker-compose.yml new file mode 100644 index 00000000..5c30824f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..5128bed6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/prometheus/prometheus.yml new file mode 100644 index 00000000..05ab87a3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..77ddeda0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/08-http-only-https/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/08-http-only-https/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/08-http-only-https/tracker/tracker.toml new file mode 100644 index 00000000..ad04b95a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/08-http-only-https/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/inventory.yml new file mode 100644 index 00000000..4576468d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/variables.yml new file mode 100644 index 00000000..0f5d67f7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/docker-compose/.env new file mode 100644 index 00000000..2ef80f1b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/docker-compose/docker-compose.yml new file mode 100644 index 00000000..5c30824f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..5128bed6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/prometheus/prometheus.yml new file mode 100644 index 00000000..05ab87a3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..77ddeda0 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/tracker/tracker.toml new file mode 100644 index 00000000..ad04b95a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/09-monitoring-stack/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:23Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/inventory.yml new file mode 100644 index 00000000..e23a0d43 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/variables.yml new file mode 100644 index 00000000..0935a131 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/10-multi-domain/docker-compose/.env new file mode 100644 index 00000000..46dd0d66 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/docker-compose/docker-compose.yml new file mode 100644 index 00000000..1476c1bc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..85a223d6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/prometheus/prometheus.yml new file mode 100644 index 00000000..3740bef7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..83718d56 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/10-multi-domain/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/10-multi-domain/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/10-multi-domain/tracker/tracker.toml new file mode 100644 index 00000000..24e26e8f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/10-multi-domain/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/inventory.yml new file mode 100644 index 00000000..e23a0d43 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/variables.yml new file mode 100644 index 00000000..0935a131 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/11-private-tracker/docker-compose/.env new file mode 100644 index 00000000..46dd0d66 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/docker-compose/docker-compose.yml new file mode 100644 index 00000000..1476c1bc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..85a223d6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/prometheus/prometheus.yml new file mode 100644 index 00000000..3740bef7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..83718d56 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/11-private-tracker/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/11-private-tracker/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/11-private-tracker/tracker/tracker.toml new file mode 100644 index 00000000..24e26e8f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/11-private-tracker/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/inventory.yml new file mode 100644 index 00000000..e23a0d43 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/variables.yml new file mode 100644 index 00000000..0935a131 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/12-high-availability/docker-compose/.env new file mode 100644 index 00000000..46dd0d66 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/docker-compose/docker-compose.yml new file mode 100644 index 00000000..1476c1bc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..85a223d6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/prometheus/prometheus.yml new file mode 100644 index 00000000..3740bef7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/tofu/hetzner/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/12-high-availability/tofu/hetzner/cloud-init.yml new file mode 100644 index 00000000..83718d56 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/tofu/hetzner/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/tofu/hetzner/main.tf b/docs/ai-training/dataset/rendered-templates/12-high-availability/tofu/hetzner/main.tf new file mode 100644 index 00000000..b7848696 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/tofu/hetzner/main.tf @@ -0,0 +1,171 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/hetzner/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for Hetzner Cloud provider. +# Defines cloud servers, SSH keys, and cloud-init configuration for Hetzner deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# This is the main OpenTofu configuration for deploying Torrust Tracker +# environments to Hetzner Cloud. +# +# Resources created: +# - SSH key: Imported from local keypair for secure access +# - Server: Hetzner Cloud server running Ubuntu with cloud-init configuration +# +# Dependencies: +# - variables.tfvars: Runtime variables (API token, server settings, SSH config) +# - cloud-init.yml: Server initialization script (rendered from template) + +terraform { + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + version = "~> 1.47" + } + } + required_version = ">= 1.0" +} + +# Configure the Hetzner Cloud provider with the API token from variables +provider "hcloud" { + token = var.hcloud_api_token +} + +# ============================================================================ +# Variables +# ============================================================================ + +variable "hcloud_api_token" { + description = "Hetzner Cloud API token for authentication" + type = string + sensitive = true +} + +variable "ssh_public_key" { + description = "Public SSH key content for server access" + type = string +} + +variable "ssh_key_name" { + description = "Name for the SSH key resource in Hetzner Cloud" + type = string +} + +variable "server_name" { + description = "Name for the server instance" + type = string +} + +variable "server_type" { + description = "Hetzner Cloud server type (e.g., cx22, cx32)" + type = string +} + +variable "server_image" { + description = "Operating system image for the server" + type = string + default = "ubuntu-24.04" +} + +variable "server_location" { + description = "Hetzner Cloud datacenter location (e.g., nbg1, fsn1, hel1)" + type = string +} + +variable "server_labels" { + description = "Labels to apply to the server for organization" + type = map(string) + default = {} +} + +# ============================================================================ +# Resources +# ============================================================================ + +# Create or import the SSH key for server access +# +# PURPOSE: This resource registers the SSH public key in Hetzner's account-level +# registry and enables ROOT SSH access as a fallback/debugging mechanism. +# +# WHY BOTH THIS AND CLOUD-INIT? +# - This SSH key (via ssh_keys on server) → root user access +# - cloud-init ssh_authorized_keys → torrust user access +# +# The root access is intentional: if cloud-init fails (syntax error, network +# issue, script error), the server would be completely inaccessible without it. +# Root SSH provides a recovery/debugging path. +# +# SECURITY NOTE: For production deployments, consider disabling root SSH access +# after verifying deployment succeeded. See docs/security/ssh-root-access-hetzner.md +# +# This key will appear in Hetzner Console → Security → SSH Keys. +resource "hcloud_ssh_key" "torrust" { + name = var.ssh_key_name + public_key = var.ssh_public_key +} + +# Create the Hetzner Cloud server +resource "hcloud_server" "torrust" { + name = var.server_name + image = var.server_image + server_type = var.server_type + location = var.server_location + labels = var.server_labels + + ssh_keys = [ + hcloud_ssh_key.torrust.id + ] + + # Cloud-init configuration for initial server setup + user_data = file("${path.module}/cloud-init.yml") + + # Ensure SSH key is created before the server + depends_on = [hcloud_ssh_key.torrust] +} + +# ============================================================================ +# Outputs +# ============================================================================ + +# IMPORTANT: This output is parsed by src/adapters/tofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created server" + value = { + name = hcloud_server.torrust.name + image = hcloud_server.torrust.image + status = hcloud_server.torrust.status + ip_address = hcloud_server.torrust.ipv4_address + } + depends_on = [hcloud_server.torrust] +} + +output "connection_commands" { + description = "Commands to connect to the server" + value = [ + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address}", + "hcloud server ssh ${var.server_name}" + ] +} + +output "test_commands" { + description = "Commands to test the server functionality" + value = [ + "hcloud server describe ${var.server_name}", + "hcloud server list", + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address} 'cat /etc/os-release'", + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address} 'cloud-init status'" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/12-high-availability/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/12-high-availability/tracker/tracker.toml new file mode 100644 index 00000000..24e26e8f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/12-high-availability/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/inventory.yml new file mode 100644 index 00000000..e23a0d43 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/variables.yml new file mode 100644 index 00000000..0935a131 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/13-backup-focused/docker-compose/.env new file mode 100644 index 00000000..46dd0d66 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/docker-compose/docker-compose.yml new file mode 100644 index 00000000..1476c1bc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..85a223d6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/prometheus/prometheus.yml new file mode 100644 index 00000000..3740bef7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..83718d56 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/13-backup-focused/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/13-backup-focused/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/13-backup-focused/tracker/tracker.toml new file mode 100644 index 00000000..24e26e8f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/13-backup-focused/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/inventory.yml new file mode 100644 index 00000000..e23a0d43 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/variables.yml new file mode 100644 index 00000000..0935a131 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/docker-compose/.env new file mode 100644 index 00000000..46dd0d66 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/docker-compose/docker-compose.yml new file mode 100644 index 00000000..1476c1bc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..85a223d6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/prometheus/prometheus.yml new file mode 100644 index 00000000..3740bef7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/tofu/hetzner/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/tofu/hetzner/cloud-init.yml new file mode 100644 index 00000000..83718d56 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/tofu/hetzner/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/tofu/hetzner/main.tf b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/tofu/hetzner/main.tf new file mode 100644 index 00000000..b7848696 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/tofu/hetzner/main.tf @@ -0,0 +1,171 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/hetzner/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for Hetzner Cloud provider. +# Defines cloud servers, SSH keys, and cloud-init configuration for Hetzner deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# This is the main OpenTofu configuration for deploying Torrust Tracker +# environments to Hetzner Cloud. +# +# Resources created: +# - SSH key: Imported from local keypair for secure access +# - Server: Hetzner Cloud server running Ubuntu with cloud-init configuration +# +# Dependencies: +# - variables.tfvars: Runtime variables (API token, server settings, SSH config) +# - cloud-init.yml: Server initialization script (rendered from template) + +terraform { + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + version = "~> 1.47" + } + } + required_version = ">= 1.0" +} + +# Configure the Hetzner Cloud provider with the API token from variables +provider "hcloud" { + token = var.hcloud_api_token +} + +# ============================================================================ +# Variables +# ============================================================================ + +variable "hcloud_api_token" { + description = "Hetzner Cloud API token for authentication" + type = string + sensitive = true +} + +variable "ssh_public_key" { + description = "Public SSH key content for server access" + type = string +} + +variable "ssh_key_name" { + description = "Name for the SSH key resource in Hetzner Cloud" + type = string +} + +variable "server_name" { + description = "Name for the server instance" + type = string +} + +variable "server_type" { + description = "Hetzner Cloud server type (e.g., cx22, cx32)" + type = string +} + +variable "server_image" { + description = "Operating system image for the server" + type = string + default = "ubuntu-24.04" +} + +variable "server_location" { + description = "Hetzner Cloud datacenter location (e.g., nbg1, fsn1, hel1)" + type = string +} + +variable "server_labels" { + description = "Labels to apply to the server for organization" + type = map(string) + default = {} +} + +# ============================================================================ +# Resources +# ============================================================================ + +# Create or import the SSH key for server access +# +# PURPOSE: This resource registers the SSH public key in Hetzner's account-level +# registry and enables ROOT SSH access as a fallback/debugging mechanism. +# +# WHY BOTH THIS AND CLOUD-INIT? +# - This SSH key (via ssh_keys on server) → root user access +# - cloud-init ssh_authorized_keys → torrust user access +# +# The root access is intentional: if cloud-init fails (syntax error, network +# issue, script error), the server would be completely inaccessible without it. +# Root SSH provides a recovery/debugging path. +# +# SECURITY NOTE: For production deployments, consider disabling root SSH access +# after verifying deployment succeeded. See docs/security/ssh-root-access-hetzner.md +# +# This key will appear in Hetzner Console → Security → SSH Keys. +resource "hcloud_ssh_key" "torrust" { + name = var.ssh_key_name + public_key = var.ssh_public_key +} + +# Create the Hetzner Cloud server +resource "hcloud_server" "torrust" { + name = var.server_name + image = var.server_image + server_type = var.server_type + location = var.server_location + labels = var.server_labels + + ssh_keys = [ + hcloud_ssh_key.torrust.id + ] + + # Cloud-init configuration for initial server setup + user_data = file("${path.module}/cloud-init.yml") + + # Ensure SSH key is created before the server + depends_on = [hcloud_ssh_key.torrust] +} + +# ============================================================================ +# Outputs +# ============================================================================ + +# IMPORTANT: This output is parsed by src/adapters/tofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created server" + value = { + name = hcloud_server.torrust.name + image = hcloud_server.torrust.image + status = hcloud_server.torrust.status + ip_address = hcloud_server.torrust.ipv4_address + } + depends_on = [hcloud_server.torrust] +} + +output "connection_commands" { + description = "Commands to connect to the server" + value = [ + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address}", + "hcloud server ssh ${var.server_name}" + ] +} + +output "test_commands" { + description = "Commands to test the server functionality" + value = [ + "hcloud server describe ${var.server_name}", + "hcloud server list", + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address} 'cat /etc/os-release'", + "ssh ${var.server_name}@${hcloud_server.torrust.ipv4_address} 'cloud-init status'" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/14-lightweight-production/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/tracker/tracker.toml new file mode 100644 index 00000000..24e26e8f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/14-lightweight-production/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/ansible.cfg b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/ansible.cfg new file mode 100644 index 00000000..d22f9eff --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/ansible.cfg @@ -0,0 +1,60 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/ansible.cfg +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible global configuration settings for connecting to provisioned VMs. +# Configures SSH connection settings and output formatting. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# 🔗 INFRASTRUCTURE WORKFLOW: +# 1. OpenTofu (templates/tofu/lxd/) provisions VMs with cloud-init +# 2. Cloud-init sets up users, SSH keys, and basic system configuration +# 3. Ansible (this directory) connects to provisioned VMs for management tasks +# 4. This config file ensures Ansible can reliably connect to OpenTofu-created VMs + +[defaults] +# Specify the default inventory file location +# This tells Ansible where to find the list of hosts to manage +# 🔗 The inventory.yml contains IPs of VMs created by OpenTofu +inventory = inventory.yml + +# Disable SSH host key checking for lab/development environments +# This prevents SSH from asking "Are you sure you want to connect?" prompts +# ⚠️ IMPORTANT: OpenTofu creates fresh VMs with new SSH host keys each time +# WARNING: Only use this in trusted lab environments, not production +host_key_checking = False + +# Use debug callback plugin for better output formatting +# This provides cleaner, more readable output from Ansible commands +stdout_callback = debug +stderr_callback = debug + +# Enable progress display for long-running tasks +# Shows task progress and timing information +callback_enabled = timer, profile_tasks + +# Display task timing information +show_task_path_on_failure = True + +# Set connection timeout in seconds +# How long to wait for SSH connections before giving up +timeout = 30 + +[ssh_connection] +# SSH connection optimization arguments +# These settings improve SSH connection performance and reliability: +# - ControlMaster=auto: Reuse SSH connections for multiple commands +# - ControlPersist=60s: Keep connections alive for 60 seconds after last use +# - StrictHostKeyChecking=no: Don't verify SSH host keys (lab environment only) +# - UserKnownHostsFile=/dev/null: Don't save host keys to known_hosts file +ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/configure-firewall.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/configure-firewall.yml new file mode 100644 index 00000000..188409fb --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/configure-firewall.yml @@ -0,0 +1,142 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-firewall.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure UFW firewall rules for SSH access. +# Ensures safe SSH connectivity before enabling firewall. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# IMPORTANT SECURITY NOTE: +# ======================= +# This playbook ONLY configures SSH firewall rules. Application service ports +# (tracker, grafana, prometheus, etc.) are NOT controlled by UFW because Docker +# bypasses UFW rules when publishing container ports. +# +# Docker Security Model: +# - Docker manipulates iptables NAT table directly, bypassing UFW's INPUT/OUTPUT chains +# - Service exposure is controlled via docker-compose port bindings, not UFW +# - Internal services (MySQL, Prometheus) have NO port bindings - Docker network only +# - Public services (Tracker, Grafana) have explicit port bindings in docker-compose +# +# For details, see ADR: docs/decisions/docker-ufw-firewall-security-strategy.md +# +# This playbook configures UFW with restrictive policies while preserving SSH access. +# CRITICAL: SSH access is allowed BEFORE enabling firewall to prevent lockout. +# +# Variables are loaded from variables.yml for centralized management. + +- name: Configure UFW firewall safely (SSH access only) + hosts: all + become: yes + gather_facts: yes + vars_files: + - variables.yml + + tasks: + - name: Install UFW (should already be present on Ubuntu) + ansible.builtin.apt: + name: ufw + state: present + update_cache: yes + tags: + - security + - firewall + - packages + + - name: Reset UFW to clean state + community.general.ufw: + state: reset + tags: + - security + - firewall + - reset + + - name: Set UFW default policy - deny incoming + community.general.ufw: + default: deny + direction: incoming + tags: + - security + - firewall + - policy + + - name: Set UFW default policy - allow outgoing + community.general.ufw: + default: allow + direction: outgoing + tags: + - security + - firewall + - policy + + # CRITICAL: Allow SSH BEFORE enabling firewall to prevent lockout + - name: Allow SSH access on configured port (BEFORE enabling firewall) + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + comment: "SSH access (configured port {{ ssh_port }})" + tags: + - security + - firewall + - ssh + + - name: Enable UFW firewall (AFTER SSH rules are in place) + community.general.ufw: + state: enabled + tags: + - security + - firewall + - enable + + - name: Verify UFW status + ansible.builtin.command: + cmd: ufw status numbered + register: ufw_status + changed_when: false + tags: + - security + - firewall + - verification + + - name: Display UFW status + ansible.builtin.debug: + var: ufw_status.stdout_lines + tags: + - security + - firewall + - verification + + - name: Verify SSH port is allowed + ansible.builtin.shell: + cmd: "ufw status | grep -E '{{ ssh_port }}/tcp.*ALLOW'" + register: ssh_port_check + changed_when: false + failed_when: ssh_port_check.rc != 0 + tags: + - security + - firewall + - verification + - ssh + + - name: Confirm firewall configuration complete + ansible.builtin.debug: + msg: + - "UFW firewall configured successfully" + - "SSH access preserved on port {{ ssh_port }}" + - "Default policy: deny incoming, allow outgoing" + - "Active rules protect against unauthorized access" + tags: + - security + - firewall diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/configure-security-updates.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/configure-security-updates.yml new file mode 100644 index 00000000..575fcdf3 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/configure-security-updates.yml @@ -0,0 +1,90 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/configure-security-updates.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to configure automatic security updates using unattended-upgrades. +# Schedules automatic reboots at 2:00 AM when updates require restart. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Configure automatic security updates + hosts: all + gather_facts: true + become: true + + tasks: + - name: 🔐 Starting automatic security updates configuration + ansible.builtin.debug: + msg: "🚀 Configuring unattended-upgrades on {{ inventory_hostname }}" + + - name: Install unattended-upgrades package + ansible.builtin.apt: + name: unattended-upgrades + state: present + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Enable automatic security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/20auto-upgrades + regexp: "^APT::Periodic::Unattended-Upgrade" + line: 'APT::Periodic::Unattended-Upgrade "1";' + create: true + backup: true + when: ansible_os_family == "Debian" + + - name: Enable automatic reboot for security updates + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot" + line: 'Unattended-Upgrade::Automatic-Reboot "true";' + backup: true + when: ansible_os_family == "Debian" + + - name: Set automatic reboot time to 2:00 AM + ansible.builtin.lineinfile: + path: /etc/apt/apt.conf.d/50unattended-upgrades + regexp: "^Unattended-Upgrade::Automatic-Reboot-Time" + line: 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' + backup: true + when: ansible_os_family == "Debian" + + - name: Enable and start unattended-upgrades service + ansible.builtin.systemd: + name: unattended-upgrades + enabled: true + state: started + when: ansible_os_family == "Debian" + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Verify unattended-upgrades configuration + ansible.builtin.command: + cmd: unattended-upgrade --dry-run --debug + register: unattended_upgrades_test + changed_when: false + failed_when: false # Don't fail on verification errors in test environments + when: ansible_os_family == "Debian" + + - name: Display verification result + ansible.builtin.debug: + msg: "✅ Unattended-upgrades dry-run completed with exit code {{ unattended_upgrades_test.rc }}" + + - name: Configuration summary + ansible.builtin.debug: + msg: | + ✅ Automatic security updates configuration completed! + 📦 Installed: unattended-upgrades + 🔄 Automatic updates: Enabled + 🕐 Automatic reboot time: 02:00 + ℹ️ Security updates will be installed automatically with scheduled reboots diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-backup-storage.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-backup-storage.yml new file mode 100644 index 00000000..2d0b7472 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-backup-storage.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-backup-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create backup storage directories on remote host. +# Creates the directory structure required for backup operations. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the backup storage directory structure on the remote host. +# The directories are created with appropriate permissions and ownership. +# +# Directory Structure: +# /opt/torrust/storage/backup/ +# └── etc/ # Backup configuration files +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Create Backup storage directories + hosts: all + become: true + + tasks: + - name: Create backup configuration directory + ansible.builtin.file: + path: /opt/torrust/storage/backup/etc + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup configuration directory exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc + register: backup_etc_dir + + - name: Assert backup directories were created + ansible.builtin.assert: + that: + - backup_etc_dir.stat.exists + - backup_etc_dir.stat.isdir + - backup_etc_dir.stat.pw_name == ansible_user + fail_msg: "Backup storage directories were not created properly" + success_msg: "Backup storage directories created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-grafana-storage.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-grafana-storage.yml new file mode 100644 index 00000000..b73fe52a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-grafana-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-grafana-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Grafana data directory with correct ownership. +# Ensures Grafana container (UID 472) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the Grafana data directory with correct ownership. +# Grafana container runs as user 472:472 (grafana), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - grafana_enabled: Whether Grafana is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create Grafana storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/grafana/data" + state: directory + mode: "0755" + owner: "472" + group: "472" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-mysql-storage.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-mysql-storage.yml new file mode 100644 index 00000000..303d7fef --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-mysql-storage.yml @@ -0,0 +1,48 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-mysql-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create MySQL data directory with correct ownership. +# Ensures MySQL container (UID 999) can write to bind-mounted storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates the MySQL data directory with correct ownership. +# MySQL container runs as user 999:999 (mysql), so the host directory +# must be owned by that user/group for the container to write data. +# +# This is required because we use bind mounts instead of named volumes. +# Docker named volumes automatically handle ownership, but bind mounts +# require explicit directory creation with correct permissions. +# +# Variables: +# - deploy_dir: Base deployment directory (e.g., /opt/torrust) +# - mysql_enabled: Whether MySQL is enabled (from variables.yml) +# +# See ADR: docs/decisions/bind-mount-standardization.md + +- name: Create MySQL storage directory + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create MySQL data directory with correct ownership + ansible.builtin.file: + path: "{{ deploy_dir }}/storage/mysql/data" + state: directory + mode: "0755" + owner: "999" + group: "999" + when: mysql_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-prometheus-storage.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-prometheus-storage.yml new file mode 100644 index 00000000..1def3e0e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-prometheus-storage.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-prometheus-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Prometheus data directory structure. +# Sets up configuration and data directories for Prometheus metrics storage. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Prometheus storage directories + hosts: all + become: true + + tasks: + - name: Create Prometheus directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/prometheus/etc diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-tracker-storage.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-tracker-storage.yml new file mode 100644 index 00000000..7ffbc0aa --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/create-tracker-storage.yml @@ -0,0 +1,35 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/create-tracker-storage.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to create Tracker storage directory structure. +# Sets up configuration, database, and log directories for the tracker. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Create Tracker storage directories + hosts: all + become: true + + tasks: + - name: Create Tracker directory structure + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/tracker/etc + - /opt/torrust/storage/tracker/lib/database + - /opt/torrust/storage/tracker/log diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-backup-config.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-backup-config.yml new file mode 100644 index 00000000..12a07f20 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-backup-config.yml @@ -0,0 +1,73 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-backup-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy backup configuration files to remote host. +# Copies rendered backup.conf and backup-paths.txt from build directory +# to backup configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys backup configuration files to the remote host. +# The configuration files are copied from the local build directory to the +# backup configuration directory on the remote instance. +# +# Requirements: +# - Backup storage directories must already exist (created by create-backup-storage playbook) +# - Build directory must contain rendered backup.conf and backup-paths.txt +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Backup configuration + hosts: all + become: true + + tasks: + - name: Copy backup.conf to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup.conf" + dest: /opt/torrust/storage/backup/etc/backup.conf + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy backup-paths.txt to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/backup-paths.txt" + dest: /opt/torrust/storage/backup/etc/backup-paths.txt + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify backup.conf exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup.conf + register: backup_conf + + - name: Verify backup-paths.txt exists + ansible.builtin.stat: + path: /opt/torrust/storage/backup/etc/backup-paths.txt + register: backup_paths + + - name: Assert backup configuration files were deployed + ansible.builtin.assert: + that: + - backup_conf.stat.exists + - backup_conf.stat.isreg + - backup_conf.stat.pw_name == ansible_user + - backup_paths.stat.exists + - backup_paths.stat.isreg + - backup_paths.stat.pw_name == ansible_user + fail_msg: "Backup configuration files were not deployed properly" + success_msg: "Backup configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-caddy-config.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-caddy-config.yml new file mode 100644 index 00000000..22fa312d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-caddy-config.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-caddy-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Caddyfile configuration to remote host. +# Copies rendered Caddyfile from build directory to Caddy configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the Caddyfile configuration file to the remote host. +# The configuration file is copied from the local build directory to the Caddy +# configuration directory on the remote instance. +# +# Requirements: +# - Build directory must contain rendered Caddyfile +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Storage Directories: +# - /opt/torrust/storage/caddy/etc/ - Caddyfile configuration +# - /opt/torrust/storage/caddy/data/ - Caddy data (certificates, etc.) +# - /opt/torrust/storage/caddy/config/ - Caddy config state + +- name: Deploy Caddy configuration + hosts: all + become: true + + tasks: + - name: Create Caddy storage directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - /opt/torrust/storage/caddy + - /opt/torrust/storage/caddy/etc + - /opt/torrust/storage/caddy/data + - /opt/torrust/storage/caddy/config + + - name: Copy Caddyfile to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../caddy/Caddyfile" + # Note: This is the host path. Inside the container, it's mounted to /etc/caddy/Caddyfile + dest: /opt/torrust/storage/caddy/etc/Caddyfile + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Caddy configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/caddy/etc/Caddyfile + register: caddy_config + + - name: Assert Caddy configuration was deployed + ansible.builtin.assert: + that: + - caddy_config.stat.exists + - caddy_config.stat.isreg + - caddy_config.stat.pw_name == ansible_user + fail_msg: "Caddy configuration file was not deployed properly" + success_msg: "Caddy configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-compose-files.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-compose-files.yml new file mode 100644 index 00000000..ba817fd4 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-compose-files.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-compose-files.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Docker Compose files to remote host. +# Copies the local docker-compose build folder to the remote deployment directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Docker Compose Files + hosts: all + gather_facts: false + become: true + + vars: + remote_deploy_dir: /opt/torrust + local_compose_dir: "{{ compose_files_source_dir }}" + + tasks: + - name: 📦 Starting Docker Compose files deployment + ansible.builtin.debug: + msg: "🚀 Deploying Docker Compose files to {{ inventory_hostname }}:{{ remote_deploy_dir }}" + + - name: Ensure remote deployment directory exists + ansible.builtin.file: + path: "{{ remote_deploy_dir }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Copy Docker Compose files to remote host + ansible.builtin.copy: + src: "{{ local_compose_dir }}/" + dest: "{{ remote_deploy_dir }}/" + mode: "0640" + directory_mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify docker-compose.yml exists on remote + ansible.builtin.stat: + path: "{{ remote_deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml was not deployed + ansible.builtin.fail: + msg: "docker-compose.yml was not found at {{ remote_deploy_dir }}/docker-compose.yml after deployment" + when: not compose_file_check.stat.exists + + - name: List deployed files + ansible.builtin.find: + paths: "{{ remote_deploy_dir }}" + file_type: file + recurse: true + register: deployed_files + + - name: Display deployment summary + ansible.builtin.debug: + msg: | + ✅ Docker Compose files deployed successfully! + 📁 Destination: {{ remote_deploy_dir }} + 📄 Files deployed: {{ deployed_files.files | length }} + {% for file in deployed_files.files %} + - {{ file.path | regex_replace(remote_deploy_dir ~ '/', '') }} + {% endfor %} diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-grafana-provisioning.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-grafana-provisioning.yml new file mode 100644 index 00000000..02bae9c5 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-grafana-provisioning.yml @@ -0,0 +1,47 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-grafana-provisioning.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Grafana provisioning configuration files. +# Sets up datasource and dashboard configuration for Grafana. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Deploy Grafana provisioning configuration + hosts: all + become: true + vars_files: + - variables.yml + + tasks: + - name: Create Grafana provisioning directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + loop: + - "{{ deploy_dir }}/storage/grafana/provisioning/datasources" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards" + - "{{ deploy_dir }}/storage/grafana/provisioning/dashboards/torrust" + when: grafana_enabled | default(false) + + - name: Deploy Grafana provisioning files + ansible.builtin.copy: + src: "{{ playbook_dir }}/../grafana/provisioning/" + dest: "{{ deploy_dir }}/storage/grafana/provisioning/" + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + when: grafana_enabled | default(false) diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-prometheus-config.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-prometheus-config.yml new file mode 100644 index 00000000..2e1c0156 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-prometheus-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-prometheus-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy Prometheus configuration file to remote host. +# Copies rendered prometheus.yml from build directory to Prometheus configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the prometheus.yml configuration file to the remote host. +# The configuration file is copied from the local build directory to the Prometheus +# configuration directory on the remote instance. +# +# Requirements: +# - Prometheus storage directories must exist (created by create-prometheus-storage.yml) +# - Build directory must contain rendered prometheus.yml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Prometheus configuration + hosts: all + become: true + + tasks: + - name: Copy prometheus.yml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../prometheus/prometheus.yml" + # Note: This is the host path. Inside the container, it's mounted to /etc/prometheus/ + dest: /opt/torrust/storage/prometheus/etc/prometheus.yml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify Prometheus configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/prometheus/etc/prometheus.yml + register: prometheus_config + + - name: Assert Prometheus configuration was deployed + ansible.builtin.assert: + that: + - prometheus_config.stat.exists + - prometheus_config.stat.isreg + - prometheus_config.stat.pw_name == ansible_user + fail_msg: "Prometheus configuration file was not deployed properly" + success_msg: "Prometheus configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-tracker-config.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-tracker-config.yml new file mode 100644 index 00000000..a841c548 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/deploy-tracker-config.yml @@ -0,0 +1,57 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/deploy-tracker-config.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to deploy tracker configuration file to remote host. +# Copies rendered tracker.toml from build directory to tracker configuration directory. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook deploys the tracker.toml configuration file to the remote host. +# The configuration file is copied from the local build directory to the tracker's +# configuration directory on the remote instance. +# +# Requirements: +# - Tracker storage directories must exist (created by create-tracker-storage.yml) +# - Build directory must contain rendered tracker.toml +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) + +- name: Deploy Tracker configuration + hosts: all + become: true + + tasks: + - name: Copy tracker.toml to VM + ansible.builtin.copy: + src: "{{ playbook_dir }}/../tracker/tracker.toml" + # Note: This is the host path. Inside the container, it's mounted to /var/lib/torrust/tracker/etc/ + dest: /opt/torrust/storage/tracker/etc/tracker.toml + mode: "0644" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + + - name: Verify tracker configuration file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/etc/tracker.toml + register: tracker_config + + - name: Assert tracker configuration was deployed + ansible.builtin.assert: + that: + - tracker_config.stat.exists + - tracker_config.stat.isreg + - tracker_config.stat.pw_name == ansible_user + fail_msg: "Tracker configuration file was not deployed properly" + success_msg: "Tracker configuration deployed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/init-tracker-database.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/init-tracker-database.yml new file mode 100644 index 00000000..60678367 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/init-tracker-database.yml @@ -0,0 +1,59 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/init-tracker-database.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to initialize Torrust Tracker SQLite database. +# Creates empty database file with proper ownership and permissions. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook creates an empty SQLite database file for the Torrust Tracker. +# The database file is created with proper ownership and permissions. +# +# Requirements: +# - The tracker storage directories must exist +# - The ansible_user must have write access to /opt/torrust/storage/tracker/lib/database/ +# +# Variables: +# - ansible_user: The user that will own the database file (default: current user) +# +# Creates: +# - /opt/torrust/storage/tracker/lib/database/tracker.db (SQLite database file) + +- name: Initialize Tracker Database + hosts: all + become: true + tasks: + - name: Create empty SQLite database file + ansible.builtin.file: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + state: touch + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + modification_time: preserve + access_time: preserve + + - name: Verify database file exists + ansible.builtin.stat: + path: /opt/torrust/storage/tracker/lib/database/tracker.db + register: db_file + + - name: Assert database file was created + ansible.builtin.assert: + that: + - db_file.stat.exists + - db_file.stat.isreg + - db_file.stat.pw_name == ansible_user + fail_msg: "Database file was not created properly" + success_msg: "Database file created successfully" diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/install-backup-crontab.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/install-backup-crontab.yml new file mode 100644 index 00000000..8e651051 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/install-backup-crontab.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-backup-crontab.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install backup crontab and maintenance script. +# Copies the maintenance backup script and cron entry for scheduled backups. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs the backup crontab and maintenance script on the remote host. +# The crontab entry will automatically execute backups on the configured schedule. +# +# Requirements: +# - Backup configuration files must already be deployed (via deploy-backup-config playbook) +# - Build directory must contain rendered maintenance-backup.cron and maintenance-backup.sh +# +# Variables: +# - ansible_user: The SSH user for the remote host (set automatically) +# +# Behavior: +# - maintenance-backup.sh: Installed to /usr/local/bin/ with executable permissions +# - maintenance-backup.cron: Installed to /etc/cron.d/tracker-backup (requires root) +# - tracker-backup.log: Created in /var/log/ with proper permissions for logging + +- name: Install backup crontab and maintenance script + hosts: all + become: true + + tasks: + - name: Copy maintenance backup script to /usr/local/bin/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.sh" + dest: /usr/local/bin/maintenance-backup.sh + mode: "0755" + owner: root + group: root + + - name: Copy maintenance backup cron to /etc/cron.d/ + ansible.builtin.copy: + src: "{{ playbook_dir }}/../backup/etc/maintenance-backup.cron" + dest: /etc/cron.d/tracker-backup + mode: "0644" + owner: root + group: root + + - name: Create backup log file with proper permissions + ansible.builtin.file: + path: /var/log/tracker-backup.log + state: touch + mode: "0644" + owner: root + group: root + + - name: Verify maintenance-backup.sh exists + ansible.builtin.stat: + path: /usr/local/bin/maintenance-backup.sh + register: maintenance_script + + - name: Verify maintenance-backup.cron exists + ansible.builtin.stat: + path: /etc/cron.d/tracker-backup + register: crontab_entry + + - name: Verify tracker-backup.log exists + ansible.builtin.stat: + path: /var/log/tracker-backup.log + register: backup_log + + - name: Assert backup crontab and script were installed + ansible.builtin.assert: + that: + - maintenance_script.stat.exists + - maintenance_script.stat.isreg + - maintenance_script.stat.mode == "0755" + - maintenance_script.stat.pw_name == "root" + - crontab_entry.stat.exists + - crontab_entry.stat.isreg + - crontab_entry.stat.mode == "0644" + - crontab_entry.stat.pw_name == "root" + - backup_log.stat.exists + - backup_log.stat.isreg + - backup_log.stat.mode == "0644" + fail_msg: "Backup crontab and script were not installed properly" + success_msg: "Backup crontab and script installed successfully" diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/install-docker-compose.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/install-docker-compose.yml new file mode 100644 index 00000000..1458a31e --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/install-docker-compose.yml @@ -0,0 +1,74 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker-compose.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker Compose v2 via direct download from GitHub releases. +# Modern plugin-based installation optimized for reliability in E2E testing. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# This playbook installs Docker Compose v2 directly from GitHub releases, +# which is the most reliable method for getting the modern plugin-based version + +- name: Install Docker Compose (Direct download for E2E) + hosts: all + become: yes + gather_facts: yes + + tasks: + - name: 🐋 Starting Docker Compose installation + debug: + msg: | + 🚀 Installing Docker Compose v2 via direct download on {{ inventory_hostname }} + + - name: Check if Docker is installed + command: docker --version + register: docker_check + failed_when: false + changed_when: false + + - name: Ensure Docker is installed + fail: + msg: "Docker must be installed before installing Docker Compose" + when: docker_check.rc != 0 + + - name: Create Docker CLI plugins directory + file: + path: /usr/local/lib/docker/cli-plugins + state: directory + mode: '0755' + + # Download with retries to handle transient network failures + # Retries help prevent flaky E2E tests when GitHub or network is temporarily slow + - name: Download Docker Compose v2 plugin + get_url: + url: "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" + dest: /usr/local/lib/docker/cli-plugins/docker-compose + mode: '0755' + timeout: 60 + retries: 3 + delay: 5 + register: download_result + until: download_result is succeeded + + - name: Test Docker Compose installation + command: docker compose version + register: compose_version + changed_when: false + + - name: 🎉 Docker Compose installation completed + debug: + msg: | + ✅ Docker Compose installed successfully! + Version: {{ compose_version.stdout }} + Command: docker compose (modern plugin syntax) diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/install-docker.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/install-docker.yml new file mode 100644 index 00000000..12348f6d --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/install-docker.yml @@ -0,0 +1,105 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/install-docker.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to install Docker runtime on remote host. +# Simplified installation approach optimized for E2E testing environments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Simplified Docker Installation Playbook for E2E Testing +# Based on successful Docker-in-Docker installation from: +# https://github.com/josecelano/test-docker-install-inside-vm-in-runner/blob/main/.github/workflows/test-docker-runtime-install.yml + +- name: Install Docker (Simplified for E2E) + hosts: all + gather_facts: true + become: true + + vars: + # Simple installation approach + use_simple_install: true + + tasks: + - name: 🐳 Starting simplified Docker installation + ansible.builtin.debug: + msg: "🚀 Installing Docker CE via Ubuntu repositories on {{ inventory_hostname }}" + + - name: Force update apt cache for container environment + ansible.builtin.apt: + update_cache: true + force_apt_get: true + when: ansible_os_family == "Debian" + + - name: Ensure universe repository is available + ansible.builtin.shell: | + apt-cache policy docker.io || echo "docker.io not found" + apt list --installed | grep -E "(universe|multiverse)" | head -5 || echo "No universe/multiverse packages found" + register: repo_check + changed_when: false + + - name: Display repository check results + ansible.builtin.debug: + var: repo_check.stdout_lines + + - name: Install Docker from Ubuntu repositories + ansible.builtin.apt: + name: + - docker.io + state: present + force_apt_get: true + when: ansible_os_family == "Debian" + register: docker_install + + - name: Start and enable Docker service + ansible.builtin.systemd: + name: docker + state: started + enabled: true + when: docker_install is succeeded + ignore_errors: true # Ignore in container environments where systemd might not work + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ ansible_user }}" + groups: docker + append: true + when: docker_install is succeeded + register: user_added_to_docker_group + + - name: Activate docker group membership immediately + ansible.builtin.shell: | + newgrp docker << 'EOF' + docker --version + EOF + when: user_added_to_docker_group is changed + ignore_errors: true + + - name: Test Docker installation + ansible.builtin.shell: docker --version + register: docker_test + changed_when: false + + - name: Display installation result + ansible.builtin.debug: + msg: "✅ Docker installed successfully: {{ docker_test.stdout }}" + when: docker_test is succeeded + + - name: Installation summary + ansible.builtin.debug: + msg: | + ✅ Docker installation completed! + 📦 Installed: docker.io + 👤 User '{{ ansible_user }}' added to docker group + 🔧 Group membership activated with newgrp + ℹ️ Note: Install Docker Compose separately using install-docker-compose.yml diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/inventory.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/inventory.yml new file mode 100644 index 00000000..e23a0d43 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/inventory.yml @@ -0,0 +1,120 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/inventory.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/inventory/context/mod.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible inventory file defining hosts (servers/VMs/containers) for deployment. +# Contains SSH connection details and host variables for Ansible playbooks. +# Supports both LXD VMs (OpenTofu workflow) and Docker containers (E2E testing). +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Ansible Inventory File (YAML format) +# This file defines the hosts (servers/VMs/containers) that Ansible will manage +# and how to connect to them +# +# 🔗 DUAL INFRASTRUCTURE SUPPORT: +# This template supports two different infrastructure types: +# +# A) LXD VIRTUAL MACHINES (Testing & Production/OpenTofu workflow) +# B) DOCKER CONTAINERS (E2E testing/testcontainers workflow) +# +# 🔗 COMPLETE OPENTOFU + LXD VM WORKFLOW: +# +# 1. OpenTofu Provisioning (config/tofu/lxd/main.tf): +# - Creates LXD VM named "torrust-tracker-vm" (variable: container_name) +# - Applies cloud-init configuration from cloud-init.yml +# - Assigns dynamic IP address via lxdbr0 network +# +# 2. Cloud-init Setup (config/tofu/lxd/cloud-init.yml): +# - Creates user "torrust" with sudo privileges +# - Installs SSH public key for authentication +# - Enables SSH service for remote access +# +# 3. Ansible Connection (this file): +# - Uses dynamic IP from step 1 (ansible_host) +# - Connects as user from step 2 (ansible_user) +# - Authenticates with private key matching step 2 (ansible_ssh_private_key_file) +# - Uses standard SSH port 22 (ansible_port) +# +# 🔗 COMPLETE TESTCONTAINERS + DOCKER WORKFLOW: +# +# 1. Testcontainers Provisioning (src/e2e/containers/provisioned.rs): +# - Creates Docker container with SSH server +# - Maps random host port to container port 22 for SSH access +# - Assigns container IP via Docker bridge network +# +# 2. Container Setup (docker/provisioned-instance/): +# - Pre-configured container image with SSH server +# - User "torrust" with sudo privileges already configured +# - SSH public key pre-installed for authentication +# - Cloud-init emulated via completion marker (no actual cloud-init execution) +# +# 3. Ansible Connection (this file): +# - Uses Docker host IP (127.0.0.1) as ansible_host +# - Connects as pre-configured user "torrust" (ansible_user) +# - Authenticates with matching private key (ansible_ssh_private_key_file) +# - Uses dynamic mapped port from testcontainers (ansible_port) + +# 'all' is the top-level group that contains all hosts +all: + # 'hosts' section defines individual machines + hosts: + # Host name: 'torrust-tracker-vm' (this is how we refer to this host in playbooks) + # 🔗 LXD VM: This name matches var.instance_name in config/tofu/lxd/main.tf + # 🔗 CONTAINER: This name is used consistently across both LXD VMs and Docker containers + torrust-tracker-vm: + # The actual IP address or hostname to connect to + # ⚠️ IMPORTANT: This IP varies by infrastructure type + # 🔗 LXD VM: IP assigned by lxd_instance.torrust_vm via lxdbr0 network + # 🔗 CONTAINER: Docker host IP (127.0.0.1) for testcontainers + # 🔗 DISCOVERY (LXD): Find current IP with: lxc list torrust-vm + # 🔗 AUTOMATION (LXD): lxc list torrust-vm -f json | jq -r '.[0].state.network.eth0.addresses[0].address' + ansible_host: 203.0.113.1 + + # SSH port to connect to (varies by infrastructure type) + # 🔗 LXD VM: Standard SSH port 22 + # 🔗 CONTAINER: Dynamic mapped port from testcontainers (e.g., 32768, 32769, etc.) + # 🔗 TESTCONTAINERS: Retrieved via container.get_host_port_ipv4(22) + ansible_port: 22 + + # The username to use when connecting via SSH + # 🔗 LXD VM: This must match the user created in cloud-init + # 🔗 CONTAINER: This must match the pre-configured user in the container image + # 🔗 CONFIGURED: Set via ssh_credentials.username in environment config + ansible_user: torrust + + # Connection method - we're using SSH + ansible_connection: ssh + + # Path to the private SSH key file + # 🔗 LXD VM: This private key corresponds to the PUBLIC key in cloud-init + # 🔗 CONTAINER: This private key corresponds to the PUBLIC key in container image + # 🔗 EXACT MATCH (LXD): config/tofu/lxd/cloud-init.yml -> users[0].ssh_authorized_keys[0] + # 🔗 EXACT MATCH (CONTAINER): docker/provisioned-instance/ -> pre-installed public key + # 🔗 KEY PAIR: ~/.ssh/testing_rsa (private) <-> public key in infrastructure + ansible_ssh_private_key_file: /home/josecelano/Documents/git/committer/me/github/torrust/torrust-tracker-deployer-agent-01/fixtures/testing_rsa + + # Additional SSH arguments for this host + # StrictHostKeyChecking=no skips host key verification (lab/testing use only) + # ⚠️ SECURITY: Only use this setting in development/testing environments + # 🔗 PURPOSE: Avoids SSH fingerprint prompts for dynamic infrastructure + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + + # 'vars' section defines variables that apply to all hosts in this group + vars: + # Tell Ansible which Python interpreter to use on the remote hosts + # Most modern Linux systems use python3 + # 🔗 COMPATIBILITY: Works for both LXD VMs and Docker containers + ansible_python_interpreter: /usr/bin/python3 diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/run-compose-services.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/run-compose-services.yml new file mode 100644 index 00000000..3f800923 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/run-compose-services.yml @@ -0,0 +1,109 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/run-compose-services.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to start Docker Compose services on remote host. +# Launches the complete application stack with all configured services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +- name: Run Docker Compose Services + hosts: all + gather_facts: false + become: true + + vars: + deploy_dir: /opt/torrust + + tasks: + - name: 🚀 Starting Docker Compose services + ansible.builtin.debug: + msg: "Starting Docker Compose services in {{ deploy_dir }}" + + - name: Verify docker-compose.yml exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/docker-compose.yml" + register: compose_file_check + + - name: Fail if docker-compose.yml not found + ansible.builtin.fail: + msg: | + docker-compose.yml not found at {{ deploy_dir }}/docker-compose.yml + + Please run the 'release' command first to deploy Docker Compose files: + cargo run -- release + when: not compose_file_check.stat.exists + + - name: Verify .env file exists + ansible.builtin.stat: + path: "{{ deploy_dir }}/.env" + register: env_file_check + + - name: Fail if .env file not found + ansible.builtin.fail: + msg: | + .env file not found at {{ deploy_dir }}/.env + + Docker Compose requires a .env file with environment variables. + Please run the 'release' command first to deploy the .env file: + cargo run -- release + + For more information, see: + https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute + when: not env_file_check.stat.exists + + - name: Pull Docker images + ansible.builtin.command: + cmd: docker compose pull + chdir: "{{ deploy_dir }}" + register: pull_result + changed_when: "'Pulled' in pull_result.stdout or 'Downloaded' in pull_result.stdout" + failed_when: pull_result.rc != 0 + + - name: Start Docker Compose services + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ deploy_dir }}" + register: compose_up_result + changed_when: "'Started' in compose_up_result.stderr or 'Creating' in compose_up_result.stderr" + failed_when: compose_up_result.rc != 0 + + - name: Wait for services to be healthy + ansible.builtin.command: + cmd: docker compose ps --format json + chdir: "{{ deploy_dir }}" + register: compose_status + retries: 30 + delay: 2 + until: > + (compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length == 0) or + (compose_status.stdout | from_json | selectattr('Health', 'defined') | selectattr('Health', 'equalto', 'healthy') | list | length == + compose_status.stdout | from_json | selectattr('Health', 'defined') | list | length) + changed_when: false + ignore_errors: true + + - name: Get running containers status + ansible.builtin.command: + cmd: docker compose ps + chdir: "{{ deploy_dir }}" + register: final_status + changed_when: false + + - name: Display service status + ansible.builtin.debug: + msg: | + ✅ Docker Compose services started! + 📁 Working directory: {{ deploy_dir }} + + Container status: + {{ final_status.stdout }} diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/update-apt-cache.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/update-apt-cache.yml new file mode 100644 index 00000000..27922fb7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/update-apt-cache.yml @@ -0,0 +1,94 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/update-apt-cache.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to update APT package cache with retries and diagnostics. +# Prepares system for package installations after VM provisioning. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH INFRASTRUCTURE: +# 1. This playbook runs after VM provisioning (OpenTofu) and cloud-init completion +# 2. It prepares the system for package installations by updating the apt cache +# 3. Extracted from other playbooks to isolate network-sensitive operations + +# Define which hosts this playbook will run on +- name: Update APT Package Cache + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: true # Collect system information to determine OS and version + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 0: Network diagnostics for CI troubleshooting + - name: Check network connectivity and DNS resolution + ansible.builtin.shell: | + echo "=== Network Diagnostics ===" + echo "Testing DNS resolution..." + nslookup archive.ubuntu.com || echo "DNS resolution failed" + echo "Testing connectivity to Ubuntu repositories..." + curl -I https://archive.ubuntu.com/ubuntu/ --connect-timeout 10 || echo "Ubuntu repo unreachable" + echo "Testing connectivity to Docker repositories..." + curl -I https://download.docker.com --connect-timeout 10 || echo "Docker repo unreachable" + echo "Current apt sources:" + cat /etc/apt/sources.list + register: network_diagnostics + changed_when: false + ignore_errors: true + + - name: Display network diagnostics + ansible.builtin.debug: + var: network_diagnostics.stdout_lines + when: network_diagnostics is defined + + # Task 1: Update package cache with retries and better error handling + - name: Update apt package cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 # Cache valid for 1 hour + force_apt_get: true # Force using apt-get instead of aptitude for better CI compatibility + register: apt_update_result + retries: 3 + delay: 10 + until: apt_update_result is succeeded + when: ansible_os_family == "Debian" + ignore_errors: false # Fail if apt update ultimately fails + + # Task 1.1: Fallback apt update with different approach if needed + - name: Fallback apt update with apt-get directly + ansible.builtin.command: apt-get update + register: apt_get_update + retries: 2 + delay: 15 + until: apt_get_update.rc == 0 + when: + - ansible_os_family == "Debian" + - apt_update_result is failed + ignore_errors: false + + # Task 2: Update package cache after adding repository with retries + - name: Update apt package cache (final update) + ansible.builtin.apt: + update_cache: true + force_apt_get: true # Force using apt-get for better CI compatibility + register: apt_update_final + retries: 3 + delay: 10 + until: apt_update_final is succeeded + when: ansible_os_family == "Debian" + + # Task 3: Display apt update completion status + - name: Display apt update completion status + ansible.builtin.debug: + msg: "APT cache update completed successfully" + when: apt_update_final is succeeded or apt_get_update is succeeded diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/variables.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/variables.yml new file mode 100644 index 00000000..0935a131 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/variables.yml @@ -0,0 +1,45 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/variables.yml.tera +# Rust Wrapper: src/infrastructure/templating/ansible/template/wrappers/variables/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Centralized Ansible variables used across playbooks for system configuration. +# Contains dynamic values like ports, enablement flags, and deployment settings. +# Follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# Centralized Ansible Variables +# This file contains all dynamic variables used across Ansible playbooks. +# It follows the same pattern as OpenTofu's variables.tfvars.tera for consistency. +# +# NOTE: The inventory file (inventory.yml.tera) cannot use this file because +# Ansible inventories don't support vars_files. Only playbooks can use vars_files. + +# System Configuration +ssh_port: 22 + +# Deployment Directory +deploy_dir: /opt/torrust + +# Service Enablement Flags +grafana_enabled: true +mysql_enabled: false + +# Tracker Firewall Configuration +tracker_udp_ports: + - 6969 +tracker_http_ports: + - 7070 +tracker_api_port: 1212 diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/wait-cloud-init.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/wait-cloud-init.yml new file mode 100644 index 00000000..b0fce32a --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/ansible/wait-cloud-init.yml @@ -0,0 +1,76 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/ansible/wait-cloud-init.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Ansible playbook to wait for cloud-init completion before proceeding with VM management. +# Ensures cloud-init setup is complete before Ansible tasks execute. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +--- +# 🔗 RELATIONSHIP WITH OPENTOFU: +# 1. OpenTofu (templates/tofu/lxd/main.tf) provisions the VM/container +# 2. OpenTofu configures cloud-init (templates/tofu/lxd/cloud-init.yml) during provisioning +# 3. Cloud-init runs inside the VM to set up users, SSH keys, and basic configuration +# 4. This Ansible playbook waits for cloud-init to complete before proceeding +# 5. Once cloud-init is done, Ansible can safely manage the VM + +# Define which hosts this playbook will run on +- name: Wait for cloud-init completion + hosts: all # Run on all hosts defined in inventory.yml + gather_facts: false # Don't collect system info initially (speeds up start) + become: true # Use sudo/root privileges for system-level operations + + # List of tasks to execute in order + tasks: + # Task 1: Wait for cloud-init to create its completion marker file + # 🔗 CLOUD-INIT RELATIONSHIP: This file is created when cloud-init finishes + # all tasks defined in templates/tofu/lxd/cloud-init.yml + - name: Wait for cloud-init to finish + ansible.builtin.wait_for: + path: /var/lib/cloud/instance/boot-finished # File created when cloud-init completes + timeout: 300 # Wait up to 5 minutes (300 seconds) + register: cloud_init_result # Store the result for later use + + # Task 2: Display success message if cloud-init completed + - name: Display cloud-init completion status + ansible.builtin.debug: + msg: "Cloud-init has completed successfully!" + when: cloud_init_result is succeeded # Only run if previous task succeeded + + # Task 3: Check cloud-init status using the built-in command + - name: Check cloud-init status + ansible.builtin.command: cloud-init status --wait + register: cloud_init_status # Store command output + changed_when: false # This command doesn't change system state + + # Task 4: Show the cloud-init status output + - name: Display cloud-init status + ansible.builtin.debug: + var: cloud_init_status.stdout # Show the standard output from the command + + # Task 5: Collect basic information about the system + - name: Gather basic system information + ansible.builtin.setup: + gather_subset: + - min # Basic facts like hostname, OS + - network # Network configuration and IP addresses + + # Task 6: Display useful system information + - name: Display system information + ansible.builtin.debug: + msg: | + Hostname: {{ ansible_hostname }} + Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }} + Architecture: {{ ansible_architecture }} + IP Address: {{ ansible_default_ipv4.address }} + Python Version: {{ ansible_python_version }} diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/docker-compose/.env b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/docker-compose/.env new file mode 100644 index 00000000..46dd0d66 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/docker-compose/.env @@ -0,0 +1,44 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/.env.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/env/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose environment variables for service configuration. +# Includes tracker credentials, database settings, and optional service configs. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# ============================================================================= +# Tracker Service Configuration +# ============================================================================= + +# Path to the tracker TOML configuration file inside the container +TORRUST_TRACKER_CONFIG_TOML_PATH='/etc/torrust/tracker/tracker.toml' + +# Database driver type - tells the container entrypoint which config template to use +# Must match the driver specified in tracker.toml +# Uses standardized TORRUST_TRACKER_CONFIG_OVERRIDE_* naming convention +TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER='sqlite3' + +# Admin API token for tracker HTTP API access +# This overrides the admin token in the tracker configuration file +TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN='MyAccessToken' + +# ============================================================================= +# Grafana Service Configuration +# ============================================================================= + +# Grafana admin credentials +# WARNING: Change default credentials in production deployments for security +GF_SECURITY_ADMIN_USER='admin' +GF_SECURITY_ADMIN_PASSWORD='admin' diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/docker-compose/docker-compose.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/docker-compose/docker-compose.yml new file mode 100644 index 00000000..1476c1bc --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/docker-compose/docker-compose.yml @@ -0,0 +1,135 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/docker-compose/docker-compose.yml.tera +# Rust Wrapper: src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Docker Compose service definitions for Torrust Tracker deployment. +# Includes tracker, optional MySQL, Prometheus, Grafana, and Caddy services. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# IMPORTANT: Environment Variable Injection Pattern +# +# All configuration values that may need to be changed during maintenance +# should be injected via environment variables from the .env file, not +# hardcoded in this docker-compose template. +# +# Pattern to follow: +# CORRECT: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +# INCORRECT: - MYSQL_ROOT_PASSWORD=hardcoded_value +# +# Rationale: +# - System administrators can modify .env values and restart services without +# regenerating templates +# - Supports runtime configuration changes without redeployment +# - Follows Docker Compose best practices for configuration management +# - Separates template generation (deploy-time) from configuration (runtime) +# +# See ADR: docs/decisions/environment-variable-injection-in-docker-compose.md + +# Common service defaults (YAML anchor for DRY configuration) +x-defaults: &defaults + tty: true + restart: unless-stopped + logging: + options: + max-size: "10m" + max-file: "10" + +services: + + tracker: + <<: *defaults + # TODO: Pin to stable v4.0.0 when released (currently using develop tag) + # Tracking issue: https://github.com/torrust/torrust-tracker-deployer/issues/TBD + # Rationale: The develop tag is mutable and introduces deployment non-reproducibility. + # Pinning to a stable release ensures predictable deployments and easier rollback. + image: torrust/tracker:develop + container_name: tracker + environment: + - USER_ID=1000 + - TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=${TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER} + - TORRUST_TRACKER_CONFIG_TOML_PATH=${TORRUST_TRACKER_CONFIG_TOML_PATH} + - TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN} + networks: + - metrics_network + ports: + # BitTorrent UDP announce + - "6969:6969/udp" + # HTTP tracker announce + - "7070:7070" + # HTTP API (stats/whitelist) + - "1212:1212" + volumes: + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z + + prometheus: + <<: *defaults + image: prom/prometheus:v3.5.0 + container_name: prometheus + networks: + - metrics_network + - visualization_network + ports: + # Prometheus metrics (localhost only) + - "127.0.0.1:9090:9090" + # Grafana accesses Prometheus via Docker network: http://prometheus:9090 + # Host can access for validation via: curl http://localhost:9090 + volumes: + - ./storage/prometheus/etc:/etc/prometheus:Z + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + depends_on: + - tracker + + grafana: + <<: *defaults + image: grafana/grafana:12.3.1 + container_name: grafana + networks: + - visualization_network + ports: + # Grafana dashboard + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} + volumes: + - ./storage/grafana/data:/var/lib/grafana + - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + prometheus: + condition: service_healthy + +# Networks are derived from service configurations in Rust code. +# See: src/domain/topology/network.rs for security rationale. + +networks: + # Metrics scraping: Tracker ↔ Prometheus + metrics_network: + driver: bridge + # Dashboard queries: Prometheus ↔ Grafana + visualization_network: + driver: bridge diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/dashboards/torrust.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/dashboards/torrust.yml new file mode 100644 index 00000000..6310f515 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/dashboards/torrust.yml @@ -0,0 +1,31 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/dashboards/torrust.yml +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana dashboard provisioning configuration for Torrust Tracker dashboards. +# Defines the dashboard provider and folder structure. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +providers: + - name: "Torrust Dashboards" + orgId: 1 + folder: "Torrust Tracker" + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards/torrust + foldersFromFilesStructure: false diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/dashboards/torrust/metrics.json b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/dashboards/torrust/metrics.json new file mode 100644 index 00000000..c95b981b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/dashboards/torrust/metrics.json @@ -0,0 +1,1424 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using metric endpoint:\n\nhttps://tracker.example.com/api/v1/metrics?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "tracker_core_persistent_torrents_downloads_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_torrents_total{job=\"tracker_metrics\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"seeder\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "swarm_coordination_registry_peer_connections_total{job=\"tracker_metrics\", peer_role=\"leecher\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_accepted_total{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_errors_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"connect\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"announce\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "avg(udp_tracker_server_performance_avg_processing_time_ns{job=\"tracker_metrics\", request_kind=\"scrape\", server_binding_address_ip_family=\"inet\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP4 Banned Requests (per sec)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_banned_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Banned Requests (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_received_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_responses_sent_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "UDP4 Requests and Responses (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(udp_tracker_server_ips_banned_total{job=\"tracker_metrics\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(udp_tracker_server_requests_aborted_total{job=\"tracker_metrics\", server_binding_address_ip_family=\"inet\"}[15m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (metrics)", + "uid": "deogmiudufm68d", + "version": 50, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/dashboards/torrust/stats.json b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/dashboards/torrust/stats.json new file mode 100644 index 00000000..c53ea60f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/dashboards/torrust/stats.json @@ -0,0 +1,1420 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Using stats endpoint:\n\nhttps://tracker.example.com/api/v1/stats?token=MyAccessToken&format=prometheus", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "completed{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Completed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "torrents{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Torrents", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "seeders{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Seeders", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "leechers{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Leechers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_connections_handled{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Connections (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_announces_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Announces (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_scrapes_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Scrapes (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "Request per second in 15 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(udp4_errors_handled[15m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP4 Errors (per sec)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Connect Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_connect_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Connect Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Announce Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_announce_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Announce Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Average Scrape Processing Time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ns" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_avg_scrape_processing_time_ns{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Average Scrape Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP banned requests (per second)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 14 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_banned{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP banned requests (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP 4 requests and responses", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp4_requests{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "expr": "rate(udp4_responses[15m])", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "UDP4 requests and responses (per second)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "UDP Banned IPs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 19 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "udp_banned_ips_total{job=\"tracker_stats\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP Banned IPs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(udp_requests_aborted{job=\"tracker_stats\"}[15m])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UDP aborted requests (per second)", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Torrust Live Demo Tracker (stats)", + "uid": "de6lx6hce8fswc", + "version": 93, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/datasources/prometheus.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..85a223d6 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,33 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/grafana/provisioning/datasources/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/grafana/template/wrapper/datasource/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Grafana datasource configuration for Prometheus. Defines the connection +# settings and query parameters for Grafana to access Prometheus metrics data. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false + jsonData: + timeInterval: "15s" + httpMethod: POST diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/prometheus/prometheus.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/prometheus/prometheus.yml new file mode 100644 index 00000000..3740bef7 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/prometheus/prometheus.yml @@ -0,0 +1,41 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/prometheus/prometheus.yml.tera +# Rust Wrapper: src/infrastructure/templating/prometheus/template/wrapper/prometheus_config/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Prometheus metrics scraping configuration for Torrust Tracker monitoring. +# Defines scrape targets for tracker statistics and operational metrics endpoints. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +global: + scrape_interval: 15s # How often to scrape metrics from targets + +scrape_configs: + # Tracker Statistics - Aggregate metrics about tracker state + - job_name: "tracker_stats" + metrics_path: "/api/v1/stats" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] + + # Tracker Metrics - Detailed operational metrics + - job_name: "tracker_metrics" + metrics_path: "/api/v1/metrics" + params: + token: ["MyAccessToken"] + format: ["prometheus"] + static_configs: + - targets: ["tracker:1212"] diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/tofu/lxd/cloud-init.yml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/tofu/lxd/cloud-init.yml new file mode 100644 index 00000000..83718d56 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/tofu/lxd/cloud-init.yml @@ -0,0 +1,59 @@ +#cloud-config +# +# ============================================================================ +# CRITICAL: The #cloud-config line above MUST be the first line in this file. +# Cloud-init requires this exact string on line 1 to recognize the +# file as a cloud-config. DO NOT add any content before it. +# ============================================================================ +# +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated at: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/common/cloud-init.yml.tera +# Rust Wrapper: src/infrastructure/templating/tofu/template/common/wrappers/cloud_init/context.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# Cloud-init configuration for VM provisioning. Shared by all providers +# (LXD, Hetzner) to ensure consistent VM initialization. Creates a user +# with SSH access and sudo privileges. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +# Template Variables (Tera syntax): +# - username: The SSH user to create +# - ssh_public_key: The public SSH key content for authentication +# - ssh_port: The SSH service port (default: 22) +# +# Note: Package updates are commented out for faster VM creation during +# development. Uncomment for production deployments. + +# Commented out for faster VM creation during development +# package_update: true +# package_upgrade: true + +# packages: +# - curl +# - wget +# - git +# - htop +# - vim + +users: + - name: torrust + groups: sudo + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + ssh_authorized_keys: + # SSH public key injected from SshConfig.ssh_pub_key_path + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw16sai+XVnawp/P/Q23kcXKekygZ6ALmQAyslREo6kbG8s5RScsmbQqOQEcIwnV2Vo88eeWVzX0N0H1dIczRa/ezijBEsGefthzmz9Ix/vM4lodzTPQFtW8c2eYw7ESy12/2x5//UQQ3mxawEWsz5Ri8XuyBEy/Xh7xH/KpoektaocIOt2/WdCe8CvZdMLd7AviGcTdHFWRiOVrmHM1Pd8znqeA3/1KQP/M4Ae5q21oPjchGjVfPkGh/e62Wt+Wo/2lT30AyMO7JHA1tB1W4xANRQkOd1Kb/TrDLXfg0PaHQ+Irmycjp/H4KkcdB06nzYawXMN5csd/5TWKwkb9/vofp6GQNP731U8+JR4cxRfD107KoHroDSJpG2Fanb2PVBkSXAiJl29YrtoP9vUtSIemQCD/aXFtTcpSv7Y16bdp7v+0adCEHwBmodm9GzLL808FpI2ZCzCi+Ae98P3z+yPCxbrnVAahU8AM2NSbrfyH1w2eb4hJ22oPjdd//tBYtkE1TZBw+i3n0vRn04s5BfPRwwj5GISxacTOZm/YWvoE4UU9axtFXOtMUniVKL3ycA+LEfK7C4velOKbluyL8fYYu4pUxHnYOOkYYeRoi2jf3oagbABOpznloPd93wYP3NoUpIdtMZW+iCF0NnZkVLC9lm1FbTcnmrfNzFtGVKCQ== testing@torrust-testing-infra + + + diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/tofu/lxd/main.tf b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/tofu/lxd/main.tf new file mode 100644 index 00000000..c69627f8 --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/tofu/lxd/main.tf @@ -0,0 +1,133 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tofu/lxd/main.tf +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# OpenTofu main configuration for LXD provider. +# Defines VM instances, profiles, storage, and networking for local LXD deployments. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +terraform { + required_providers { + lxd = { + source = "terraform-lxd/lxd" + version = "~> 2.0" + } + } + required_version = ">= 1.0" +} + +# Configure the LXD Provider +provider "lxd" { + # Use local LXD daemon via unix socket +} + +# Variables +variable "instance_name" { + description = "Name of the LXD instance" + type = string + default = "torrust-tracker-vm" +} + +variable "profile_name" { + description = "Name of the LXD profile" + type = string + default = "torrust-profile" +} + +variable "image" { + description = "LXD image to use" + type = string + default = "ubuntu:24.04" +} + +# Create a profile for our container with cloud-init support +resource "lxd_profile" "torrust_profile" { + name = var.profile_name + + config = { + "user.user-data" = file("${path.module}/cloud-init.yml") + "limits.memory" = "2GB" + "limits.cpu" = "2" + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = "default" + size = "10GB" + } + } + + device { + name = "eth0" + type = "nic" + properties = { + network = "lxdbr0" + name = "eth0" + } + } +} + +# Create the LXD virtual machine +resource "lxd_instance" "torrust_vm" { + name = var.instance_name + image = var.image + type = "virtual-machine" + profiles = [lxd_profile.torrust_profile.name] + + config = { + "boot.autostart" = "true" + "security.secureboot" = "false" + } + + # Give VM more time to start up + wait_for_network = true +} + +# Output information about the container +# IMPORTANT: This output is parsed by src/opentofu/json_parser.rs +# The output name "instance_info" and all fields (name, image, status, ip_address) +# are required by the parser and must remain present with these exact names. +output "instance_info" { + description = "Information about the created container" + value = { + name = lxd_instance.torrust_vm.name + image = lxd_instance.torrust_vm.image + status = lxd_instance.torrust_vm.status + ip_address = lxd_instance.torrust_vm.ipv4_address + } + depends_on = [lxd_instance.torrust_vm] +} + +output "connection_commands" { + description = "Commands to connect to the container" + value = [ + "lxc exec ${var.instance_name} -- /bin/bash", + "lxc exec ${var.instance_name} -- whoami", + "lxc exec ${var.instance_name} -- systemctl status", + "lxc list ${var.instance_name}" + ] +} + +output "test_commands" { + description = "Commands to test the container functionality" + value = [ + "lxc exec ${var.instance_name} -- cat /etc/os-release", + "lxc exec ${var.instance_name} -- df -h", + "lxc exec ${var.instance_name} -- free -h", + "lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running", + "lxc exec ${var.instance_name} -- cloud-init status" + ] +} diff --git a/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/tracker/tracker.toml b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/tracker/tracker.toml new file mode 100644 index 00000000..24e26e8f --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/15-sqlite-monitoring/tracker/tracker.toml @@ -0,0 +1,61 @@ +# ============================================================================ +# Torrust Tracker Deployer - Generated Configuration +# ============================================================================ +# +# This file was generated by the Torrust Tracker Deployer. +# Generated: 2026-02-13T10:02:24Z +# +# DOCUMENTATION: +# Repository: https://github.com/torrust/torrust-tracker-deployer +# Template: templates/tracker/tracker.toml.tera +# Rust Wrapper: src/infrastructure/templating/tracker/template/wrapper/tracker_config/template.rs +# API Docs: https://docs.rs/torrust-tracker-deployer/latest/ +# +# DESCRIPTION: +# This file configures the Torrust Tracker BitTorrent tracker service. +# It defines database settings, tracker policies, network configuration, +# and API endpoints for tracker operation. +# +# For configuration options and valid values, see the API documentation link above. +# ============================================================================ + +[metadata] +app = "torrust-tracker" +purpose = "configuration" +schema_version = "2.0.0" + +[logging] +threshold = "info" + +[core] +listed = false +private = false + +[core.tracker_policy] +persistent_torrent_completed_stat = true + +[core.announce_policy] +interval = 300 +interval_min = 300 + +[core.net] +# Whether the tracker expects X-Forwarded-For headers from a reverse proxy. +# Set to true when ANY HTTP tracker uses Caddy TLS termination (use_tls_proxy: true). +# Note: This is a global setting - see docs/external-issues/tracker/on-reverse-proxy-global-setting.md +on_reverse_proxy = false + +[core.database] +driver = "sqlite3" +# Note: This path is inside the Docker container. The host path is /opt/torrust/storage/tracker/database/ +# which is mounted to /var/lib/torrust/tracker/ inside the container. +path = "/var/lib/torrust/tracker/database/tracker.db" +[[udp_trackers]] +bind_address = "0.0.0.0:6969" +[[http_trackers]] +bind_address = "0.0.0.0:7070" + +[http_api] +bind_address = "0.0.0.0:1212" + +[health_check_api] +bind_address = "127.0.0.1:1313" diff --git a/docs/ai-training/dataset/rendered-templates/README.md b/docs/ai-training/dataset/rendered-templates/README.md new file mode 100644 index 00000000..c364d58b --- /dev/null +++ b/docs/ai-training/dataset/rendered-templates/README.md @@ -0,0 +1,110 @@ +# AI Training Outputs - Rendered Deployment Artifacts + +This directory contains the **rendered deployment artifacts** for all AI training example configurations. + +## Purpose + +These outputs complete the AI training dataset with **input/output pairs**: + +- **Input**: Environment configuration JSON files in [`../environment-configs/`](../environment-configs/) +- **Output**: Rendered deployment templates (this directory) + +This mapping demonstrates how the deployer transforms high-level configuration into concrete deployment artifacts. + +## Structure + +Each subdirectory corresponds to an example configuration: + +```text +outputs/ +├── 01-minimal-lxd/ # Outputs for environment-configs/01-minimal-lxd.json +├── 02-full-stack-lxd/ # Outputs for environment-configs/02-full-stack-lxd.json +├── 03-minimal-hetzner/ # Outputs for environment-configs/03-minimal-hetzner.json +└── ... # (15 total) +``` + +### Artifact Structure + +Each output directory contains rendered templates for all deployment services: + +```text +01-minimal-lxd/ +├── ansible/ # Ansible playbooks and inventory +│ ├── ansible.cfg +│ ├── inventory.yml +│ ├── variables.yml +│ ├── install-docker.yml +│ └── ... +├── docker-compose/ # Docker Compose configuration +│ ├── docker-compose.yml +│ └── .env +├── grafana/ # Grafana dashboards and datasources +│ └── provisioning/ +├── prometheus/ # Prometheus configuration +│ └── prometheus.yml +├── tofu/ # OpenTofu infrastructure definitions +│ ├── lxd/ # (for LXD provider) +│ └── hetzner/ # (for Hetzner provider) +└── tracker/ # Torrust Tracker configuration + └── tracker.toml +``` + +## Benefits + +**For AI Agents:** + +- **Few-shot learning**: Full input/output examples for configuration generation +- **Pattern recognition**: See how config options map to rendered templates +- **Diff analysis**: Compare outputs to understand configuration impact + +**For Humans:** + +- **Documentation**: Concrete examples of what gets deployed +- **Debugging**: Reference to verify expected template content +- **Learning**: Understand the deployer's transformation process + +## Generation + +These outputs are generated automatically using: + +```bash +./scripts/generate-ai-training-outputs.sh +``` + +The script: + +1. Reads each example from `docs/ai-training/dataset/environment-configs/` +2. Replaces generic SSH paths with project fixture paths +3. Calls `render` command with placeholder IP `203.0.113.1` (RFC 5737 TEST-NET-1) +4. Outputs artifacts to corresponding subdirectory here + +**Regeneration**: Run the script to update outputs when: + +- Example configurations change +- Templates are modified +- New features are added + +## Placeholder Values + +All outputs use safe placeholder values: + +- **IP Address**: `203.0.113.1` (RFC 5737 documentation range) +- **SSH Keys**: Project fixture keys from `fixtures/testing_rsa*` +- **API Tokens**: Placeholder strings (e.g., `PLACEHOLDER_API_TOKEN_FOR_TESTING`) +- **Passwords**: Example values (e.g., `admin-password`, `tracker_user_password`) + +**These are NOT production values** - they're for documentation and training purposes only. + +## Size + +Total size: ~4.1 MB for all 15 examples (reasonable for git storage). + +## Maintenance + +When templates change significantly: + +1. Run regeneration script: `./scripts/generate-ai-training-outputs.sh` +2. Review diffs to ensure changes are expected +3. Commit updated outputs with template changes + +This keeps the dataset synchronized with the current deployer version. diff --git a/docs/ai-training/questionnaire.md b/docs/ai-training/questionnaire.md new file mode 100644 index 00000000..93ef88d2 --- /dev/null +++ b/docs/ai-training/questionnaire.md @@ -0,0 +1,494 @@ +# Environment Configuration Questionnaire + +This structured questionnaire helps AI agents and users gather all required information to create a valid environment configuration for torrust-tracker-deployer. Follow the decision tree to collect configuration details systematically. + +## Basic Information + +### 1. Environment Name + +**Question**: What name do you want for this environment? + +**Constraints**: + +- Pattern: Lowercase letters (a-z), numbers (0-9), and hyphens (-) only +- Length: 3-50 characters +- Cannot start or end with hyphens +- Cannot start with numbers +- Used for resource naming and organization + +**Examples**: + +- `dev` - Development environment +- `production` - Production deployment +- `staging-01` - First staging environment +- `test-mysql` - Testing with MySQL database + +**Field**: `environment.name` + +### 2. Environment Description (Optional) + +**Question**: How would you describe this environment's purpose? (2-3 sentences recommended) + +**Purpose**: Helps document: + +- Use case: What this environment is designed for +- Key decisions: Why certain values were chosen +- Context: When this environment is appropriate + +**Examples**: + +- "Minimal development setup with SQLite and UDP/HTTP trackers. No HTTPS or monitoring. Ideal for local testing." +- "Production-ready deployment with MySQL, full monitoring stack (Prometheus + Grafana), and HTTPS for all services. Includes daily backups with 7-day retention." +- "UDP-only tracker optimized for high-performance scenarios. No HTTP endpoints to minimize overhead." + +**Field**: `environment.description` + +## Infrastructure Provider + +### 3. Provider Selection + +**Question**: Which infrastructure provider do you want to use? + +**Options**: + +- **LXD** (local development, testing, on-premises) + - Pros: Fast provisioning, no cloud costs, local control + - Cons: Requires LXD installation, limited to local/network resources + - Best for: Development, testing, on-premises deployments + +- **Hetzner** (cloud production) + - Pros: Public IP, cloud infrastructure, scalable + - Cons: Costs per hour, requires API token + - Best for: Production deployments, public-facing services + +**Field**: `provider.provider` + +#### 3a. If LXD Selected + +**Question**: What LXD profile name should be used? + +**Constraints**: + +- Pattern: Lowercase letters, numbers, and hyphens +- Typically prefixed with `torrust-profile-` for organization +- Must not conflict with existing LXD profiles + +**Example**: `torrust-profile-dev` for environment named `dev` + +**Field**: `provider.profile_name` + +**Default Behavior**: Uses default LXD bridge network automatically + +#### 3b. If Hetzner Selected + +**Questions**: + +1. **API Token**: What is your Hetzner API token? + - **Security**: Use a read-write token with server creation permissions + - **Field**: `provider.api_token` + +2. **Server Location**: Which Hetzner datacenter location? + - Options: `nbg1` (Nuremberg), `fsn1` (Falkenstein), `hel1` (Helsinki), `ash` (Ashburn, VA) + - Default: `nbg1` + - **Field**: `provider.location` + +3. **Server Type**: Which server size? + - Options: `cx22` (2 vCPU, 4 GB RAM), `cx32` (4 vCPU, 8 GB RAM), `cx42` (8 vCPU, 16 GB RAM) + - Default: `cx22` + - **Field**: `provider.server_type` + +4. **Image**: Which operating system image? + - Default: `ubuntu-24.04` (Ubuntu 24.04 LTS) + - **Field**: `provider.image` + +## Database Configuration + +### 4. Database Type + +**Question**: What database do you want to use? + +**Options**: + +- **SQLite** (file-based) + - Pros: Simpler setup, no credentials, single-file storage + - Cons: Lower concurrent performance for high loads + - Best for: Small deployments, testing, development + - **Driver**: `sqlite3` + +- **MySQL** (server-based) + - Pros: Better for high-load, production deployments, concurrent access + - Cons: Requires credentials, separate container + - Best for: Production, high-traffic trackers + - **Driver**: `mysql` + +**Field**: `tracker.core.database.driver` + +#### 4a. If SQLite Selected + +**Question**: What database filename? + +- Default: `tracker.db` +- **Field**: `tracker.core.database.database_name` + +#### 4b. If MySQL Selected + +**Questions**: + +1. **Database Host**: What MySQL server hostname? + - Default: `mysql` (Docker Compose service name) + - **Field**: `tracker.core.database.host` + +2. **Database Port**: What MySQL server port? + - Default: `3306` + - **Field**: `tracker.core.database.port` + +3. **Database Name**: What database name? + - Default: `tracker` + - **Field**: `tracker.core.database.database_name` + +4. **Database Username**: What database user? + - Example: `tracker_user` + - **Field**: `tracker.core.database.username` + +5. **Database Password**: What database password? + - **Security**: Use a strong password for production + - **Field**: `tracker.core.database.password` + +## Tracker Configuration + +### 5. Tracker Privacy Mode + +**Question**: Do you want to run a private tracker? + +**Options**: + +- **Public** (`false`): Anyone can announce/scrape torrents +- **Private** (`true`): Only authorized users can participate + +**Default**: `false` (public) + +**Field**: `tracker.core.private` + +### 6. UDP Trackers + +**Question**: How many UDP tracker instances do you want? + +**Options**: + +- 0 = No UDP trackers +- 1+ = One or more UDP tracker instances on different ports + +**Common scenarios**: + +- 0 instances: HTTP-only tracker +- 1 instance: Standard UDP tracker +- 2+ instances: Load distribution, different ports for different swarms + +#### 6a. For EACH UDP Tracker + +**Questions**: + +1. **Binding Address**: What IP:port should the UDP tracker bind to? + - Format: `{ip}:{port}` + - Default IP: `0.0.0.0` (all interfaces) + - Default port: `6969` + - **Constraint**: Port must be unique across all services + - **Field**: `tracker.udp_trackers[].bind_address` + +2. **Custom Domain** (Optional): Do you want a custom domain for this UDP tracker? + - **Note**: UDP does not support HTTPS/TLS + - If yes: What domain? (e.g., `udp.example.com`) + - **Field**: `tracker.udp_trackers[].domain` + +### 7. HTTP Trackers + +**Question**: How many HTTP tracker instances do you want? + +**Options**: + +- 0 = No HTTP trackers +- 1+ = One or more HTTP tracker instances + +**Common scenarios**: + +- 0 instances: UDP-only tracker +- 1 instance: Standard HTTP tracker +- 2+ instances: Multiple endpoints for different use cases + +#### 7a. For EACH HTTP Tracker + +**Questions**: + +1. **Binding Address**: What IP:port should the HTTP tracker bind to? + - Format: `{ip}:{port}` + - Default IP: `0.0.0.0` (all interfaces) + - Default port: `7070` + - **Constraint**: Port must be unique across all services + - **Field**: `tracker.http_trackers[].bind_address` + +2. **Custom Domain** (Optional): Do you want a custom domain for this HTTP tracker? + - If yes: What domain? (e.g., `tracker.example.com`) + - **Field**: `tracker.http_trackers[].domain` + +3. **HTTPS/TLS** (requires domain): Do you want to enable HTTPS for this HTTP tracker? + - **Requires**: Domain must be configured + - If yes: Set `use_tls_proxy: true` + - **Field**: `tracker.http_trackers[].use_tls_proxy` + +### 8. HTTP API + +**Question**: Do you want to enable the HTTP API? + +**Purpose**: Provides management endpoints for tracker administration + +#### 8a. If HTTP API Enabled + +**Questions**: + +1. **Binding Address**: What IP:port should the HTTP API bind to? + - Format: `{ip}:{port}` + - Default: `0.0.0.0:1212` + - **Constraint**: Port must be unique + - **Field**: `tracker.http_api.bind_address` + +2. **Admin Token**: What admin access token? + - Example: `MyAccessToken` -**Security**: Use a strong, unique token for production + - **Field**: `tracker.http_api.admin_token` + +3. **Custom Domain** (Optional): Do you want a custom domain for the HTTP API? + - If yes: What domain? (e.g., `api.example.com`) + - **Field**: `tracker.http_api.domain` + +4. **HTTPS/TLS** (requires domain): Do you want to enable HTTPS for the HTTP API? + - **Requires**: Domain must be configured + - If yes: Set `use_tls_proxy: true` + - **Field**: `tracker.http_api.use_tls_proxy` + +### 9. Health Check API + +**Question**: Do you want to expose the Health Check API? + +**Purpose**: Provides health monitoring endpoints + +#### 9a. If Health Check API Enabled + +**Questions**: + +1. **Binding Address**: What IP:port should the Health Check API bind to? + - Default: `127.0.0.1:1313` (localhost only for security) + - **Field**: `tracker.health_check_api.bind_address` + +2. **Custom Domain** (Optional): Do you want a custom domain for the Health Check API? + - If yes: What domain? (e.g., `health.example.com`) + - **Field**: `tracker.health_check_api.domain` + +3. **HTTPS/TLS** (requires domain): Do you want to enable HTTPS for the Health Check API? + - **Requires**: Domain must be configured + - **Field**: `tracker.health_check_api.use_tls_proxy` + +## HTTPS/TLS Configuration + +### 10. HTTPS Requirement Check + +**Automatic Check**: Are there any services with domains configured for HTTPS? + +Services that can use HTTPS: + +- HTTP trackers with `use_tls_proxy: true` +- HTTP API with `use_tls_proxy: true` +- Health Check API with `use_tls_proxy: true` +- Grafana with `use_tls_proxy: true` + +#### 10a. If ANY Service Uses HTTPS + +**Required Configuration**: + +1. **Admin Email**: What email address for Let's Encrypt certificate notifications? + - Format: Valid email address + - Used for: Certificate expiration notices, account recovery + - **Field**: `https.admin_email` + +2. **Certificate Environment**: Which Let's Encrypt environment? + - **Staging** (`use_staging: true`): Safe for testing, no rate limits + - Certificates are not trusted by browsers (for testing only) + - Use for: Development, LXD environments, testing HTTPS flow + - **Production** (`use_staging: false` or omit): Real trusted certificates + - Certificates trusted by all browsers + - Rate limits apply (50 certificates per domain per week) + - Use for: Production deployments on Hetzner + - **Field**: `https.use_staging` + +**Recommendation**: Always use staging certificates for LXD/local testing to avoid hitting production rate limits. + +## Monitoring (Optional) + +### 11. Prometheus + +**Question**: Do you want to enable Prometheus metrics collection? + +**Purpose**: Collects metrics from the tracker for monitoring and alerting + +#### 11a. If Prometheus Enabled + +**Questions**: + +1. **Scrape Interval**: How often should Prometheus scrape metrics (in seconds)? + - Default: `15` seconds + - Range: 5-300 seconds + - **Field**: `prometheus.scrape_interval_in_secs` + +2. **Custom Domain** (Optional): Do you want a custom domain for Prometheus? + - If yes: What domain? (e.g., `prometheus.example.com`) + - **Note**: Prometheus domain configuration is not currently supported in schema + +3. **HTTPS/TLS** (Optional): Do you want to enable HTTPS for Prometheus? + - **Note**: Prometheus HTTPS configuration is not currently supported in schema + +### 12. Grafana + +**Question**: Do you want to enable Grafana dashboards? + +**Requirements**: Prometheus must be enabled (Grafana depends on Prometheus as data source) + +#### 12a. If Grafana Enabled + +**Questions**: + +1. **Admin User**: What admin username for Grafana? + - Default: `admin` + - **Field**: `grafana.admin_user` + +2. **Admin Password**: What admin password for Grafana? + - **Security**: Use a strong password for production + - **Field**: `grafana.admin_password` + +3. **Custom Domain** (Optional): Do you want a custom domain for Grafana? + - If yes: What domain? (e.g., `grafana.example.com`) + - **Field**: `grafana.domain` + +4. **HTTPS/TLS** (requires domain): Do you want to enable HTTPS for Grafana? + - **Requires**: Domain must be configured + - If yes: Set `use_tls_proxy: true` + - **Field**: `grafana.use_tls_proxy` + +## Backup Configuration (Optional) + +### 13. Backups + +**Question**: Do you want to enable automated backups? + +**Purpose**: Automated backups of tracker database and persistent data + +#### 13a. If Backups Enabled + +**Questions**: + +1. **Backup Schedule**: What cron schedule for backups? + - Format: 5-field cron expression (minute hour day month weekday) + - Default: `0 3 * * *` (3:00 AM daily) + - Examples: + - `0 3 * * *` - 3:00 AM daily + - `0 */6 * * *` - Every 6 hours + - `0 0 * * 0` - Midnight every Sunday + - **Field**: `backup.schedule` + +2. **Retention Period**: How many days to retain backups before automatic deletion? + - Default: `7` days + - Must be greater than 0 + - **Field**: `backup.retention_days` + +## SSH Access + +### 14. SSH Credentials + +**Required for all deployments**: SSH credentials to access the provisioned instance + +**Questions**: + +1. **Private Key Path**: What is the absolute path to your SSH private key file? + - Example: `/home/user/.ssh/id_rsa` + - **Constraint**: File must exist and be readable + - **Field**: `ssh_credentials.private_key_path` + +2. **Public Key Path**: What is the absolute path to your SSH public key file? + - Example: `/home/user/.ssh/id_rsa.pub` + - **Constraint**: File must exist and be readable + - **Field**: `ssh_credentials.public_key_path` + +3. **SSH Username**: What username to use for SSH access? + - Default: `torrust` + - **Field**: `ssh_credentials.username` + +4. **SSH Port**: What SSH port? + - Default: `22` + - **Field**: `ssh_credentials.port` + +## Validation Rules + +After gathering all information, the configuration must satisfy these validation rules: + +**Cross-Service Validation**: + +1. **Unique Ports**: All binding addresses must use unique ports + - UDP trackers, HTTP trackers, HTTP API, Health Check API must not conflict + +2. **HTTPS Dependencies**: If any service has `use_tls_proxy: true`, then: + - That service must have a `domain` configured + - The `https` section must be present with `admin_email` + +3. **Grafana Dependencies**: If Grafana is enabled, then: + - Prometheus must also be enabled + +4. **Domain Format**: All domains must be valid DNS names (e.g., `example.com`, `subdomain.example.com`) + +5. **SSH Keys**: Both private and public key files must exist at the specified paths + +## Output Format + +Once all information is gathered, generate a JSON configuration file following the schema at [`schemas/environment-config.json`](../../schemas/environment-config.json). + +**Validation Command**: + +```bash +cargo run -- validate --env-file +``` + +**Template Generation** (for manual editing): + +```bash +cargo run -- create template --provider {lxd|hetzner} --output +``` + +## Common Configuration Patterns + +**Minimal Development (LXD + SQLite)**: + +- Provider: LXD +- Database: SQLite +- Trackers: 1 UDP + 1 HTTP (no HTTPS) +- No monitoring, no backups + +**Production (Hetzner + MySQL + HTTPS + Monitoring)**: + +- Provider: Hetzner +- Database: MySQL with credentials +- Trackers: Multiple UDP + HTTP with HTTPS +- Monitoring: Prometheus + Grafana with HTTPS +- Backups: Daily with 7-day retention +- HTTPS: Production certificates + +**Development Full-Stack (LXD + All Features)**: + +- Provider: LXD +- Database: MySQL +- Trackers: Multiple with HTTPS staging certificates +- Monitoring: Full stack +- Backups: Enabled +- HTTPS: Staging certificates (safe for testing) + +## References + +- [Environment Configuration Schema](../../schemas/environment-config.json) +- [Create Command Documentation](../user-guide/commands/create.md) +- [Configuration DTO Documentation](../../src/application/command_handlers/create/config/README.md) +- [Example Configurations](./examples/) (after Phase 3-4 implementation) diff --git a/docs/issues/339-provide-config-examples-and-questionnaire-for-ai-agents.md b/docs/issues/339-provide-config-examples-and-questionnaire-for-ai-agents.md index f2b4c1f1..48f430a8 100644 --- a/docs/issues/339-provide-config-examples-and-questionnaire-for-ai-agents.md +++ b/docs/issues/339-provide-config-examples-and-questionnaire-for-ai-agents.md @@ -13,11 +13,11 @@ AI agents currently have tools (`create template`, `validate`, JSON schema, docu ## Goals -- [ ] Provide structured questionnaire to gather all configuration requirements systematically -- [ ] Create curated example dataset mapping user requirements to validated JSON configs -- [ ] Reduce AI agent hallucination and trial-and-error through real validated examples -- [ ] Serve as user-facing documentation and configuration templates -- [ ] Enable machine-readable format for potential training/RAG use cases +- [x] Provide structured questionnaire to gather all configuration requirements systematically +- [x] Create curated example dataset mapping user requirements to validated JSON configs +- [x] Reduce AI agent hallucination and trial-and-error through real validated examples +- [x] Serve as user-facing documentation and configuration templates +- [x] Enable machine-readable format for potential training/RAG use cases ## 🏗️ Architecture Requirements @@ -27,19 +27,19 @@ AI agents currently have tools (`create template`, `validate`, JSON schema, docu ### Module Structure Requirements -- [ ] Add optional `description` field to environment configuration schema -- [ ] Update JSON schema at `schemas/environment-config.json` -- [ ] Update Rust DTO at `src/application/command_handlers/create/config/dto.rs` -- [ ] Examples validated with `cargo run -- validate --env-file` -- [ ] All examples are JSON files (no separate markdown documentation) +- [x] Add optional `description` field to environment configuration schema +- [x] Update JSON schema at `schemas/environment-config.json` +- [x] Update Rust DTO at `src/application/command_handlers/create/config/environment_config.rs` +- [x] Examples validated with `cargo run -- validate --env-file` +- [x] All examples are JSON files (no separate markdown documentation) ### Architectural Constraints -- [ ] `description` field must be optional (not required for existing configs) -- [ ] Description should be free-text string, 2-3 sentences recommended -- [ ] All example configs must be validated against the updated schema -- [ ] Questionnaire must align with validation rules in `src/application/command_handlers/create/config/` -- [ ] Examples must not include sensitive data (use fixture keys only) +- [x] `description` field must be optional (not required for existing configs) +- [x] Description should be free-text string, 2-3 sentences recommended +- [x] All example configs must be validated against the updated schema +- [x] Questionnaire must align with validation rules in `src/application/command_handlers/create/config/` +- [x] Examples must not include sensitive data (use fixture keys only) ### Anti-Patterns to Avoid @@ -229,7 +229,7 @@ Each JSON file contains a complete, validated environment configuration with a ` **Validation**: ```bash -cargo run -- validate --env-file docs/ai-training/examples/01-minimal-sqlite-lxd.json +cargo run -- validate --env-file docs/ai-training/dataset/environment-configs/01-minimal-lxd.json ``` **Automated Testing**: @@ -238,7 +238,7 @@ To ensure all examples remain valid as the schema evolves, an integration test w ```rust // tests/validate_examples.rs -// Iterates over docs/ai-training/examples/*.json +// Iterates over docs/ai-training/dataset/environment-configs/*.json // Runs validate command on each file // Fails if any example is invalid ``` @@ -406,50 +406,58 @@ This configuration exercises every deployment feature and serves as a comprehens ## Implementation Plan -### Phase 1: Add Description Field to Schema (1 hour) +### Phase 1: Add Description Field to Schema (1 hour) ✅ -- [ ] Add optional `description` field to `schemas/environment-config.json` -- [ ] Update Rust DTO in `src/application/command_handlers/create/config/dto.rs` -- [ ] Field type: `Option`, free-text, no length constraints at schema level -- [ ] Update validation tests to accept configs with description field -- [ ] Run tests to ensure backward compatibility (existing configs without description still work) +- [x] Add optional `description` field to `schemas/environment-config.json` +- [x] Update Rust DTO in `src/application/command_handlers/create/config/environment_config.rs` +- [x] Field type: `Option`, free-text, no length constraints at schema level +- [x] Update validation tests to accept configs with description field +- [x] Run tests to ensure backward compatibility (existing configs without description still work) -### Phase 2: Questionnaire Template (1 hour) +### Phase 2: Questionnaire Template (1 hour) ✅ -- [ ] Create `docs/ai-training/questionnaire.md` with full decision tree -- [ ] Include validation rules and constraints for each question -- [ ] Add conditional logic notes (if X then ask Y) +- [x] Create `docs/ai-training/questionnaire.md` with full decision tree +- [x] Include validation rules and constraints for each question +- [x] Add conditional logic notes (if X then ask Y) -### Phase 3: Core Example Configurations (2-3 hours) +### Phase 3: Core Example Configurations (2-3 hours) ✅ -- [ ] Create 6 core scenario JSON files with description field -- [ ] Scenarios: 01-minimal LXD, 02-full-stack LXD (staging), 03-minimal Hetzner, 04-full-stack Hetzner (production), 05-MySQL development, 09-monitoring stack -- [ ] Each description includes use case + key decisions (2-3 sentences) -- [ ] Validate each config with `cargo run -- validate --env-file` -- [ ] Use fixture keys only (no real credentials) +- [x] Create 6 core scenario JSON files with description field +- [x] Scenarios: 01-minimal LXD, 02-full-stack LXD (staging), 03-minimal Hetzner, 04-full-stack Hetzner (production), 05-MySQL development, 09-monitoring stack +- [x] Each description includes use case + key decisions (2-3 sentences) +- [x] Validate each config with `cargo run -- validate --env-file` +- [x] Use fixture keys only (no real credentials) -### Phase 4: Extended Example Configurations (2-3 hours) +### Phase 4: Extended Example Configurations (2-3 hours) ✅ -- [ ] Add 9 more scenario JSON files covering specific use cases -- [ ] Cover: 06-production HTTPS (staging), 07-UDP-only, 08-HTTP-only HTTPS (staging), 10-multi-domain (staging), 11-private tracker, 12-high-availability (staging), 13-backup-focused (staging), 14-lightweight production (staging), 15-sqlite-monitoring -- [ ] Validate all configs -- [ ] Document common mistakes in README +- [x] Add 9 more scenario JSON files covering specific use cases +- [x] Cover: 06-production HTTPS (staging), 07-UDP-only, 08-HTTP-only HTTPS (staging), 10-multi-domain (staging), 11-private tracker, 12-high-availability (staging), 13-backup-focused (staging), 14-lightweight production (staging), 15-sqlite-monitoring +- [x] Validate all configs +- [x] Document common mistakes in README -### Phase 5: Documentation and Index (1 hour) +### Phase 5: Documentation and Index (1 hour) ✅ -- [ ] Create `docs/ai-training/README.md` with overview and scenarios table -- [ ] Include usage instructions for AI agents and human users -- [ ] Add table mapping scenario IDs to files (like "Proposed Example Scenarios" in spec) -- [ ] Include guidance on when to use each scenario type +- [x] Create `docs/ai-training/README.md` with overview and scenarios table +- [x] Include usage instructions for AI agents and human users +- [x] Add table mapping scenario IDs to files (like "Proposed Example Scenarios" in spec) +- [x] Include guidance on when to use each scenario type -### Phase 6: Integration Test for Examples (30 minutes) +### Phase 6: Integration Test for Examples (30 minutes) ✅ -- [ ] Create integration test at `tests/validate_examples.rs` -- [ ] Test iterates over all JSON files in `docs/ai-training/examples/` -- [ ] For each example: run `validate` command and assert success -- [ ] Similar to `tests/e2e/validate_command.rs` but for multiple files -- [ ] Test ensures examples remain valid as schema evolves -- [ ] Run test as part of CI to catch regressions early +- [x] Create integration test at `tests/validate_ai_training_examples.rs` +- [x] Test iterates over all JSON files in `docs/ai-training/dataset/environment-configs/` +- [x] For each example: run `validate` command and assert success +- [x] Added 4 comprehensive test functions validating all aspects +- [x] Test ensures examples remain valid as schema evolves +- [x] Run test as part of CI to catch regressions early +- [x] Added regex dependency for pattern matching + +**Completed**: Integration test created with 4 test functions: + +- `it_should_validate_all_ai_training_example_configurations()` - Validates all 15 examples +- `it_should_verify_expected_number_of_examples()` - Ensures exactly 15 files exist +- `it_should_verify_example_naming_convention()` - Checks NN-descriptive-name.json pattern +- `it_should_verify_all_examples_have_descriptions()` - Verifies environment.description field ## Acceptance Criteria @@ -457,32 +465,32 @@ This configuration exercises every deployment feature and serves as a comprehens **Quality Checks**: -- [ ] Pre-commit checks pass: `./scripts/pre-commit.sh` -- [ ] All linters pass (markdown, cspell) -- [ ] All example JSON configurations validated with `cargo run -- validate --env-file` -- [ ] Integration test passes: `cargo test validate_examples` (validates all examples automatically) +- [x] Pre-commit checks pass: `./scripts/pre-commit.sh` +- [x] All linters pass (markdown, cspell) +- [x] All example JSON configurations validated with `cargo run -- validate --env-file` +- [x] Integration test passes: `cargo test validate_ai_training_examples` (validates all examples automatically) **Task-Specific Criteria**: -- [ ] Optional `description` field added to schema and validated -- [ ] Backward compatibility maintained (configs without description still work) -- [ ] Questionnaire template created at `docs/ai-training/questionnaire.md` -- [ ] All 15 example JSON configurations created with description field -- [ ] Each example validated with `cargo run -- validate --env-file` -- [ ] All descriptions are 2-3 sentences covering use case + key decisions -- [ ] Example configurations use fixture keys only (e.g., `fixtures/testing_rsa`) -- [ ] README created with scenarios table, usage instructions, and guidance -- [ ] Directory structure matches specification (JSON files only, no markdown per scenario) -- [ ] Full-stack scenarios (02 and 04) include all features: MySQL, Prometheus, Grafana, backup, domains -- [ ] Scenarios 01-02 (LXD minimal and full-stack) and 03-04 (Hetzner minimal and full-stack) demonstrate the complete spectrum -- [ ] All scenarios except 03-04 use LXD for local testing consistency -- [ ] All LXD scenarios with HTTPS (02, 06, 08, 10, 12, 13, 14) use staging certificates (`use_staging: true`) -- [ ] Hetzner production scenario (04) uses production certificates (no `use_staging` or `use_staging: false`) -- [ ] Scenario 13 demonstrates backup without monitoring overhead -- [ ] Scenario 14 demonstrates lightweight production (SQLite + HTTPS + backup) -- [ ] Scenario 15 demonstrates monitoring stack with SQLite simplicity -- [ ] Integration test `tests/validate_examples.rs` created and passing -- [ ] All examples validated automatically by integration test (prevents regressions) +- [x] Optional `description` field added to schema and validated +- [x] Backward compatibility maintained (configs without description still work) +- [x] Questionnaire template created at `docs/ai-training/questionnaire.md` +- [x] All 15 example JSON configurations created with description field +- [x] Each example validated with `cargo run -- validate --env-file` +- [x] All descriptions are 2-3 sentences covering use case + key decisions +- [x] Example configurations use fixture keys only (e.g., `fixtures/testing_rsa`) +- [x] README created with scenarios table, usage instructions, and guidance +- [x] Directory structure matches specification (JSON files only, no markdown per scenario) +- [x] Full-stack scenarios (02 and 04) include all features: MySQL, Prometheus, Grafana, backup, domains +- [x] Scenarios 01-02 (LXD minimal and full-stack) and 03-04 (Hetzner minimal and full-stack) demonstrate the complete spectrum +- [x] All scenarios except 03-04 use LXD for local testing consistency +- [x] All LXD scenarios with HTTPS (02, 06, 08, 10, 12, 13, 14) use staging certificates (`use_staging: true`) +- [x] Hetzner production scenario (04) uses production certificates (no `use_staging` or `use_staging: false`) +- [x] Scenario 13 demonstrates backup without monitoring overhead +- [x] Scenario 14 demonstrates lightweight production (SQLite + HTTPS + backup) +- [x] Scenario 15 demonstrates monitoring stack with SQLite simplicity +- [x] Integration test `tests/validate_ai_training_examples.rs` created and passing +- [x] All examples validated automatically by integration test (prevents regressions) ## Related Documentation @@ -511,7 +519,7 @@ description: | ## Before generating a config 1. Read the questionnaire: `docs/ai-training/questionnaire.md` -2. Find a similar example: `docs/ai-training/examples/` +2. Find a similar example: `docs/ai-training/dataset/environment-configs/` 3. Use `cargo run -- create template` to generate base 4. Customize based on user requirements 5. Validate with `cargo run -- validate --env-file` diff --git a/docs/issues/drafts/verify-ai-training-dataset-freshness.md b/docs/issues/drafts/verify-ai-training-dataset-freshness.md new file mode 100644 index 00000000..b0b082a3 --- /dev/null +++ b/docs/issues/drafts/verify-ai-training-dataset-freshness.md @@ -0,0 +1,158 @@ +# Draft Issue: Automated Verification of AI Training Dataset Freshness + +**Status:** Draft +**Priority:** Low +**Category:** Developer Experience / Testing +**Related:** Issue #339 - AI Training Resources + +## Problem + +The AI training dataset in `docs/ai-training/dataset/` requires manual regeneration whenever: + +- Input examples in `dataset/environment-configs/` change +- Template files in `templates/` are modified +- Rendering code changes in a way that affects output + +Currently, developers must manually run `scripts/generate-ai-training-outputs.sh` to regenerate the `rendered-templates/` directory. + +**Risk:** The rendered outputs can become stale without anyone noticing, leading to: + +- Outdated training data for AI models +- Inconsistencies between documentation and actual behavior +- Confusion for users examining the examples + +## Current Workaround + +Manual regeneration by running: + +```bash +./scripts/generate-ai-training-outputs.sh +``` + +## Challenge + +Templates contain dynamic timestamps that make direct directory comparison difficult: + +```text +# Generated: 2026-02-13T10:02:22Z +``` + +Any comparison mechanism would need to: + +- Strip/normalize timestamps before comparison, OR +- Use timestamp-agnostic comparison methods, OR +- Include deterministic timestamp generation for testing + +## Potential Solutions + +### Option 1: CI Workflow with Timestamp Normalization + +Create a GitHub Actions workflow that: + +1. Strips timestamp lines matching pattern `# Generated: YYYY-MM-DDTHH:MM:SSZ` +2. Compares normalized outputs with existing rendered templates +3. Fails if differences exist (beyond timestamps) + +**Pros:** Catches stale outputs automatically +**Cons:** Requires maintaining comparison logic, may have false positives + +### Option 2: Metadata File with Input Hashes + +Store a `.metadata.json` file alongside rendered outputs: + +```json +{ + "generated_at": "2026-02-13T10:02:22Z", + "input_hashes": { + "environment_configs": "sha256:abc123...", + "templates": "sha256:def456...", + "rendering_code": "sha256:ghi789..." + }, + "tool_version": "0.1.0" +} +``` + +CI checks if relevant paths changed since metadata was created. + +**Pros:** Fast, deterministic, no false positives +**Cons:** Requires metadata management, doesn't detect code logic changes + +### Option 3: Deterministic Timestamp Flag + +Add `--mock-timestamp` or `--fixed-timestamp` flag to render command: + +```bash +torrust-tracker-deployer render ... --fixed-timestamp "2024-01-01T00:00:00Z" +``` + +Use this flag for test/CI renders to enable exact comparison. + +**Pros:** Simple, enables deterministic testing +**Cons:** CI outputs would have fake timestamps (but that's fine for testing) + +### Option 4: Git-Based Staleness Check + +Simple bash script that checks: + +```bash +# Get last modification time of rendered outputs +rendered_mtime=$(find docs/ai-training/dataset/rendered-templates -type f -printf '%T@\n' | sort -n | tail -1) + +# Compare with modification times of inputs and templates +inputs_mtime=$(find docs/ai-training/dataset/environment-configs templates src/infrastructure/templating -type f -printf '%T@\n' | sort -n | tail -1) + +if (( inputs_mtime > rendered_mtime )); then + echo "⚠️ Rendered outputs may be stale. Run: ./scripts/generate-ai-training-outputs.sh" + exit 1 +fi +``` + +**Pros:** Very simple, no changes to application code +**Cons:** Doesn't catch all cases (e.g., code logic changes without file mtime change) + +### Option 5: Pre-commit Hook (Auto-regeneration) + +Add regeneration to pre-commit checks: + +```bash +./scripts/generate-ai-training-outputs.sh +git add docs/ai-training/dataset/rendered-templates +``` + +**Pros:** Always up-to-date automatically +**Cons:** Slow pre-commit (renders 15 examples), may be disruptive + +## Recommendation + +**Short-term (easiest):** Option 4 (Git-based staleness check) + +- Minimal implementation effort +- Good enough for catching most cases +- Can be added to pre-commit or CI immediately + +**Long-term (best):** Option 3 (Deterministic timestamp flag) + +- Enables proper testing and comparison +- Clean separation of test vs production behavior +- More maintainable and robust + +## Implementation Estimate + +- **Option 4:** ~30 minutes (bash script + CI integration) +- **Option 3:** ~2-3 hours (add flag, update templates, tests) +- **Option 2:** ~4-5 hours (metadata generation, comparison logic) +- **Option 1:** ~5-6 hours (normalization logic, CI workflow, edge cases) + +## Related Files + +- Script: `scripts/generate-ai-training-outputs.sh` +- Inputs: `docs/ai-training/dataset/environment-configs/*.json` +- Outputs: `docs/ai-training/dataset/rendered-templates/*/` +- Templates: `templates/**/*.tera` +- Rendering code: `src/infrastructure/templating/**` + +## Notes + +- Not urgent since dataset is primarily for AI training, not runtime behavior +- Manual regeneration is acceptable for now (done periodically) +- Worth revisiting when/if we automate AI model training pipeline diff --git a/docs/roadmap.md b/docs/roadmap.md index 991b578c..c8eda270 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -197,9 +197,10 @@ Minor changes to improve the output of some commands and overall user experience - Include hint about `show` command for full details - Issue: [#334](https://github.com/torrust/torrust-tracker-deployer/issues/334) - Implemented in [PR #337](https://github.com/torrust/torrust-tracker-deployer/pull/337) -- [ ] **10.3** Add DNS resolution check to `test` command - [Issue #336](https://github.com/torrust/torrust-tracker-deployer/issues/336) +- [x] **10.3** Add DNS resolution check to `test` command ✅ Completed - Verify configured domains resolve to the expected instance IP - Advisory warning only (doesn't fail tests) - DNS is decoupled from service tests + - Issue: [#336](https://github.com/torrust/torrust-tracker-deployer/issues/336) - See spec: [`docs/issues/336-add-dns-resolution-check-to-test-command.md`](./issues/336-add-dns-resolution-check-to-test-command.md) - [x] **10.4** Add `purge` command to remove local environment data - [Issue #322](https://github.com/torrust/torrust-tracker-deployer/issues/322) ✅ Completed - Removes `data/{env}/` and `build/{env}/` for destroyed environments @@ -231,7 +232,7 @@ Add features and documentation that make the use of AI agents to operate the dep - Enables AI agents to find documentation even when working with deployed configs - See draft: [`docs/issues/drafts/add-ai-discoverable-documentation-headers-to-templates.md`](./issues/drafts/add-ai-discoverable-documentation-headers-to-templates.md) -- [ ] **11.3** Provide configuration examples and questionnaire for AI agent guidance +- [x] **11.3** Provide configuration examples and questionnaire for AI agent guidance ✅ Completed - Problem: AI agents struggle with the many valid configuration combinations - Questionnaire template: structured decision tree to gather all required user information - Example dataset: real-world scenarios mapping requirements to validated configs @@ -239,7 +240,8 @@ Add features and documentation that make the use of AI agents to operate the dep - Benefits: few-shot learning for agents, reduced hallucination, training/RAG dataset - Can integrate with `create-environment-config` skill from task 11.1 - See specification: [Issue #339](https://github.com/torrust/torrust-tracker-deployer/issues/339), [`docs/issues/339-provide-config-examples-and-questionnaire-for-ai-agents.md`](./issues/339-provide-config-examples-and-questionnaire-for-ai-agents.md) - - See spec: [`docs/issues/provide-config-examples-and-questionnaire-for-ai-agents.md`](./issues/provide-config-examples-and-questionnaire-for-ai-agents.md) + - **Deliverables**: Questionnaire template (494 lines), 15 validated example configs, comprehensive README (469 lines), integration test suite + - **Components**: `docs/ai-training/questionnaire.md`, `docs/ai-training/dataset/environment-configs/*.json`, `docs/ai-training/README.md`, `tests/validate_ai_training_examples.rs` - [ ] **11.4** Add dry-run mode for all commands - Allow AI agents (and users) to preview what will happen before executing operations diff --git a/schemas/environment-config.json b/schemas/environment-config.json index 04cb4f8c..95cc5e67 100644 --- a/schemas/environment-config.json +++ b/schemas/environment-config.json @@ -162,6 +162,14 @@ "description": "Environment-specific configuration section\n\nContains configuration specific to the environment being created.", "type": "object", "properties": { + "description": { + "description": "Optional description of the environment\n\nFree-text field (2-3 sentences recommended) describing:\n- Use case: What this environment is designed for\n- Key decisions: Why certain values were chosen\n- Context: When this environment is appropriate\n\nThis field is primarily used for documentation and AI agent training\nto provide context about environment intent and design decisions.\n\n# Examples\n\n```text\n\"Minimal development setup with SQLite and UDP/HTTP trackers.\n No HTTPS or monitoring. Ideal for local testing.\"\n```", + "type": [ + "string", + "null" + ], + "default": null + }, "instance_name": { "description": "Optional custom instance name for the VM/container\n\nIf not provided, auto-generated as `torrust-tracker-vm-{env_name}`.\nWhen provided, must follow instance naming rules:\n- 1-63 characters\n- ASCII letters, numbers, and dashes only\n- Cannot start with digit or dash\n- Cannot end with dash", "type": [ @@ -507,4 +515,4 @@ ] } } -} \ No newline at end of file +} diff --git a/scripts/generate-ai-training-outputs.sh b/scripts/generate-ai-training-outputs.sh new file mode 100755 index 00000000..9b2c6e14 --- /dev/null +++ b/scripts/generate-ai-training-outputs.sh @@ -0,0 +1,249 @@ +#!/bin/bash +# Generate rendered template outputs for AI training example configurations +# This script processes all example configs in docs/ai-training/dataset/environment-configs/ and +# generates the corresponding deployment artifacts using the 'render' command. +# +# Purpose: Complete the AI training dataset with input/output pairs: +# - Input: Environment configuration JSON files +# - Output: Rendered deployment templates (OpenTofu, Ansible, Docker, etc.) +# +# The script: +# 1. Replaces generic SSH paths with actual fixture paths +# 2. Calls 'render' command with placeholder IP address +# 3. Outputs artifacts to docs/ai-training/dataset/rendered-templates// +# 4. Reports success/failure for each example + +set -euo pipefail + +# Configuration +EXAMPLES_DIR="docs/ai-training/dataset/environment-configs" +OUTPUTS_DIR="docs/ai-training/dataset/rendered-templates" +PLACEHOLDER_IP="203.0.113.1" # TEST-NET-1 (RFC 5737) - documentation IP range +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +FIXTURE_PRIVATE_KEY="${PROJECT_ROOT}/fixtures/testing_rsa" +FIXTURE_PUBLIC_KEY="${PROJECT_ROOT}/fixtures/testing_rsa.pub" + +# Counters +SUCCESS_COUNT=0 +FAILURE_COUNT=0 +FAILED_EXAMPLES=() + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to display usage +usage() { + cat << EOF +Usage: $0 [OPTIONS] + +Generate rendered deployment artifacts for AI training examples. + +Options: + -h, --help Show this help message + -c, --clean Remove existing outputs directory before generating + +Examples: + $0 Generate outputs for all examples + $0 --clean Clean and regenerate all outputs + +Output: + Generated artifacts are written to: ${OUTPUTS_DIR}/ + Each example gets its own subdirectory with rendered templates. +EOF +} + +# Function to log messages +log_info() { + echo -e "${BLUE}ℹ${NC} $*" +} + +log_success() { + echo -e "${GREEN}✓${NC} $*" +} + +log_warning() { + echo -e "${YELLOW}⚠${NC} $*" +} + +log_error() { + echo -e "${RED}✗${NC} $*" +} + +# Function to replace SSH paths in JSON config +replace_ssh_paths() { + local input_file=$1 + local output_file=$2 + + # Use jq to replace SSH key paths + jq --arg private_key "$FIXTURE_PRIVATE_KEY" \ + --arg public_key "$FIXTURE_PUBLIC_KEY" \ + '.ssh_credentials.private_key_path = $private_key | + .ssh_credentials.public_key_path = $public_key' \ + "$input_file" > "$output_file" +} + +# Function to extract example name from filename +get_example_name() { + local filename=$1 + basename "$filename" .json +} + +# Function to process a single example +process_example() { + local example_file=$1 + local example_name + local temp_config + local output_dir + + example_name=$(get_example_name "$example_file") + output_dir="${OUTPUTS_DIR}/${example_name}" + temp_config=$(mktemp) + + log_info "Processing: ${example_name}" + + # Replace SSH paths in config + if ! replace_ssh_paths "$example_file" "$temp_config"; then + log_error "Failed to process config: ${example_name}" + rm -f "$temp_config" + return 1 + fi + + # Run render command (capture output for error reporting) + # Use --force to allow overwriting existing output directories + local render_output + if render_output=$(cargo run -q -- render \ + --env-file "$temp_config" \ + --instance-ip "$PLACEHOLDER_IP" \ + --output-dir "$output_dir" \ + --force 2>&1); then + log_success "Generated: ${example_name}" + SUCCESS_COUNT=$((SUCCESS_COUNT + 1)) + else + log_error "Failed to render: ${example_name}" + echo "$render_output" | grep -E "(Error|Failed|error)" || echo "$render_output" + FAILED_EXAMPLES+=("$example_name") + FAILURE_COUNT=$((FAILURE_COUNT + 1)) + fi + + # Cleanup temp file + rm -f "$temp_config" +} + +# Function to clean outputs directory +clean_outputs() { + if [[ -d "$OUTPUTS_DIR" ]]; then + log_info "Cleaning existing outputs directory..." + rm -rf "$OUTPUTS_DIR" + log_success "Outputs directory cleaned" + fi +} + +# Main execution +main() { + local clean_mode=false + + # Parse command line arguments + while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + usage + exit 0 + ;; + -c|--clean) + clean_mode=true + shift + ;; + *) + echo "Unknown option: $1" + usage + exit 1 + ;; + esac + done + + # Change to project root + cd "$PROJECT_ROOT" + + echo "🚀 AI Training Outputs Generator" + echo "=================================" + echo + log_info "Project root: ${PROJECT_ROOT}" + log_info "Examples directory: ${EXAMPLES_DIR}" + log_info "Outputs directory: ${OUTPUTS_DIR}" + log_info "Placeholder IP: ${PLACEHOLDER_IP}" + echo + + # Clean if requested + if [[ "$clean_mode" == true ]]; then + clean_outputs + echo + fi + + # Check if examples directory exists + if [[ ! -d "$EXAMPLES_DIR" ]]; then + log_error "Examples directory not found: ${EXAMPLES_DIR}" + exit 1 + fi + + # Check if fixture SSH keys exist + if [[ ! -f "$FIXTURE_PRIVATE_KEY" ]] || [[ ! -f "$FIXTURE_PUBLIC_KEY" ]]; then + log_error "Fixture SSH keys not found:" + log_error " Expected: ${FIXTURE_PRIVATE_KEY}" + log_error " Expected: ${FIXTURE_PUBLIC_KEY}" + exit 1 + fi + + # Check if jq is installed + if ! command -v jq &> /dev/null; then + log_error "jq is required but not installed" + log_error "Install with: sudo apt-get install jq" + exit 1 + fi + + # Create outputs directory + mkdir -p "$OUTPUTS_DIR" + + # Find all example JSON files + local examples + mapfile -t examples < <(find "$EXAMPLES_DIR" -maxdepth 1 -name "*.json" | sort) + + if [[ ${#examples[@]} -eq 0 ]]; then + log_error "No example files found in ${EXAMPLES_DIR}" + exit 1 + fi + + log_info "Found ${#examples[@]} example configuration(s)" + echo + + # Process each example + for example_file in "${examples[@]}"; do + process_example "$example_file" + done + + # Summary + echo + echo "📊 Summary" + echo "==========" + log_success "Successfully generated: ${SUCCESS_COUNT}" + + if [[ $FAILURE_COUNT -gt 0 ]]; then + log_error "Failed to generate: ${FAILURE_COUNT}" + echo + log_error "Failed examples:" + for failed in "${FAILED_EXAMPLES[@]}"; do + echo " - ${failed}" + done + exit 1 + else + echo + log_success "All examples processed successfully!" + log_info "Outputs available in: ${OUTPUTS_DIR}/" + fi +} + +# Run main function +main "$@" diff --git a/src/application/command_handlers/create/config/environment_config.rs b/src/application/command_handlers/create/config/environment_config.rs index 2d24576f..932b19af 100644 --- a/src/application/command_handlers/create/config/environment_config.rs +++ b/src/application/command_handlers/create/config/environment_config.rs @@ -164,6 +164,25 @@ pub struct EnvironmentSection { /// - Cannot start with numbers pub name: String, + /// Optional description of the environment + /// + /// Free-text field (2-3 sentences recommended) describing: + /// - Use case: What this environment is designed for + /// - Key decisions: Why certain values were chosen + /// - Context: When this environment is appropriate + /// + /// This field is primarily used for documentation and AI agent training + /// to provide context about environment intent and design decisions. + /// + /// # Examples + /// + /// ```text + /// "Minimal development setup with SQLite and UDP/HTTP trackers. + /// No HTTPS or monitoring. Ideal for local testing." + /// ``` + #[serde(default)] + pub description: Option, + /// Optional custom instance name for the VM/container /// /// If not provided, auto-generated as `torrust-tracker-vm-{env_name}`. @@ -191,6 +210,7 @@ impl EnvironmentCreationConfig { /// let config = EnvironmentCreationConfig::new( /// EnvironmentSection { /// name: "dev".to_string(), + /// description: None, /// instance_name: None, /// }, /// SshCredentialsConfig::new( @@ -311,6 +331,7 @@ impl EnvironmentCreationConfig { Self { environment: EnvironmentSection { name: "REPLACE_WITH_ENVIRONMENT_NAME".to_string(), + description: None, instance_name: None, // Auto-generated if not provided }, ssh_credentials: SshCredentialsConfig { @@ -442,6 +463,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new( @@ -593,6 +615,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "staging".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new( @@ -626,6 +649,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, // Auto-generate }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -660,6 +684,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "prod".to_string(), + description: None, instance_name: Some("my-custom-instance".to_string()), }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -691,6 +716,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "Invalid_Name".to_string(), // uppercase - invalid + description: None, instance_name: None, }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -724,6 +750,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: Some("invalid-".to_string()), // ends with dash - invalid }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -758,6 +785,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -793,6 +821,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new( @@ -832,6 +861,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new( @@ -871,6 +901,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new( @@ -911,6 +942,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "test-env".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -940,6 +972,7 @@ mod tests { let original = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: Some("my-vm".to_string()), }, SshCredentialsConfig::new( @@ -1032,6 +1065,7 @@ mod tests { let regular_config = EnvironmentCreationConfig::new( EnvironmentSection { name: "test".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new( @@ -1185,6 +1219,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new( @@ -1239,6 +1274,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new( @@ -1269,6 +1305,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -1304,6 +1341,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -1337,6 +1375,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -1400,6 +1439,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "dev".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), diff --git a/src/application/command_handlers/create/config/validated_params.rs b/src/application/command_handlers/create/config/validated_params.rs index 1ea02146..5410caf2 100644 --- a/src/application/command_handlers/create/config/validated_params.rs +++ b/src/application/command_handlers/create/config/validated_params.rs @@ -161,6 +161,7 @@ mod tests { EnvironmentCreationConfig::new( EnvironmentSection { name: "test-env".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -196,6 +197,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "my-env".to_string(), + description: None, instance_name: Some("custom-vm-name".to_string()), }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), @@ -222,6 +224,7 @@ mod tests { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "INVALID_NAME".to_string(), // uppercase not allowed + description: None, instance_name: None, }, SshCredentialsConfig::new(private_key_path, public_key_path, "torrust".to_string(), 22), diff --git a/src/application/command_handlers/create/handler.rs b/src/application/command_handlers/create/handler.rs index 21c5b656..7e69f6d3 100644 --- a/src/application/command_handlers/create/handler.rs +++ b/src/application/command_handlers/create/handler.rs @@ -63,6 +63,7 @@ use super::errors::CreateCommandHandlerError; /// let config = EnvironmentCreationConfig::new( /// EnvironmentSection { /// name: "dev".to_string(), +/// description: None, /// instance_name: None, // Auto-generate from environment name /// }, /// SshCredentialsConfig::new( @@ -181,6 +182,7 @@ impl CreateCommandHandler { /// let config = EnvironmentCreationConfig::new( /// EnvironmentSection { /// name: "staging".to_string(), + /// description: None, /// instance_name: None, // Auto-generate from environment name /// }, /// SshCredentialsConfig::new( diff --git a/src/application/command_handlers/create/mod.rs b/src/application/command_handlers/create/mod.rs index bf85af4d..34f8585c 100644 --- a/src/application/command_handlers/create/mod.rs +++ b/src/application/command_handlers/create/mod.rs @@ -45,6 +45,7 @@ //! let config = EnvironmentCreationConfig::new( //! EnvironmentSection { //! name: "production".to_string(), +//! description: None, //! instance_name: None, // Auto-generate from environment name //! }, //! SshCredentialsConfig::new( diff --git a/src/application/command_handlers/create/tests/builders.rs b/src/application/command_handlers/create/tests/builders.rs index c0d3d534..7ca35a3c 100644 --- a/src/application/command_handlers/create/tests/builders.rs +++ b/src/application/command_handlers/create/tests/builders.rs @@ -265,6 +265,7 @@ pub fn create_valid_test_config(temp_dir: &TempDir, env_name: &str) -> Environme EnvironmentCreationConfig::new( EnvironmentSection { name: env_name.to_string(), + description: None, instance_name: None, // Auto-generate from environment name }, SshCredentialsConfig::new( diff --git a/src/application/command_handlers/create/tests/integration.rs b/src/application/command_handlers/create/tests/integration.rs index 597674ef..78fd5241 100644 --- a/src/application/command_handlers/create/tests/integration.rs +++ b/src/application/command_handlers/create/tests/integration.rs @@ -129,6 +129,7 @@ fn it_should_fail_with_invalid_environment_name() { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "Invalid_Name".to_string(), // Invalid: contains uppercase + description: None, instance_name: None, }, SshCredentialsConfig::new( @@ -180,6 +181,7 @@ fn it_should_fail_when_ssh_private_key_not_found() { let config = EnvironmentCreationConfig::new( EnvironmentSection { name: "test-env".to_string(), + description: None, instance_name: None, }, SshCredentialsConfig::new( diff --git a/src/testing/e2e/tasks/run_create_command.rs b/src/testing/e2e/tasks/run_create_command.rs index e3cee448..20ed1c30 100644 --- a/src/testing/e2e/tasks/run_create_command.rs +++ b/src/testing/e2e/tasks/run_create_command.rs @@ -88,6 +88,7 @@ pub fn run_create_command( let config = EnvironmentCreationConfig::new( EnvironmentSection { name: environment_name.to_string(), + description: None, instance_name: None, // Auto-generate from environment name }, SshCredentialsConfig::new( diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 174f1cb8..98377654 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -10,6 +10,8 @@ //! - `TempWorkspace` - Local to `tests/` (only used by integration tests) //! - `EnvironmentStateAssertions` - Local to `tests/` (only used by integration tests) +#![allow(dead_code, unused_imports)] + mod assertions; mod temp_workspace; diff --git a/tests/validate_ai_training_examples.rs b/tests/validate_ai_training_examples.rs new file mode 100644 index 00000000..a06c1d53 --- /dev/null +++ b/tests/validate_ai_training_examples.rs @@ -0,0 +1,299 @@ +/// Integration test to validate all AI training example configurations. +/// +/// This test ensures that: +/// 1. All example configuration files exist +/// 2. All examples pass schema validation +/// 3. All examples are properly formatted JSON +/// 4. All examples can be rendered into deployment artifacts +/// +/// Run with: `cargo test --test validate_ai_training_examples` +mod support; + +use std::fs; +use std::path::{Path, PathBuf}; +use support::ProcessRunner; +use tempfile::TempDir; + +const EXAMPLES_DIR: &str = "docs/ai-training/dataset/environment-configs"; +const FIXTURE_PRIVATE_KEY: &str = "fixtures/testing_rsa"; +const FIXTURE_PUBLIC_KEY: &str = "fixtures/testing_rsa.pub"; +const PLACEHOLDER_IP: &str = "203.0.113.1"; // RFC 5737 TEST-NET-1 + +// Helper Functions + +/// Extract file name from path, returning "unknown" if extraction fails +fn get_file_name(path: &Path) -> &str { + path.file_name() + .and_then(|n| n.to_str()) + .unwrap_or("unknown") +} + +/// Setup test examples by collecting all JSON files from the examples directory +fn setup_test_examples() -> Vec { + let examples_dir = PathBuf::from(EXAMPLES_DIR); + assert!( + examples_dir.exists(), + "Examples directory should exist: {EXAMPLES_DIR}" + ); + + let example_files = collect_example_files(&examples_dir); + assert!( + !example_files.is_empty(), + "Should find example configuration files in {EXAMPLES_DIR}" + ); + + example_files +} + +/// Convert a path to a string slice with better error context +fn path_to_str<'a>(path: &'a Path, context: &str) -> Result<&'a str, String> { + path.to_str() + .ok_or_else(|| format!("Invalid UTF-8 in {context}: {}", path.display())) +} + +/// Report failures and panic with a summary +fn report_failures_and_panic(failures: &[(String, String)], total: usize, operation: &str) { + eprintln!("\n❌ Failed {operation}s:\n"); + for (file, error) in failures { + eprintln!(" • {file}: {error}"); + } + panic!( + "\n{} out of {total} example configurations failed {operation}", + failures.len() + ); +} + +// Test Functions + +#[test] +fn it_should_validate_all_ai_training_example_configurations() { + let example_files = setup_test_examples(); + let mut failures = Vec::new(); + + for example_file in &example_files { + let file_name = get_file_name(example_file); + + if let Err(error) = validate_configuration(example_file) { + println!("✗"); + failures.push((file_name.to_string(), error)); + } + } + + if !failures.is_empty() { + report_failures_and_panic(&failures, example_files.len(), "validation"); + } +} + +fn collect_example_files(examples_dir: &PathBuf) -> Vec { + let mut files = Vec::new(); + + if let Ok(entries) = fs::read_dir(examples_dir) { + for entry in entries.flatten() { + let path = entry.path(); + if path.extension().and_then(|s| s.to_str()) == Some("json") { + files.push(path); + } + } + } + + files.sort(); + files +} + +fn validate_configuration(config_path: &Path) -> Result<(), String> { + let config_path_str = path_to_str(config_path, "config path")?; + + let result = ProcessRunner::new() + .run_validate_command(config_path_str) + .map_err(|e| format!("Failed to run validate command: {e}"))?; + + if result.success() { + Ok(()) + } else { + Err(format!( + "Validation failed with exit code: {:?}\nstderr: {}", + result.exit_code(), + result.stderr() + )) + } +} + +#[test] +fn it_should_verify_expected_number_of_examples() { + const EXPECTED_EXAMPLES: usize = 15; + + let example_files = setup_test_examples(); + + assert_eq!( + example_files.len(), + EXPECTED_EXAMPLES, + "Expected {EXPECTED_EXAMPLES} example files, found {}. Files: {:?}", + example_files.len(), + example_files + .iter() + .map(|p| get_file_name(p)) + .collect::>() + ); +} + +#[test] +fn it_should_verify_example_naming_convention() { + let example_files = setup_test_examples(); + let pattern = + regex::Regex::new(r"^\d{2}-[a-z0-9-]+\.json$").expect("Regex pattern should be valid"); + + for example_file in &example_files { + let file_name = get_file_name(example_file); + + assert!( + pattern.is_match(file_name), + "Example file '{file_name}' does not match naming convention: NN-descriptive-name.json" + ); + } +} + +#[test] +fn it_should_verify_all_examples_have_descriptions() { + let example_files = setup_test_examples(); + let mut missing_descriptions = Vec::new(); + + for example_file in &example_files { + let file_name = get_file_name(example_file); + let content = + fs::read_to_string(example_file).expect("Should be able to read example file"); + let json: serde_json::Value = + serde_json::from_str(&content).expect("Example file should contain valid JSON"); + + if !has_valid_description(&json) { + missing_descriptions.push(file_name.to_string()); + } + } + + assert!( + missing_descriptions.is_empty(), + "The following example files are missing descriptions: {missing_descriptions:?}" + ); +} + +fn has_valid_description(json: &serde_json::Value) -> bool { + json.get("environment") + .and_then(|env| env.get("description")) + .and_then(|desc| desc.as_str()) + .is_some_and(|s| !s.is_empty()) +} + +#[test] +fn it_should_render_all_ai_training_example_configurations() { + let example_files = setup_test_examples(); + let mut failures = Vec::new(); + + for example_file in &example_files { + let file_name = get_file_name(example_file); + + if let Err(error) = render_configuration(example_file) { + eprintln!("✗ {file_name}"); + failures.push((file_name.to_string(), error)); + } + } + + if !failures.is_empty() { + report_failures_and_panic(&failures, example_files.len(), "render"); + } +} + +fn render_configuration(config_path: &Path) -> Result<(), String> { + let temp_workspace = + TempDir::new().map_err(|e| format!("Failed to create temp workspace: {e}"))?; + + let temp_config_file = prepare_test_config(config_path, &temp_workspace)?; + let output_dir = temp_workspace.path().join("output"); + + execute_render_command(&temp_config_file, &output_dir)?; + verify_render_output_directories(&output_dir)?; + + Ok(()) +} + +/// Prepare a test configuration by replacing SSH credentials with fixture paths +fn prepare_test_config(config_path: &Path, temp_workspace: &TempDir) -> Result { + let config_content = + fs::read_to_string(config_path).map_err(|e| format!("Failed to read config file: {e}"))?; + + let mut config: serde_json::Value = serde_json::from_str(&config_content) + .map_err(|e| format!("Failed to parse config JSON: {e}"))?; + + replace_ssh_credentials_with_fixtures(&mut config)?; + + let temp_config_file = temp_workspace.path().join("config.json"); + fs::write( + &temp_config_file, + serde_json::to_string_pretty(&config) + .map_err(|e| format!("Failed to serialize modified config: {e}"))?, + ) + .map_err(|e| format!("Failed to write temp config file: {e}"))?; + + Ok(temp_config_file) +} + +/// Replace SSH credentials in config with absolute paths to test fixtures +fn replace_ssh_credentials_with_fixtures(config: &mut serde_json::Value) -> Result<(), String> { + let workspace_root = + std::env::current_dir().map_err(|e| format!("Failed to get current directory: {e}"))?; + let abs_private_key = workspace_root.join(FIXTURE_PRIVATE_KEY); + let abs_public_key = workspace_root.join(FIXTURE_PUBLIC_KEY); + + if let Some(ssh_credentials) = config.get_mut("ssh_credentials") { + if let Some(credentials_map) = ssh_credentials.as_object_mut() { + credentials_map.insert( + "private_key_path".to_string(), + serde_json::Value::String( + path_to_str(&abs_private_key, "private key path")?.to_string(), + ), + ); + credentials_map.insert( + "public_key_path".to_string(), + serde_json::Value::String( + path_to_str(&abs_public_key, "public key path")?.to_string(), + ), + ); + } + } + + Ok(()) +} + +/// Execute the render command using `ProcessRunner` +fn execute_render_command(temp_config_file: &Path, output_dir: &Path) -> Result<(), String> { + let temp_config_file_str = path_to_str(temp_config_file, "temp config file path")?; + let output_dir_str = path_to_str(output_dir, "output directory path")?; + + let result = ProcessRunner::new() + .run_render_command_with_config_file(temp_config_file_str, PLACEHOLDER_IP, output_dir_str) + .map_err(|e| format!("Failed to run render command: {e}"))?; + + if !result.success() { + return Err(format!( + "Render command failed with exit code: {:?}\nstderr: {}", + result.exit_code(), + result.stderr() + )); + } + + Ok(()) +} + +/// Verify that the render command created the expected output directories +fn verify_render_output_directories(output_dir: &Path) -> Result<(), String> { + let ansible_dir = output_dir.join("ansible"); + let docker_compose_dir = output_dir.join("docker-compose"); + + if !ansible_dir.exists() { + return Err("Render succeeded but ansible directory was not created".to_string()); + } + + if !docker_compose_dir.exists() { + return Err("Render succeeded but docker-compose directory was not created".to_string()); + } + + Ok(()) +}