-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathreal_container.rs
More file actions
173 lines (152 loc) · 5.36 KB
/
Copy pathreal_container.rs
File metadata and controls
173 lines (152 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! Real SSH server container using Docker
use std::net::{IpAddr, Ipv4Addr};
use testcontainers::{
core::{IntoContainerPort, WaitFor},
runners::AsyncRunner,
ContainerAsync, GenericImage,
};
use super::config::SshServerConfig;
use super::constants::SSH_CONTAINER_PORT;
use super::error::SshServerError;
use crate::adapters::docker::DockerClient;
/// Real SSH server container using Docker
///
/// This implementation starts an actual Docker container running SSH server
/// for full integration testing. Use this when you need to test actual SSH
/// protocol connectivity and command execution.
pub struct RealSshServerContainer {
config: SshServerConfig,
#[allow(dead_code)]
container: ContainerAsync<GenericImage>,
host_ip: IpAddr,
ssh_port: u16,
}
impl RealSshServerContainer {
/// Start a real SSH server container with custom configuration
///
/// This starts an actual Docker container running SSH server using the provided
/// configuration. The Docker image is built automatically from the Dockerfile.
/// This ensures the tests are self-contained and work in CI environments.
///
/// # Arguments
///
/// * `config` - Configuration specifying image name, credentials, ports, etc.
///
/// # Returns
///
/// A real container with SSH server running, or an error if Docker
/// is not available or the image cannot be built/started.
///
/// # Errors
///
/// Returns an error if:
/// - The Dockerfile is not found at the configured path
/// - Docker build command fails
/// - Container startup fails
/// - Port mapping fails
pub async fn start_with_config(config: SshServerConfig) -> Result<Self, SshServerError> {
// Build the SSH server image from Dockerfile
// This ensures tests are self-contained and work in CI
let dockerfile_dir = &config.dockerfile_dir;
if !dockerfile_dir.exists() || !dockerfile_dir.join("Dockerfile").exists() {
return Err(SshServerError::DockerfileNotFound {
expected_path: dockerfile_dir.join("Dockerfile"),
});
}
// Build the Docker image using docker CLI
println!(
"Building SSH server Docker image from {}",
dockerfile_dir.display()
);
let docker_client = DockerClient::new();
docker_client
.build_image(
&config.dockerfile_dir,
&config.image_name,
&config.image_tag,
)
.map_err(|source| SshServerError::DockerClientError {
source: Box::new(source),
})?;
println!("SSH server Docker image built successfully");
// Start the container using the built image
// Note: SSH always runs on port 22 inside the container
let image = GenericImage::new(&config.image_name, &config.image_tag)
.with_exposed_port(SSH_CONTAINER_PORT.tcp())
.with_wait_for(WaitFor::seconds(config.startup_wait_secs));
let container = image
.start()
.await
.map_err(|source| SshServerError::ContainerStartFailed { source })?;
// Get the mapped SSH port (testcontainers maps to random host port)
let ssh_port: u16 = container
.get_host_port_ipv4(SSH_CONTAINER_PORT)
.await
.map_err(|source| SshServerError::PortMappingFailed { source })?;
Ok(Self {
config,
container,
host_ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
ssh_port,
})
}
/// Start a real SSH server container with default configuration
///
/// This is a convenience method that starts a container with default
/// configuration values from constants. For custom configuration,
/// use [`Self::start_with_config`].
///
/// # Returns
///
/// A real container with SSH server running, or an error if Docker
/// is not available or the image cannot be built/started.
///
/// # Errors
///
/// Returns an error if:
/// - The `docker/ssh-server/Dockerfile` is not found
/// - Docker build command fails
/// - Container startup fails
/// - Port mapping fails
pub async fn start() -> Result<Self, SshServerError> {
Self::start_with_config(SshServerConfig::default()).await
}
/// Get the SSH port mapped by the container
///
/// Returns the host port that maps to the container's SSH port (22).
#[must_use]
pub fn ssh_port(&self) -> u16 {
self.ssh_port
}
/// Get the container's host IP address
///
/// Returns the IP address to connect to the container from the host.
#[must_use]
pub fn host_ip(&self) -> IpAddr {
self.host_ip
}
/// Get the test username configured in the container
#[must_use]
pub fn test_username(&self) -> &str {
&self.config.username
}
/// Get the test password configured in the container
#[must_use]
pub fn test_password(&self) -> &str {
&self.config.password
}
}
impl super::SshServerContainer for RealSshServerContainer {
fn ssh_port(&self) -> u16 {
self.ssh_port
}
fn host_ip(&self) -> IpAddr {
self.host_ip
}
fn test_username(&self) -> &str {
&self.config.username
}
fn test_password(&self) -> &str {
&self.config.password
}
}