Skip to content

Commit 6d6f866

Browse files
committed
Node/testing: logging for OCaml config
1 parent d0674fc commit 6d6f866

File tree

1 file changed

+192
-13
lines changed

1 file changed

+192
-13
lines changed

node/testing/src/node/ocaml/config.rs

Lines changed: 192 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
//! OCaml Node Configuration Module
2+
//!
3+
//! This module provides configuration structures and executable management
4+
//! for OCaml Mina nodes in the testing framework. It supports multiple
5+
//! execution modes including local binaries and Docker containers.
6+
//!
7+
//! # Key Components
8+
//!
9+
//! - [`OcamlNodeExecutable`] - Execution method selection (local/Docker)
10+
//! - [`OcamlNodeTestingConfig`] - High-level node configuration
11+
//! - [`OcamlNodeConfig`] - Low-level process configuration
12+
//! - [`DaemonJson`] - Genesis configuration management
13+
//!
14+
//! # Executable Auto-Detection
15+
//!
16+
//! The module automatically detects the best available execution method:
17+
//! 1. Local `mina` binary (preferred)
18+
//! 2. Docker with default image (fallback)
19+
//! 3. Custom Docker images (configurable)
20+
121
use std::{
222
ffi::{OsStr, OsString},
323
fs,
@@ -6,13 +26,25 @@ use std::{
626
str::FromStr,
727
};
828

9-
use node::{account::AccountSecretKey, p2p::connection::outgoing::P2pConnectionOutgoingInitOpts};
29+
use node::{
30+
account::AccountSecretKey,
31+
core::log::{info, system_time, warn},
32+
p2p::connection::outgoing::P2pConnectionOutgoingInitOpts,
33+
};
1034
use serde::{Deserialize, Serialize};
1135

36+
/// High-level configuration for OCaml node testing scenarios.
37+
///
38+
/// This struct provides the main configuration interface for creating
39+
/// OCaml nodes in test scenarios, abstracting away low-level details
40+
/// like port allocation and process management.
1241
#[derive(Serialize, Deserialize, Debug, Clone)]
1342
pub struct OcamlNodeTestingConfig {
43+
/// List of initial peer connection targets
1444
pub initial_peers: Vec<P2pConnectionOutgoingInitOpts>,
45+
/// Genesis ledger configuration (file path or in-memory)
1546
pub daemon_json: DaemonJson,
47+
/// Optional block producer secret key
1648
pub block_producer: Option<AccountSecretKey>,
1749
}
1850

@@ -53,10 +85,40 @@ pub struct OcamlNodeConfig {
5385
pub block_producer: Option<AccountSecretKey>,
5486
}
5587

88+
/// OCaml node execution methods.
89+
///
90+
/// Supports multiple ways of running the OCaml Mina daemon,
91+
/// from local binaries to Docker containers with automatic
92+
/// detection and fallback behavior.
5693
#[derive(Serialize, Deserialize, Debug, Clone)]
5794
pub enum OcamlNodeExecutable {
95+
/// Use locally installed Mina binary
96+
///
97+
/// # Arguments
98+
/// * `String` - Path to the mina executable
99+
///
100+
/// # Example
101+
/// ```
102+
/// OcamlNodeExecutable::Installed("/usr/local/bin/mina".to_string())
103+
/// ```
58104
Installed(String),
105+
106+
/// Use specific Docker image
107+
///
108+
/// # Arguments
109+
/// * `String` - Docker image tag
110+
///
111+
/// # Example
112+
/// ```
113+
/// OcamlNodeExecutable::Docker("minaprotocol/mina-daemon:3.0.0".to_string())
114+
/// ```
59115
Docker(String),
116+
117+
/// Use default Docker image
118+
///
119+
/// Falls back to the predefined default image when no local
120+
/// binary is available. See [`DEFAULT_DOCKER_IMAGE`] for the
121+
/// current default.
60122
DockerDefault,
61123
}
62124

@@ -81,17 +143,40 @@ impl OcamlNodeConfig {
81143
{
82144
match &self.executable {
83145
OcamlNodeExecutable::Installed(program) => {
146+
info!(system_time(); "Using local Mina binary: {}", program);
84147
let mut cmd = Command::new(program);
85148
cmd.envs(envs);
86149
cmd
87150
}
88-
OcamlNodeExecutable::Docker(tag) => self.docker_run_cmd(tag, envs),
151+
OcamlNodeExecutable::Docker(tag) => {
152+
info!(system_time(); "Using custom Docker image: {}", tag);
153+
self.docker_run_cmd(tag, envs)
154+
}
89155
OcamlNodeExecutable::DockerDefault => {
156+
info!(
157+
system_time();
158+
"Using default Docker image: {}",
159+
OcamlNodeExecutable::DEFAULT_DOCKER_IMAGE
160+
);
90161
self.docker_run_cmd(OcamlNodeExecutable::DEFAULT_DOCKER_IMAGE, envs)
91162
}
92163
}
93164
}
94165

166+
/// Create a Docker run command with proper configuration.
167+
///
168+
/// Sets up a Docker container with appropriate networking, user mapping,
169+
/// volume mounts, and environment variables for running OCaml Mina daemon.
170+
///
171+
/// # Arguments
172+
/// * `tag` - Docker image tag to use
173+
/// * `envs` - Environment variables to pass to the container
174+
///
175+
/// # Docker Configuration
176+
/// - Uses host networking for P2P connectivity
177+
/// - Maps host user ID to avoid permission issues
178+
/// - Mounts node directory for persistent data
179+
/// - Sets working directory to `/tmp` for key generation
95180
fn docker_run_cmd<I, K, V>(&self, tag: &str, envs: I) -> Command
96181
where
97182
I: IntoIterator<Item = (K, V)>,
@@ -104,9 +189,18 @@ impl OcamlNodeConfig {
104189
let uid = std::env::var("$UID").unwrap_or_else(|_| "1000".to_owned());
105190
let container_name = OcamlNodeExecutable::docker_container_name(&self.dir);
106191

192+
info!(
193+
system_time();
194+
"Configuring Docker container: name={}, image={}, uid={}, mount={}",
195+
container_name,
196+
tag,
197+
uid,
198+
dir_path
199+
);
200+
107201
// set docker opts
108202
cmd.arg("run")
109-
.args(["--name".to_owned(), container_name])
203+
.args(["--name".to_owned(), container_name.clone()])
110204
.args(["--network", "host"])
111205
.args(["--user".to_owned(), format!("{uid}:{uid}")])
112206
.args(["-v".to_owned(), format!("{dir_path}:{dir_path}")])
@@ -116,14 +210,19 @@ impl OcamlNodeConfig {
116210
.args(["-w", "/tmp"]);
117211

118212
// set docker container envs
213+
let mut env_count = 0;
119214
for (key, value) in envs {
120215
let arg: OsString = [key.as_ref(), value.as_ref()].join(OsStr::new("="));
121216
cmd.args(["-e".as_ref(), arg.as_os_str()]);
217+
env_count += 1;
122218
}
123219

220+
info!(system_time(); "Added {} environment variables to Docker container", env_count);
221+
124222
// set docker image
125223
cmd.arg(tag);
126224

225+
info!(system_time(); "Docker command configured for container: {}", container_name);
127226
cmd
128227
}
129228
}
@@ -138,40 +237,119 @@ impl OcamlNodeExecutable {
138237
format!("mina_testing_ocaml_{}", &path[1..])
139238
}
140239

141-
/// Additional logic for killing the node.
240+
/// Clean up resources when terminating an OCaml node.
241+
///
242+
/// Handles cleanup logic specific to the execution method:
243+
/// - Local binaries: No additional cleanup needed
244+
/// - Docker containers: Stop and remove the container
245+
///
246+
/// # Arguments
247+
/// * `tmp_dir` - Temporary directory used by the node
142248
pub fn kill(&self, tmp_dir: &temp_dir::TempDir) {
143249
match self {
144-
OcamlNodeExecutable::Installed(_) => {}
250+
OcamlNodeExecutable::Installed(program) => {
251+
info!(system_time(); "No additional cleanup needed for local binary: {}", program);
252+
}
145253
OcamlNodeExecutable::Docker(_) | OcamlNodeExecutable::DockerDefault => {
254+
let name = Self::docker_container_name(tmp_dir);
255+
let image_info = match self {
256+
OcamlNodeExecutable::Docker(img) => img.clone(),
257+
OcamlNodeExecutable::DockerDefault => Self::DEFAULT_DOCKER_IMAGE.to_string(),
258+
_ => unreachable!(),
259+
};
260+
261+
info!(
262+
system_time();
263+
"Cleaning up Docker container: {} (image: {})",
264+
name,
265+
image_info
266+
);
267+
146268
// stop container.
269+
info!(system_time(); "Stopping Docker container: {}", name);
147270
let mut cmd = Command::new("docker");
148-
let name = Self::docker_container_name(tmp_dir);
149-
cmd.args(["stop".to_owned(), name]);
150-
let _ = cmd.status();
271+
cmd.args(["stop".to_owned(), name.clone()]);
272+
match cmd.status() {
273+
Ok(status) if status.success() => {
274+
info!(system_time(); "Successfully stopped Docker container: {}", name);
275+
}
276+
Ok(status) => {
277+
warn!(
278+
system_time();
279+
"Docker stop command failed for container {}: exit code {:?}",
280+
name,
281+
status.code()
282+
);
283+
}
284+
Err(e) => {
285+
warn!(system_time(); "Failed to stop Docker container {}: {}", name, e);
286+
}
287+
}
151288

152289
// remove container.
290+
info!(system_time(); "Removing Docker container: {}", name);
153291
let mut cmd = Command::new("docker");
154-
let name = Self::docker_container_name(tmp_dir);
155-
cmd.args(["rm".to_owned(), name]);
156-
let _ = cmd.status();
292+
cmd.args(["rm".to_owned(), name.clone()]);
293+
match cmd.status() {
294+
Ok(status) if status.success() => {
295+
info!(system_time(); "Successfully removed Docker container: {}", name);
296+
}
297+
Ok(status) => {
298+
warn!(
299+
system_time();
300+
"Docker rm command failed for container {}: exit code {:?}",
301+
name,
302+
status.code()
303+
);
304+
}
305+
Err(e) => {
306+
warn!(system_time(); "Failed to remove Docker container {}: {}", name, e);
307+
}
308+
}
157309
}
158310
}
159311
}
160312

313+
/// Automatically detect and return the best available OCaml executable.
314+
///
315+
/// This method implements the auto-detection strategy:
316+
/// 1. First, attempt to use locally installed `mina` binary
317+
/// 2. If not found, fall back to Docker with default image
318+
/// 3. Automatically pull the Docker image if needed
319+
///
320+
/// # Returns
321+
/// * `Ok(OcamlNodeExecutable)` - Best available execution method
322+
/// * `Err(anyhow::Error)` - No usable execution method found
323+
///
324+
/// # Docker Fallback
325+
/// When falling back to Docker, this method will automatically
326+
/// pull the default image if not already present locally.
161327
pub fn find_working() -> anyhow::Result<Self> {
162328
let program_name = Self::DEFAULT_MINA_EXECUTABLE;
329+
info!(system_time(); "Attempting to find local Mina binary: {}", program_name);
330+
163331
match Command::new(program_name)
164332
.stdout(Stdio::null())
165333
.stderr(Stdio::null())
166334
.spawn()
167335
{
168-
Ok(_) => return Ok(Self::Installed(program_name.to_owned())),
336+
Ok(_) => {
337+
info!(system_time(); "Found working local Mina binary: {}", program_name);
338+
return Ok(Self::Installed(program_name.to_owned()));
339+
}
169340
Err(err) => match err.kind() {
170-
std::io::ErrorKind::NotFound => {}
341+
std::io::ErrorKind::NotFound => {
342+
info!(system_time(); "Local Mina binary not found, falling back to Docker");
343+
}
171344
_ => anyhow::bail!("'{program_name}' returned an error: {err}"),
172345
},
173346
};
174347

348+
info!(
349+
system_time();
350+
"Pulling default Docker image: {}",
351+
Self::DEFAULT_DOCKER_IMAGE
352+
);
175353
let mut cmd = Command::new("docker");
176354

177355
let status = cmd
@@ -184,6 +362,7 @@ impl OcamlNodeExecutable {
184362
anyhow::bail!("error status pulling ocaml node: {status:?}");
185363
}
186364

365+
info!(system_time(); "Successfully pulled Docker image, using DockerDefault");
187366
Ok(Self::DockerDefault)
188367
}
189368
}

0 commit comments

Comments
 (0)