Skip to main content

torrust_tracker_deployer_lib/domain/environment/
params.rs

1//! Environment Creation Parameters
2//!
3//! This module provides `EnvironmentParams`, a domain value object that holds
4//! all validated parameters needed to create an `Environment` aggregate.
5//!
6//! # DDD Pattern: Factory Input
7//!
8//! This is a value object that groups all the inputs required by the
9//! `Environment::create()` factory method. It provides:
10//!
11//! - **Named fields**: Self-documenting, no positional confusion
12//! - **Type safety**: All fields are validated domain types
13//! - **Clean API**: Single parameter instead of 10+ arguments
14//!
15//! # Architecture
16//!
17//! ```text
18//! EnvironmentCreationConfig (DTO - Application Layer)
19//!         │
20//!         │ TryFrom (in Application Layer)
21//!         ▼
22//! EnvironmentParams (Domain Value Object)
23//!         │
24//!         │ Environment::create(params, working_dir, timestamp)
25//!         ▼
26//! Environment<Created> (Domain Aggregate)
27//! ```
28//!
29//! The `TryFrom` implementation lives in the Application layer since it needs
30//! to reference the DTO, but `EnvironmentParams` itself is a pure domain type.
31//!
32//! # Usage
33//!
34//! ```rust,no_run
35//! use torrust_tracker_deployer_lib::domain::environment::EnvironmentParams;
36//! use torrust_tracker_deployer_lib::domain::{EnvironmentName, InstanceName};
37//!
38//! // EnvironmentParams is typically constructed via TryFrom in application layer
39//! // or directly in domain tests
40//! ```
41
42use crate::adapters::ssh::SshCredentials;
43use crate::domain::backup::BackupConfig;
44use crate::domain::grafana::GrafanaConfig;
45use crate::domain::https::HttpsConfig;
46use crate::domain::prometheus::PrometheusConfig;
47use crate::domain::provider::ProviderConfig;
48use crate::domain::tracker::TrackerConfig;
49use crate::domain::{EnvironmentName, InstanceName};
50
51/// Parameters for creating a new Environment aggregate
52///
53/// This value object contains all validated domain objects needed to construct
54/// an `Environment<Created>` aggregate. It serves as a "factory input" pattern,
55/// grouping related parameters into a single, self-documenting type.
56///
57/// # Field Categories
58///
59/// - **Identity**: `environment_name`, `instance_name`
60/// - **Infrastructure**: `provider_config`, `ssh_credentials`, `ssh_port`
61/// - **Application**: `tracker_config`
62/// - **Observability**: `prometheus_config`, `grafana_config`
63/// - **Security**: `https_config`
64///
65/// # Invariants
66///
67/// All fields are pre-validated domain types. Cross-field validation
68/// (e.g., Grafana requires Prometheus) happens in `Environment::create()`.
69#[derive(Debug, Clone)]
70pub struct EnvironmentParams {
71    /// Validated environment name (e.g., "production", "staging")
72    pub environment_name: EnvironmentName,
73
74    /// Validated instance name for the VM/container
75    ///
76    /// Either user-provided or auto-generated as `torrust-tracker-vm-{env_name}`
77    pub instance_name: InstanceName,
78
79    /// Provider-specific configuration (LXD, Hetzner, etc.)
80    pub provider_config: ProviderConfig,
81
82    /// SSH credentials for remote access to the deployed instance
83    pub ssh_credentials: SshCredentials,
84
85    /// SSH port for remote connections (typically 22)
86    pub ssh_port: u16,
87
88    /// Tracker application configuration
89    pub tracker_config: TrackerConfig,
90
91    /// Optional Prometheus monitoring configuration
92    pub prometheus_config: Option<PrometheusConfig>,
93
94    /// Optional Grafana dashboard configuration
95    ///
96    /// Note: Requires `prometheus_config` to be set (validated in `Environment::create()`)
97    pub grafana_config: Option<GrafanaConfig>,
98
99    /// Optional HTTPS/TLS configuration for secure endpoints
100    pub https_config: Option<HttpsConfig>,
101
102    /// Optional backup service configuration
103    pub backup_config: Option<BackupConfig>,
104}
105
106impl EnvironmentParams {
107    /// Creates a new `EnvironmentParams` instance
108    ///
109    /// This constructor is primarily used in domain tests. In production,
110    /// `EnvironmentParams` is typically constructed via `TryFrom` conversion
111    /// from a configuration DTO in the application layer.
112    ///
113    /// # Arguments
114    ///
115    /// * `environment_name` - Validated environment name
116    /// * `instance_name` - Validated instance name
117    /// * `provider_config` - Provider configuration
118    /// * `ssh_credentials` - SSH access credentials
119    /// * `ssh_port` - SSH port number
120    /// * `tracker_config` - Tracker application configuration
121    /// * `prometheus_config` - Optional Prometheus configuration
122    /// * `grafana_config` - Optional Grafana configuration
123    /// * `https_config` - Optional HTTPS configuration
124    /// * `backup_config` - Optional backup configuration
125    #[must_use]
126    #[allow(clippy::too_many_arguments)]
127    pub fn new(
128        environment_name: EnvironmentName,
129        instance_name: InstanceName,
130        provider_config: ProviderConfig,
131        ssh_credentials: SshCredentials,
132        ssh_port: u16,
133        tracker_config: TrackerConfig,
134        prometheus_config: Option<PrometheusConfig>,
135        grafana_config: Option<GrafanaConfig>,
136        https_config: Option<HttpsConfig>,
137        backup_config: Option<BackupConfig>,
138    ) -> Self {
139        Self {
140            environment_name,
141            instance_name,
142            provider_config,
143            ssh_credentials,
144            ssh_port,
145            tracker_config,
146            prometheus_config,
147            grafana_config,
148            https_config,
149            backup_config,
150        }
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use crate::domain::provider::LxdConfig;
158    use crate::domain::ProfileName;
159    use crate::shared::Username;
160    use std::path::PathBuf;
161
162    fn sample_ssh_credentials() -> SshCredentials {
163        let project_root = env!("CARGO_MANIFEST_DIR");
164        SshCredentials::new(
165            PathBuf::from(format!("{project_root}/fixtures/testing_rsa")),
166            PathBuf::from(format!("{project_root}/fixtures/testing_rsa.pub")),
167            Username::new("torrust").unwrap(),
168        )
169    }
170
171    fn sample_tracker_config() -> TrackerConfig {
172        TrackerConfig::default()
173    }
174
175    #[test]
176    fn it_should_create_environment_params_with_all_fields() {
177        let params = EnvironmentParams::new(
178            EnvironmentName::new("test-env").unwrap(),
179            InstanceName::new("test-instance".to_string()).unwrap(),
180            ProviderConfig::Lxd(LxdConfig {
181                profile_name: ProfileName::new("lxd-test").unwrap(),
182            }),
183            sample_ssh_credentials(),
184            22,
185            sample_tracker_config(),
186            None,
187            None,
188            None,
189            None,
190        );
191
192        assert_eq!(params.environment_name.as_str(), "test-env");
193        assert_eq!(params.instance_name.as_str(), "test-instance");
194        assert_eq!(params.ssh_port, 22);
195    }
196
197    #[test]
198    fn it_should_provide_named_field_access() {
199        let params = EnvironmentParams::new(
200            EnvironmentName::new("prod").unwrap(),
201            InstanceName::new("prod-vm".to_string()).unwrap(),
202            ProviderConfig::Lxd(LxdConfig {
203                profile_name: ProfileName::new("lxd-prod").unwrap(),
204            }),
205            sample_ssh_credentials(),
206            2222,
207            sample_tracker_config(),
208            None,
209            None,
210            None,
211            None,
212        );
213
214        // All fields accessible by name
215        let _name: &EnvironmentName = &params.environment_name;
216        let _instance: &InstanceName = &params.instance_name;
217        let _provider: &ProviderConfig = &params.provider_config;
218        let _ssh: &SshCredentials = &params.ssh_credentials;
219        let _port: u16 = params.ssh_port;
220        let _tracker: &TrackerConfig = &params.tracker_config;
221        let _prometheus: &Option<PrometheusConfig> = &params.prometheus_config;
222        let _grafana: &Option<GrafanaConfig> = &params.grafana_config;
223        let _https: &Option<HttpsConfig> = &params.https_config;
224    }
225}