Skip to content

Commit 549fcaf

Browse files
committed
feat: [#315] Step 2.4 - Update create template command to include backup with defaults
- Include BackupSection in template with default values - Default schedule: 3:00 AM daily (0 3 * * *) - Default retention: 7 days - Enables backup support for users immediately upon creation - Regenerate JSON schema to include BackupSection definition - Schema now documents schedule and retention_days fields - Provides IDE autocomplete and validation for backup configuration - Follows schema regeneration instructions from schemas/README.md - Update comment to clarify that backups are now enabled by default - Users can remove the backup section if not needed - Follows the same pattern as prometheus and grafana Relates to: #315
1 parent 5cd5b4a commit 549fcaf

2 files changed

Lines changed: 42 additions & 11 deletions

File tree

schemas/environment-config.json

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1-
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.10s
2-
Running `target/debug/torrust-tracker-deployer create schema`
31
{
42
"$schema": "https://json-schema.org/draft/2020-12/schema",
53
"title": "EnvironmentCreationConfig",
6-
"description": "Configuration for creating a deployment environment\n\nThis is the top-level configuration object that contains all information\nneeded to create a new deployment environment. It deserializes from JSON\nconfiguration and provides type-safe conversion to domain parameters.\n\n# Examples\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::{\n EnvironmentCreationConfig, EnvironmentSection, ProviderSection, LxdProviderSection\n};\n\nlet json = r#\"{\n \"environment\": {\n \"name\": \"dev\"\n },\n \"ssh_credentials\": {\n \"private_key_path\": \"fixtures/testing_rsa\",\n \"public_key_path\": \"fixtures/testing_rsa.pub\"\n },\n \"provider\": {\n \"provider\": \"lxd\",\n \"profile_name\": \"torrust-profile-dev\"\n },\n \"tracker\": {\n \"core\": {\n \"database\": {\n \"driver\": \"sqlite3\",\n \"database_name\": \"tracker.db\"\n },\n \"private\": false\n },\n \"udp_trackers\": [\n {\n \"bind_address\": \"0.0.0.0:6969\"\n }\n ],\n \"http_trackers\": [\n {\n \"bind_address\": \"0.0.0.0:7070\"\n }\n ],\n \"http_api\": {\n \"bind_address\": \"0.0.0.0:1212\",\n \"admin_token\": \"MyAccessToken\"\n },\n \"health_check_api\": {\n \"bind_address\": \"127.0.0.1:1313\"\n }\n },\n \"prometheus\": {\n \"scrape_interval_in_secs\": 15\n },\n \"grafana\": {\n \"admin_user\": \"admin\",\n \"admin_password\": \"admin\"\n }\n}\"#;\n\nlet config: EnvironmentCreationConfig = serde_json::from_str(json)?;\n# Ok::<(), Box<dyn std::error::Error>>(())\n```",
4+
"description": "Configuration for creating a deployment environment\n\nThis is the top-level configuration object that contains all information\nneeded to create a new deployment environment. It deserializes from JSON\nconfiguration and provides type-safe conversion to domain parameters.\n\n# Examples\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::{\n EnvironmentCreationConfig, EnvironmentSection, ProviderSection, LxdProviderSection\n};\n\nlet json = r#\"{\n \"environment\": {\n \"name\": \"dev\"\n },\n \"ssh_credentials\": {\n \"private_key_path\": \"fixtures/testing_rsa\",\n \"public_key_path\": \"fixtures/testing_rsa.pub\"\n },\n \"provider\": {\n \"provider\": \"lxd\",\n \"profile_name\": \"torrust-profile-dev\"\n },\n \"tracker\": {\n \"core\": {\n \"database\": {\n \"driver\": \"sqlite3\",\n \"database_name\": \"tracker.db\"\n },\n \"private\": false\n },\n \"udp_trackers\": [\n {\n \"bind_address\": \"0.0.0.0:6969\"\n }\n ],\n \"http_trackers\": [\n {\n \"bind_address\": \"0.0.0.0:7070\"\n }\n ],\n \"http_api\": {\n \"bind_address\": \"0.0.0.0:1212\",\n \"admin_token\": \"MyAccessToken\"\n },\n \"health_check_api\": {\n \"bind_address\": \"127.0.0.1:1313\"\n }\n },\n \"prometheus\": {\n \"scrape_interval_in_secs\": 15\n },\n \"grafana\": {\n \"admin_user\": \"admin\",\n \"admin_password\": \"admin\"\n },\n \"backup\": {\n \"schedule\": \"0 3 * * *\",\n \"retention_days\": 7\n }\n}\"#;\n\nlet config: EnvironmentCreationConfig = serde_json::from_str(json)?;\n# Ok::<(), Box<dyn std::error::Error>>(())\n```",
75
"type": "object",
86
"properties": {
7+
"backup": {
8+
"description": "Backup configuration (optional)\n\nWhen present, automated backups will be configured for the tracker\ndatabase and other persistent data.\n\nUses `BackupSection` for JSON parsing with String primitives (cron schedule).\nConverted to domain `BackupConfig` via `TryInto<EnvironmentParams>`.\n\nDefault schedule: 3:00 AM daily (\"0 3 * * *\")\nDefault retention: 7 days",
9+
"anyOf": [
10+
{
11+
"$ref": "#/$defs/BackupSection"
12+
},
13+
{
14+
"type": "null"
15+
}
16+
],
17+
"default": null
18+
},
919
"environment": {
1020
"description": "Environment-specific settings",
1121
"$ref": "#/$defs/EnvironmentSection"
1222
},
1323
"grafana": {
14-
"description": "Grafana dashboard configuration (optional)\n\nWhen present, Grafana will be deployed for visualization.\n**Requires Prometheus to be configured** - Grafana depends on\nPrometheus as its data source.\n\nUses `GrafanaSection` for JSON parsing with String primitives.\nConverted to domain `GrafanaConfig` via `to_environment_params()`.",
24+
"description": "Grafana dashboard configuration (optional)\n\nWhen present, Grafana will be deployed for visualization.\n**Requires Prometheus to be configured** - Grafana depends on\nPrometheus as its data source.\n\nUses `GrafanaSection` for JSON parsing with String primitives.\nConverted to domain `GrafanaConfig` via `TryInto<EnvironmentParams>`.",
1525
"anyOf": [
1626
{
1727
"$ref": "#/$defs/GrafanaSection"
@@ -35,7 +45,7 @@
3545
"default": null
3646
},
3747
"prometheus": {
38-
"description": "Prometheus monitoring configuration (optional)\n\nWhen present, Prometheus will be deployed to monitor the tracker.\nUses `PrometheusSection` for JSON parsing with String primitives.\nConverted to domain `PrometheusConfig` via `to_environment_params()`.",
48+
"description": "Prometheus monitoring configuration (optional)\n\nWhen present, Prometheus will be deployed to monitor the tracker.\nUses `PrometheusSection` for JSON parsing with String primitives.\nConverted to domain `PrometheusConfig` via `TryInto<EnvironmentParams>`.",
3949
"anyOf": [
4050
{
4151
"$ref": "#/$defs/PrometheusSection"
@@ -47,15 +57,15 @@
4757
"default": null
4858
},
4959
"provider": {
50-
"description": "Provider-specific configuration (LXD, Hetzner, etc.)\n\nUses `ProviderSection` for JSON parsing with raw primitives.\nConverted to domain `ProviderConfig` via `to_environment_params()`.",
60+
"description": "Provider-specific configuration (LXD, Hetzner, etc.)\n\nUses `ProviderSection` for JSON parsing with raw primitives.\nConverted to domain `ProviderConfig` via `TryInto<EnvironmentParams>`.",
5161
"$ref": "#/$defs/ProviderSection"
5262
},
5363
"ssh_credentials": {
5464
"description": "SSH credentials configuration",
5565
"$ref": "#/$defs/SshCredentialsConfig"
5666
},
5767
"tracker": {
58-
"description": "Tracker deployment configuration\n\nUses `TrackerSection` for JSON parsing with String primitives.\nConverted to domain `TrackerConfig` via `to_environment_params()`.",
68+
"description": "Tracker deployment configuration\n\nUses `TrackerSection` for JSON parsing with String primitives.\nConverted to domain `TrackerConfig` via `TryInto<EnvironmentParams>`.",
5969
"$ref": "#/$defs/TrackerSection"
6070
}
6171
},
@@ -66,6 +76,24 @@
6676
"tracker"
6777
],
6878
"$defs": {
79+
"BackupSection": {
80+
"description": "Backup configuration section (DTO)\n\nOptional configuration for automated backups. If present, backup support\nis enabled with the specified schedule and retention policy.\n\n# Examples\n\n```json\n{\n \"schedule\": \"0 3 * * *\",\n \"retention_days\": 7\n}\n```\n\nAll fields have defaults, so you can enable backup with minimal config:\n\n```json\n{\n \"backup\": {}\n}\n```",
81+
"type": "object",
82+
"properties": {
83+
"retention_days": {
84+
"description": "Number of days to retain backups before automatic deletion\n\nDefault: 7 days\n\nMust be greater than 0.",
85+
"type": "integer",
86+
"format": "uint32",
87+
"default": 7,
88+
"minimum": 0
89+
},
90+
"schedule": {
91+
"description": "Cron schedule for backups (5-field format: minute hour day month weekday)\n\nDefault: \"0 3 * * *\" (3:00 AM daily)\n\nExamples:\n- \"0 3 * * *\" - 3:00 AM daily\n- \"0 */6 * * *\" - Every 6 hours\n- \"0 0 * * 0\" - Midnight every Sunday",
92+
"type": "string",
93+
"default": "0 3 * * *"
94+
}
95+
}
96+
},
6997
"DatabaseSection": {
7098
"description": "Database configuration section (application DTO)\n\nMirrors the domain `DatabaseConfig` enum but at the application layer.\nSupports both `SQLite` and `MySQL` database backends.\n\n# Examples\n\n```json\n{\n \"driver\": \"sqlite3\",\n \"database_name\": \"tracker.db\"\n}\n```\n\n```json\n{\n \"driver\": \"mysql\",\n \"host\": \"localhost\",\n \"port\": 3306,\n \"database_name\": \"tracker\",\n \"username\": \"tracker_user\",\n \"password\": \"secure_password\"\n}\n```",
7199
"oneOf": [
@@ -237,12 +265,15 @@
237265
]
238266
},
239267
"HttpApiSection": {
268+
"description": "HTTP API configuration section (Application DTO)\n\nThis is a Data Transfer Object that uses primitive types (`String`) for\nJSON deserialization. It converts to the domain type `HttpApiConfig` via\nthe `TryFrom` trait, which delegates validation to the domain layer.\n\n# Responsibility Split\n\n- **This DTO**: Parse strings into typed values (`SocketAddr`, `DomainName`)\n- **Domain type**: Enforce business invariants (port != 0, TLS requires domain, etc.)\n\n# Usage\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::tracker::HttpApiSection;\nuse torrust_tracker_deployer_lib::domain::tracker::HttpApiConfig;\n\nlet section = HttpApiSection {\n bind_address: \"0.0.0.0:1212\".to_string(),\n admin_token: \"MyToken\".to_string(),\n domain: None,\n use_tls_proxy: None,\n};\n\nlet config: HttpApiConfig = section.try_into()?;\n# Ok::<(), Box<dyn std::error::Error>>(())\n```\n\n# JSON Example\n\n```json\n{\n \"bind_address\": \"0.0.0.0:1212\",\n \"admin_token\": \"MyAccessToken\",\n \"domain\": \"api.example.com\",\n \"use_tls_proxy\": true\n}\n```",
240269
"type": "object",
241270
"properties": {
242271
"admin_token": {
272+
"description": "Admin token as plain string (at DTO boundary)\n\nConverted to `ApiToken` (secrecy-wrapped) in domain layer.",
243273
"type": "string"
244274
},
245275
"bind_address": {
276+
"description": "Bind address as string (e.g., \"0.0.0.0:1212\")\n\nParsed to `SocketAddr` during conversion.",
246277
"type": "string"
247278
},
248279
"domain": {
@@ -337,7 +368,7 @@
337368
]
338369
},
339370
"ProviderSection": {
340-
"description": "Provider-specific configuration section\n\nEach variant contains the configuration fields specific to that provider\nusing **raw primitives** (`String`) for JSON deserialization.\n\nThis is a tagged enum that deserializes based on the `\"provider\"` field in JSON.\n\n# Conversion\n\nUse `to_provider_config()` to validate and convert to domain types.\n\n# Examples\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::{\n ProviderSection, LxdProviderSection\n};\n\nlet section = ProviderSection::Lxd(LxdProviderSection {\n profile_name: \"torrust-profile-dev\".to_string(),\n});\n\nlet config = section.to_provider_config().unwrap();\nassert_eq!(config.provider_name(), \"lxd\");\n```",
371+
"description": "Provider-specific configuration section\n\nEach variant contains the configuration fields specific to that provider\nusing **raw primitives** (`String`) for JSON deserialization.\n\nThis is a tagged enum that deserializes based on the `\"provider\"` field in JSON.\n\n# Conversion\n\nUse `try_into()` or `ProviderConfig::try_from()` to validate and convert to domain types.\n\n# Examples\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::{\n ProviderSection, LxdProviderSection\n};\nuse torrust_tracker_deployer_lib::domain::provider::ProviderConfig;\nuse std::convert::TryInto;\n\nlet section = ProviderSection::Lxd(LxdProviderSection {\n profile_name: \"torrust-profile-dev\".to_string(),\n});\n\nlet config: ProviderConfig = section.try_into().unwrap();\nassert_eq!(config.provider_name(), \"lxd\");\n```",
341372
"oneOf": [
342373
{
343374
"description": "LXD provider configuration",
@@ -476,4 +507,4 @@
476507
]
477508
}
478509
}
479-
}
510+
}

src/application/command_handlers/create/config/environment_config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,8 @@ impl EnvironmentCreationConfig {
346346
},
347347
prometheus: Some(PrometheusSection::default()),
348348
grafana: Some(GrafanaSection::default()),
349-
https: None, // Set to HttpsSection with admin_email for HTTPS deployments
350-
backup: None, // Set to BackupSection or empty object {} for automated backups
349+
https: None, // Set to HttpsSection with admin_email for HTTPS deployments
350+
backup: Some(super::backup::BackupSection::default()), // Backups enabled by default with daily 3 AM schedule and 7-day retention
351351
}
352352
}
353353

0 commit comments

Comments
 (0)