pub struct CreateCommandHandler { /* private fields */ }Expand description
Command to create a new deployment environment
This command is delivery-agnostic and can be used from CLI, REST API, GraphQL, or any other delivery mechanism. It orchestrates the business logic for environment creation without knowledge of how the configuration was obtained.
§Architecture
The command follows these design principles:
- Synchronous: No async/await, following existing patterns
- Dependency Injection: Uses
Arc<dyn Trait>for testability - Repository Pattern: Delegates persistence to repository
- Explicit Errors: All failures return structured errors with
.help()
§Business Logic Flow
- Convert configuration to domain objects
- Check if environment already exists (prevent duplicates)
- Create environment entity using
Environment::new() - Persist via repository (repository handles directory creation)
§Examples
use std::sync::Arc;
use torrust_tracker_deployer_lib::application::command_handlers::create::CreateCommandHandler;
use torrust_tracker_deployer_lib::application::command_handlers::create::config::{
EnvironmentCreationConfig, EnvironmentSection, LxdProviderSection, ProviderSection,
SshCredentialsConfig,
};
use torrust_tracker_deployer_lib::application::command_handlers::create::config::tracker::TrackerSection;
use torrust_tracker_deployer_lib::infrastructure::persistence::file_repository_factory::FileRepositoryFactory;
use torrust_tracker_deployer_lib::shared::{SystemClock, Clock};
// Setup dependencies
let file_repository_factory = FileRepositoryFactory::new(std::time::Duration::from_secs(30));
let repository = file_repository_factory.create(std::path::PathBuf::from("."));
let clock: Arc<dyn Clock> = Arc::new(SystemClock);
// Create command
let command = CreateCommandHandler::new(repository, clock);
// Prepare configuration
let config = EnvironmentCreationConfig::new(
EnvironmentSection {
name: "dev".to_string(),
description: None,
instance_name: None, // Auto-generate from environment name
},
SshCredentialsConfig::new(
"fixtures/testing_rsa".to_string(),
"fixtures/testing_rsa.pub".to_string(),
"torrust".to_string(),
22,
),
ProviderSection::Lxd(LxdProviderSection {
profile_name: "lxd-dev".to_string(),
}),
TrackerSection::default(),
None, // prometheus
None, // grafana
None, // https
None, // backup
);
// Execute command with working directory
let working_dir = std::path::Path::new(".");
let environment = command.execute(config, working_dir)?;
println!("Created environment: {}", environment.name());Implementations§
Source§impl CreateCommandHandler
impl CreateCommandHandler
Sourcepub fn new(
environment_repository: Arc<dyn EnvironmentRepository>,
clock: Arc<dyn Clock>,
) -> Self
pub fn new( environment_repository: Arc<dyn EnvironmentRepository>, clock: Arc<dyn Clock>, ) -> Self
Create a new CreateCommandHandler with required dependencies
§Arguments
environment_repository- Repository for persisting environment stateclock- Clock for timestamp generation (for future use)
§Examples
use std::sync::Arc;
use torrust_tracker_deployer_lib::application::command_handlers::create::CreateCommandHandler;
use torrust_tracker_deployer_lib::infrastructure::persistence::file_repository_factory::FileRepositoryFactory;
use torrust_tracker_deployer_lib::shared::{SystemClock, Clock};
let file_repository_factory = FileRepositoryFactory::new(std::time::Duration::from_secs(30));
let repository = file_repository_factory.create(std::path::PathBuf::from("."));
let clock: Arc<dyn Clock> = Arc::new(SystemClock);
let command = CreateCommandHandler::new(repository, clock);Sourcepub fn execute(
&self,
config: EnvironmentCreationConfig,
working_dir: &Path,
) -> Result<Environment<Created>, CreateCommandHandlerError>
pub fn execute( &self, config: EnvironmentCreationConfig, working_dir: &Path, ) -> Result<Environment<Created>, CreateCommandHandlerError>
Execute the create command with validated configuration
This method orchestrates the complete environment creation workflow:
- Converts configuration to domain objects
- Validates environment uniqueness
- Creates the environment entity
- Persists the environment state
§Arguments
config- Validated environment configuration from domain layer
§Returns
Ok(Environment<Created>)- Successfully created environmentErr(CreateCommandHandlerError)- Business logic or persistence failure
§Business Rules
- Configuration must convert to valid domain objects
- Environment name must be unique (no duplicates)
- Repository handles directory creation atomically during save
- Environment state must be persisted successfully
§Errors
Returns an error if:
- Configuration validation fails
- Environment with the same name already exists
- Repository persistence fails
All errors implement .help() with detailed troubleshooting guidance.
§Panics
This function does not panic in practice. The internal .expect() call
when generating the profile name is theoretically unreachable because
valid environment names always produce valid profile names.
§Examples
use torrust_tracker_deployer_lib::application::command_handlers::create::CreateCommandHandler;
use torrust_tracker_deployer_lib::application::command_handlers::create::config::{
EnvironmentCreationConfig, EnvironmentSection, LxdProviderSection, ProviderSection,
SshCredentialsConfig,
};
use torrust_tracker_deployer_lib::application::command_handlers::create::config::tracker::TrackerSection;
let config = EnvironmentCreationConfig::new(
EnvironmentSection {
name: "staging".to_string(),
description: None,
instance_name: None, // Auto-generate from environment name
},
SshCredentialsConfig::new(
"keys/stage_key".to_string(),
"keys/stage_key.pub".to_string(),
"torrust".to_string(),
22,
),
ProviderSection::Lxd(LxdProviderSection {
profile_name: "lxd-staging".to_string(),
}),
TrackerSection::default(),
None, // prometheus
None, // grafana
None, // https
None, // backup
);
let working_dir = std::path::Path::new(".");
let environment = command.execute(config, working_dir)?;
println!("Created: {}", environment.name());Auto Trait Implementations§
impl !RefUnwindSafe for CreateCommandHandler
impl !Send for CreateCommandHandler
impl !Sync for CreateCommandHandler
impl !UnwindSafe for CreateCommandHandler
impl Freeze for CreateCommandHandler
impl Unpin for CreateCommandHandler
impl UnsafeUnpin for CreateCommandHandler
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request