Skip to content

Commit d556053

Browse files
committed
feat: [#229] add create schema command handler
1 parent 3ade9db commit d556053

File tree

5 files changed

+632
-0
lines changed

5 files changed

+632
-0
lines changed

schema.json

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"title": "EnvironmentCreationConfig",
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 }\n}\"#;\n\nlet config: EnvironmentCreationConfig = serde_json::from_str(json)?;\n# Ok::<(), Box<dyn std::error::Error>>(())\n```",
5+
"type": "object",
6+
"properties": {
7+
"environment": {
8+
"description": "Environment-specific settings",
9+
"$ref": "#/$defs/EnvironmentSection"
10+
},
11+
"provider": {
12+
"description": "Provider-specific configuration (LXD, Hetzner, etc.)\n\nUses `ProviderSection` for JSON parsing with raw primitives.\nConverted to domain `ProviderConfig` via `to_environment_params()`.",
13+
"$ref": "#/$defs/ProviderSection"
14+
},
15+
"ssh_credentials": {
16+
"description": "SSH credentials configuration",
17+
"$ref": "#/$defs/SshCredentialsConfig"
18+
},
19+
"tracker": {
20+
"description": "Tracker deployment configuration\n\nUses `TrackerSection` for JSON parsing with String primitives.\nConverted to domain `TrackerConfig` via `to_environment_params()`.",
21+
"$ref": "#/$defs/TrackerSection"
22+
}
23+
},
24+
"required": [
25+
"environment",
26+
"ssh_credentials",
27+
"provider",
28+
"tracker"
29+
],
30+
"$defs": {
31+
"DatabaseSection": {
32+
"description": "Database configuration section (application DTO)\n\nMirrors the domain `DatabaseConfig` enum but at the application layer.\nCurrently only `SQLite` is supported.\n\n# Examples\n\n```json\n{\n \"driver\": \"sqlite3\",\n \"database_name\": \"tracker.db\"\n}\n```",
33+
"oneOf": [
34+
{
35+
"description": "`SQLite` file-based database",
36+
"type": "object",
37+
"properties": {
38+
"database_name": {
39+
"description": "Database file name",
40+
"type": "string"
41+
},
42+
"driver": {
43+
"type": "string",
44+
"const": "sqlite3"
45+
}
46+
},
47+
"required": [
48+
"driver",
49+
"database_name"
50+
]
51+
}
52+
]
53+
},
54+
"EnvironmentSection": {
55+
"description": "Environment-specific configuration section\n\nContains configuration specific to the environment being created.",
56+
"type": "object",
57+
"properties": {
58+
"instance_name": {
59+
"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",
60+
"type": [
61+
"string",
62+
"null"
63+
],
64+
"default": null
65+
},
66+
"name": {
67+
"description": "Name of the environment to create\n\nMust follow environment naming rules:\n- Lowercase letters and numbers only\n- Dashes as word separators\n- Cannot start or end with separators\n- Cannot start with numbers",
68+
"type": "string"
69+
}
70+
},
71+
"required": [
72+
"name"
73+
]
74+
},
75+
"HetznerProviderSection": {
76+
"description": "Hetzner-specific configuration section\n\nUses raw `String` fields for JSON deserialization. Convert to domain\n`HetznerConfig` via `ProviderSection::to_provider_config()`.\n\n# Examples\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::HetznerProviderSection;\n\nlet section = HetznerProviderSection {\n api_token: \"your-api-token\".to_string(),\n server_type: \"cx22\".to_string(),\n location: \"nbg1\".to_string(),\n image: \"ubuntu-24.04\".to_string(),\n};\n```",
77+
"type": "object",
78+
"properties": {
79+
"api_token": {
80+
"description": "Hetzner API token (raw string).",
81+
"type": "string"
82+
},
83+
"image": {
84+
"description": "Hetzner server image (e.g., \"ubuntu-24.04\", \"ubuntu-22.04\", \"debian-12\").",
85+
"type": "string"
86+
},
87+
"location": {
88+
"description": "Hetzner datacenter location (e.g., \"fsn1\", \"nbg1\", \"hel1\").",
89+
"type": "string"
90+
},
91+
"server_type": {
92+
"description": "Hetzner server type (e.g., \"cx22\", \"cx32\", \"cpx11\").",
93+
"type": "string"
94+
}
95+
},
96+
"required": [
97+
"api_token",
98+
"server_type",
99+
"location",
100+
"image"
101+
]
102+
},
103+
"HttpApiSection": {
104+
"type": "object",
105+
"properties": {
106+
"admin_token": {
107+
"type": "string"
108+
},
109+
"bind_address": {
110+
"type": "string"
111+
}
112+
},
113+
"required": [
114+
"bind_address",
115+
"admin_token"
116+
]
117+
},
118+
"HttpTrackerSection": {
119+
"type": "object",
120+
"properties": {
121+
"bind_address": {
122+
"type": "string"
123+
}
124+
},
125+
"required": [
126+
"bind_address"
127+
]
128+
},
129+
"LxdProviderSection": {
130+
"description": "LXD-specific configuration section\n\nUses raw `String` for JSON deserialization. Convert to domain `LxdConfig`\nvia `ProviderSection::to_provider_config()`.\n\n# Examples\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::LxdProviderSection;\n\nlet section = LxdProviderSection {\n profile_name: \"torrust-profile-dev\".to_string(),\n};\n```",
131+
"type": "object",
132+
"properties": {
133+
"profile_name": {
134+
"description": "LXD profile name (raw string - validated on conversion).",
135+
"type": "string"
136+
}
137+
},
138+
"required": [
139+
"profile_name"
140+
]
141+
},
142+
"ProviderSection": {
143+
"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```",
144+
"oneOf": [
145+
{
146+
"description": "LXD provider configuration",
147+
"type": "object",
148+
"properties": {
149+
"provider": {
150+
"type": "string",
151+
"const": "lxd"
152+
}
153+
},
154+
"$ref": "#/$defs/LxdProviderSection",
155+
"required": [
156+
"provider"
157+
]
158+
},
159+
{
160+
"description": "Hetzner provider configuration",
161+
"type": "object",
162+
"properties": {
163+
"provider": {
164+
"type": "string",
165+
"const": "hetzner"
166+
}
167+
},
168+
"$ref": "#/$defs/HetznerProviderSection",
169+
"required": [
170+
"provider"
171+
]
172+
}
173+
]
174+
},
175+
"SshCredentialsConfig": {
176+
"description": "SSH credentials configuration for remote instance authentication\n\nThis is a configuration-layer value object that uses strings for paths\nand username. It is distinct from `adapters::ssh::SshCredentials` which\nuses domain types (`PathBuf`, `Username`).\n\n# Examples\n\n```no_run\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::SshCredentialsConfig;\n\nlet config = SshCredentialsConfig {\n private_key_path: \"fixtures/testing_rsa\".to_string(),\n public_key_path: \"fixtures/testing_rsa.pub\".to_string(),\n username: \"torrust\".to_string(),\n port: 22,\n};\n```",
177+
"type": "object",
178+
"properties": {
179+
"port": {
180+
"description": "SSH port for remote connections\n\nDefaults to 22 (standard SSH port) if not specified in configuration.",
181+
"type": "integer",
182+
"format": "uint16",
183+
"default": 22,
184+
"maximum": 65535,
185+
"minimum": 0
186+
},
187+
"private_key_path": {
188+
"description": "Path to the SSH private key file (as string in config)",
189+
"type": "string"
190+
},
191+
"public_key_path": {
192+
"description": "Path to the SSH public key file (as string in config)",
193+
"type": "string"
194+
},
195+
"username": {
196+
"description": "SSH username (as string in config)\n\nDefaults to \"torrust\" if not specified in configuration.",
197+
"type": "string",
198+
"default": "torrust"
199+
}
200+
},
201+
"required": [
202+
"private_key_path",
203+
"public_key_path"
204+
]
205+
},
206+
"TrackerCoreSection": {
207+
"description": "Tracker core configuration section (application DTO)\n\nContains core tracker settings like database and privacy mode.\n\n# Examples\n\n```json\n{\n \"database\": {\n \"driver\": \"sqlite3\",\n \"database_name\": \"tracker.db\"\n },\n \"private\": false\n}\n```",
208+
"type": "object",
209+
"properties": {
210+
"database": {
211+
"description": "Database configuration",
212+
"$ref": "#/$defs/DatabaseSection"
213+
},
214+
"private": {
215+
"description": "Privacy mode: true for private tracker, false for public",
216+
"type": "boolean"
217+
}
218+
},
219+
"required": [
220+
"database",
221+
"private"
222+
]
223+
},
224+
"TrackerSection": {
225+
"description": "Tracker configuration section (application DTO)\n\nAggregates all tracker configuration sections: core, UDP trackers,\nHTTP trackers, and HTTP API.\n\n# Examples\n\n```json\n{\n \"core\": {\n \"database\": {\n \"driver\": \"sqlite3\",\n \"database_name\": \"tracker.db\"\n },\n \"private\": false\n },\n \"udp_trackers\": [\n { \"bind_address\": \"0.0.0.0:6969\" }\n ],\n \"http_trackers\": [\n { \"bind_address\": \"0.0.0.0:7070\" }\n ],\n \"http_api\": {\n \"bind_address\": \"0.0.0.0:1212\",\n \"admin_token\": \"MyAccessToken\"\n }\n}\n```",
226+
"type": "object",
227+
"properties": {
228+
"core": {
229+
"description": "Core tracker configuration (database, privacy mode)",
230+
"$ref": "#/$defs/TrackerCoreSection"
231+
},
232+
"http_api": {
233+
"description": "HTTP API configuration",
234+
"$ref": "#/$defs/HttpApiSection"
235+
},
236+
"http_trackers": {
237+
"description": "HTTP tracker instances",
238+
"type": "array",
239+
"items": {
240+
"$ref": "#/$defs/HttpTrackerSection"
241+
}
242+
},
243+
"udp_trackers": {
244+
"description": "UDP tracker instances",
245+
"type": "array",
246+
"items": {
247+
"$ref": "#/$defs/UdpTrackerSection"
248+
}
249+
}
250+
},
251+
"required": [
252+
"core",
253+
"udp_trackers",
254+
"http_trackers",
255+
"http_api"
256+
]
257+
},
258+
"UdpTrackerSection": {
259+
"type": "object",
260+
"properties": {
261+
"bind_address": {
262+
"type": "string"
263+
}
264+
},
265+
"required": [
266+
"bind_address"
267+
]
268+
}
269+
}
270+
}

src/application/command_handlers/create/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
pub mod config;
7676
pub mod errors;
7777
pub mod handler;
78+
pub mod schema;
7879

7980
#[cfg(test)]
8081
mod tests;

0 commit comments

Comments
 (0)